psy 3 anni fa
parent
commit
acb75fc76a
100 ha cambiato i file con 22324 aggiunte e 0 eliminazioni
  1. 106 0
      ecoin/INSTALL
  2. 2 0
      ecoin/build/build.h
  3. 11 0
      ecoin/doc/DNSSEEDS.txt
  4. 20 0
      ecoin/doc/MINING.txt
  5. 42 0
      ecoin/doc/REWARDS.txt
  6. 23 0
      ecoin/doc/TRANSLATIONS.txt
  7. 87 0
      ecoin/doc/build-linux.txt
  8. 85 0
      ecoin/doc/build-osx.txt
  9. 51 0
      ecoin/doc/build-win32.txt
  10. 21 0
      ecoin/doc/qr-codes.txt
  11. 427 0
      ecoin/ecoin-qt.pro
  12. 30 0
      ecoin/share/genbuild.sh
  13. BIN
      ecoin/share/pixmaps/addressbook16.bmp
  14. BIN
      ecoin/share/pixmaps/addressbook16mask.bmp
  15. BIN
      ecoin/share/pixmaps/addressbook20.bmp
  16. BIN
      ecoin/share/pixmaps/addressbook20mask.bmp
  17. BIN
      ecoin/share/pixmaps/check.ico
  18. BIN
      ecoin/share/pixmaps/ecoin.ico
  19. 608 0
      ecoin/share/pixmaps/ecoin32.xpm
  20. 2645 0
      ecoin/share/pixmaps/ecoin80.xpm
  21. BIN
      ecoin/share/pixmaps/favicon.ico
  22. BIN
      ecoin/share/pixmaps/nsis-header.bmp
  23. BIN
      ecoin/share/pixmaps/nsis-wizard.bmp
  24. BIN
      ecoin/share/pixmaps/send16.bmp
  25. BIN
      ecoin/share/pixmaps/send16mask.bmp
  26. BIN
      ecoin/share/pixmaps/send16masknoshadow.bmp
  27. BIN
      ecoin/share/pixmaps/send20.bmp
  28. BIN
      ecoin/share/pixmaps/send20mask.bmp
  29. 58 0
      ecoin/share/qt/extract_strings_qt.py
  30. BIN
      ecoin/share/qt/img/reload.xcf
  31. 42 0
      ecoin/share/qt/make_spinner.py
  32. 5 0
      ecoin/share/qt/make_windows_icon.sh
  33. 157 0
      ecoin/share/setup.nsi
  34. 18 0
      ecoin/share/ui.rc
  35. 525 0
      ecoin/src/addrman.cpp
  36. 449 0
      ecoin/src/addrman.h
  37. 228 0
      ecoin/src/alert.cpp
  38. 88 0
      ecoin/src/alert.h
  39. 211 0
      ecoin/src/allocators.h
  40. 434 0
      ecoin/src/base58.h
  41. 717 0
      ecoin/src/bignum.h
  42. 406 0
      ecoin/src/checkpoints.cpp
  43. 152 0
      ecoin/src/checkpoints.h
  44. 10 0
      ecoin/src/clientversion.h
  45. 57 0
      ecoin/src/coincontrol.h
  46. 60 0
      ecoin/src/compat.h
  47. 127 0
      ecoin/src/crypter.cpp
  48. 101 0
      ecoin/src/crypter.h
  49. 551 0
      ecoin/src/db.cpp
  50. 283 0
      ecoin/src/db.h
  51. 1225 0
      ecoin/src/ecoinrpc.cpp
  52. 179 0
      ecoin/src/ecoinrpc.h
  53. 837 0
      ecoin/src/init.cpp
  54. 14 0
      ecoin/src/init.h
  55. 364 0
      ecoin/src/irc.cpp
  56. 8 0
      ecoin/src/irc.h
  57. 24 0
      ecoin/src/json/LICENSE.txt
  58. 14 0
      ecoin/src/json/json_spirit.h
  59. 46 0
      ecoin/src/json/json_spirit_error_position.h
  60. 134 0
      ecoin/src/json/json_spirit_reader.cpp
  61. 58 0
      ecoin/src/json/json_spirit_reader.h
  62. 592 0
      ecoin/src/json/json_spirit_reader_template.h
  63. 62 0
      ecoin/src/json/json_spirit_stream_reader.h
  64. 57 0
      ecoin/src/json/json_spirit_utils.h
  65. 8 0
      ecoin/src/json/json_spirit_value.cpp
  66. 507 0
      ecoin/src/json/json_spirit_value.h
  67. 91 0
      ecoin/src/json/json_spirit_writer.cpp
  68. 43 0
      ecoin/src/json/json_spirit_writer.h
  69. 238 0
      ecoin/src/json/json_spirit_writer_template.h
  70. 308 0
      ecoin/src/kernel.cpp
  71. 37 0
      ecoin/src/kernel.h
  72. 385 0
      ecoin/src/key.cpp
  73. 119 0
      ecoin/src/key.h
  74. 217 0
      ecoin/src/keystore.cpp
  75. 160 0
      ecoin/src/keystore.h
  76. 13 0
      ecoin/src/leveldb/.gitignore
  77. 11 0
      ecoin/src/leveldb/AUTHORS
  78. 27 0
      ecoin/src/leveldb/LICENSE
  79. 206 0
      ecoin/src/leveldb/Makefile
  80. 17 0
      ecoin/src/leveldb/NEWS
  81. 51 0
      ecoin/src/leveldb/README
  82. 14 0
      ecoin/src/leveldb/TODO
  83. 39 0
      ecoin/src/leveldb/WINDOWS.md
  84. 214 0
      ecoin/src/leveldb/build_detect_platform
  85. 88 0
      ecoin/src/leveldb/db/builder.cc
  86. 34 0
      ecoin/src/leveldb/db/builder.h
  87. 595 0
      ecoin/src/leveldb/db/c.cc
  88. 390 0
      ecoin/src/leveldb/db/c_test.c
  89. 359 0
      ecoin/src/leveldb/db/corruption_test.cc
  90. 979 0
      ecoin/src/leveldb/db/db_bench.cc
  91. 1485 0
      ecoin/src/leveldb/db/db_impl.cc
  92. 203 0
      ecoin/src/leveldb/db/db_impl.h
  93. 299 0
      ecoin/src/leveldb/db/db_iter.cc
  94. 26 0
      ecoin/src/leveldb/db/db_iter.h
  95. 2092 0
      ecoin/src/leveldb/db/db_test.cc
  96. 140 0
      ecoin/src/leveldb/db/dbformat.cc
  97. 227 0
      ecoin/src/leveldb/db/dbformat.h
  98. 112 0
      ecoin/src/leveldb/db/dbformat_test.cc
  99. 139 0
      ecoin/src/leveldb/db/filename.cc
  100. 0 0
      ecoin/src/leveldb/db/filename.h

+ 106 - 0
ecoin/INSTALL

@@ -0,0 +1,106 @@
+
+ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+===================================================
+# BUILDS - ECOlogical P2P Crypto-Currency (ECOin) #
+===================================================
+
+=================
++ Build Makefile:
+=================
+
+cd ecoin/
+
+  + GNU/Linux:
+
+	/usr/lib/x86_64-linux-gnu/qt4/bin/qmake USE_UPNP=0 -o Makefile ecoin-qt.pro
+
+================
++ Build leveldb:
+================
+
+cd src/leveldb
+
+  + GNU/Linux:
+
+        TARGET_OS=Linux make libleveldb.a libmemenv.a
+
+  + MacOS X:
+
+        TARGET_OS=Darwin make libleveldb.a libmemenv.a
+
+  + Windows:
+
+        TARGET_OS=NATIVE_WINDOWS make libleveldb.a libmemenv.a
+
+===================================================
+
+===============
++ Build Wallet:
+===============
+
+See doc/build-*.txt for detailed instructions on building /ecoin-qt/ on different platforms.
+
+  + GNU/Linux:
+
+	cd ..
+	qmake USE_UPNP=- (or USE_UPNP=0)
+	make
+
+        ./ecoin-qt
+
+===============
++ Build Daemon:
+===============
+
+See doc/build-*.txt for detailed instructions on building /ecoind/ on different platforms.
+
+  + GNU/Linux:
+
+	cd src/
+	make -f makefile.linux USE_UPNP=- (or USE_UPNP=0)
+	strip ecoind
+
+        ./ecoind
+
+==========================
++ Configuration (Example):
+==========================
+
+  + GNU/Linux:
+
+     - File: /home/$USER/.ecoin/ecoin.conf # chmod 600 ecoin.conf
+
+        rpcuser=ecoinrpc
+        rpcpassword=YourPassWord2021 # use lowercase, upper case and numbers
+        rpcallowip=127.0.0.1
+        noirc=1
+        listen=1
+        server=1
+        daemon=1
+        testnet=0
+	addnode = 176.28.23.46 # 03c8.net
+
+     ./ecoind -conf=/home/$USER/.ecoin/ecoin.conf 
+
+====================
++ Testnet (Running):
+====================
+
+  + GNU/Linux:
+
+     cd src/
+     ./ecoind -testnet
+
+====================
++ Ports (Listening):
+====================
+
+  + Default P2P Port: 7408
+
+  + Default RPC Port (main): 7474
+
+  + Default RPC Port (testnet): 17474
+
+===================================================
+

+ 2 - 0
ecoin/build/build.h

@@ -0,0 +1,2 @@
+// No build information available
+#define BUILD_DATE "2014-05-10 14:37:22 -0500"

+ 11 - 0
ecoin/doc/DNSSEEDS.txt

@@ -0,0 +1,11 @@
+
+ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+=====================================================
+# DNSSEEDS - ECOlogical P2P Crypto-Currency (ECOin) #
+=====================================================
+
+  + ecoinseed.03c8.net
+
+=====================================================
+

+ 20 - 0
ecoin/doc/MINING.txt

@@ -0,0 +1,20 @@
+
+ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+===================================================
+# MINING - ECOlogical P2P Crypto-Currency (ECOin) #
+===================================================
+
+  + Solo-Mining:
+
+       1) cpuminer-multi:
+
+          git clone https://github.com/tpruvot/cpuminer-multi
+          cd cpuminer-multi/
+          sh build.sh
+
+             a) ./ecoin-qt -server
+             b) ./cpuminer -o http://0.0.0.0:7474 -O <USER>:<PASSWORD> --coinbase-addr=<YOUR_ECOIN_ADDRESS>
+
+===================================================
+

+ 42 - 0
ecoin/doc/REWARDS.txt

@@ -0,0 +1,42 @@
+
+ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+====================================================
+# REWARDS - ECOlogical P2P Crypto-Currency (ECOin) #
+====================================================
+
+ + Proof-Of-Work Mining (POW) Rewards (halving every 262,800 blocks):
+
+       -------------------------------------------------------------
+	Block	 		Reward
+       -------------------------------------------------------------
+	Block 1 - 3263: 	Random 500 5000 (1 minute blocks)
+	Block 3264 - 22000: 	Random 1000 10000 (2 minute blocks)
+	Block 22001 - 34750: 	Static reward 5000
+	Block 34751 - 39791: 	Static reward 2500
+	Block 39792 - 44832: 	Static reward 1250
+	Block 44833 - 49873: 	Static reward 625
+	Block 49874 - 54914: 	Static reward 312.50
+	Block 54915 - 59955: 	Static reward 156.25
+	Block 59956 - 64996: 	Static reward 78.125
+	Block 64997 - 70037: 	Static reward 39.0625
+	Block 70038 - 332838: 	static reward 20
+
+	Halving every 262800 blocks
+
+==================================
+
+ + Proof-Of-Stake Mining (POS):  
+
+	Variable interest based on coin age.
+
+==================================
+
+ + Proof-Of-Transaction Mining (POT): 
+
+	Mining by matching address segment to block hash (on tansactions greater than 500 coins), 50% of the block subsidy.
+
+	Increased security on Proof-of-Work blocks though verification of coinbase tx value, size, and destinations of coinbase transactions via Proof-Of-Transaction checking.
+
+====================================================
+

+ 23 - 0
ecoin/doc/TRANSLATIONS.txt

@@ -0,0 +1,23 @@
+
+ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+=========================================================
+# TRANSLATIONS - ECOlogical P2P Crypto-Currency (ECOin) #
+=========================================================
+
+       ---------------------------------------------------
+	NAME	 		LANGUAGE
+       --------------------------------------------------
+	 en 			 English
+	 ru 		         Russian
+	 ca			 Catalan
+	 de			 German
+         es			 Spanish
+	 es_EU			 Basque
+         fr			 French
+         it			 Italian
+         pt			 Portuguese
+         zh_CN			 Chinese
+
+=========================================================
+

+ 87 - 0
ecoin/doc/build-linux.txt

@@ -0,0 +1,87 @@
+
+ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+======================================
+# SOURCES for ECOin (Unix/GNU-Linux) #
+======================================
+
+All of the commands should be executed in a shell.
+
+------------------------------
+
+(0.) Clone the github tree to get the source code:
+
+  + Official:
+
+	git clone http://code.03c8.net:3000/epsylon/ecoin
+
+  + Mirror:
+
+	git clone https://github.com/epsylon/ecoin
+
+------------------------------
+
+==============================================
+# Daemon -ecoind- for ECOin (Unix/GNU-Linux) #
+==============================================
+
+(1.) Install dependencies:
+
+ Library       Purpose           Description
+ -------       -------           -----------
+ libssl1.0-dev SSL Support       Secure communications
+ libdb5.3      Berkeley DB       Blockchain & wallet storage
+ libboost      Boost             C++ Library
+ miniupnpc     UPnP Support      Optional firewall-jumping support
+ libqrencode   QRCode generation Optional QRCode generation
+
+    + From Debian/Ubuntu:
+  
+        sudo apt-get install build-essential libssl1.0-dev libssl1.0.2 libdb5.3-dev libdb5.3++-dev libboost-all-dev miniupnpc libminiupnpc-dev
+
+        + Optionally install qrencode (and set USE_QRCODE=1):
+
+       	    sudo apt-get install libqrencode-dev
+
+(2.) Now you should be able to build ecoind:
+
+	cd src/
+	make -f makefile.linux
+	strip ecoind
+
+     An executable named 'ecoind' will be built.
+
+------------------------------
+
+======================================
+# Qt4 GUI for ECOin (Unix/GNU-Linux) #
+======================================
+
+(1.) First, make sure that the required packages for Qt4 development of your distribution are installed, for Debian and Ubuntu these are:
+
+        sudo apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev miniupnpc libminiupnpc-dev \
+             libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev libssl1.0-dev libssl1.0.2 libdb5.3++-dev
+
+(2.) Then execute the following:
+
+        qmake
+        make
+
+     An executable named 'ecoin-qt' will be built.
+
+======================================
+
+For a list of command-line options:
+
+  ./ecoind --help
+
+To start the ECOin daemon:
+
+  ./ecoind -daemon
+
+For ECOin-QT Wallet
+
+  ./ecoin-qt
+
+======================================
+

+ 85 - 0
ecoin/doc/build-osx.txt

@@ -0,0 +1,85 @@
+
+ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+===============================
+# SOURCES for ECOin (MacOS X) #
+===============================
+
+All of the commands should be executed in Terminal.app
+
+------------------------------
+
+(0.) Clone the github tree to get the source code:
+
+  + Official:
+
+	git clone http://code.03c8.net:3000/epsylon/ecoin
+
+  + Mirror:
+
+	git clone https://github.com/epsylon/ecoin
+
+------------------------------
+
+=======================================
+# DAEMON -ecoind- for ECOin (MacOS X) #
+=======================================
+
+(1.) Download and install MacPorts from: http://www.macports.org/
+
+(2.) Install dependencies:
+
+ Library     Purpose           Description
+ -------     -------           -----------
+ libssl      SSL Support       Secure communications
+ libdb5.3    Berkeley DB       Blockchain & wallet storage
+ libboost    Boost             C++ Library
+ miniupnpc   UPnP Support      Optional firewall-jumping support
+ libqrencode QRCode generation Optional QRCode generation
+
+    + From MacPorts:
+	
+ 	sudo port install boost db53 openssl miniupnpc
+
+	+ Optionally install qrencode (and set USE_QRCODE=1):
+
+             sudo port install qrencode
+
+(3.) Now you should be able to build ecoind:
+
+	cd ecoin/src
+	make -f makefile.osx
+
+     An executable named 'ecoind' will be built.
+
+===============================
+# Qt4 GUI for ECOin (MacOS X) #
+===============================
+
+(1.) Download and install MacPorts from: http://www.macports.org/
+
+(2.) Execute the following commands in a terminal to get the dependencies:
+
+        sudo port selfupdate
+        sudo port install qt4-mac qt4-creator-mac boost db53 miniupnpc
+
+(3.) Open the .pro file in Qt Creator (with Desktop kit) and build as normal (cmd-B)
+
+	An executable named 'ecoin-qt' will be built.
+
+===============================
+
+For a list of command-line options:
+
+  ./ecoind --help
+
+To start the ECOin daemon:
+
+  ./ecoind -daemon
+
+For ECOin-QT Wallet
+
+  ./ecoin-qt
+
+===============================
+

+ 51 - 0
ecoin/doc/build-win32.txt

@@ -0,0 +1,51 @@
+
+ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+=============================
+# SOURCES for ECOin (Win32) #
+=============================
+
+------------------------------
+
+(0.) Clone the github tree to get the source code:
+
+  + Official:
+
+	git clone http://code.03c8.net:3000/epsylon/ecoin
+
+  + Mirror:
+
+	git clone https://github.com/epsylon/ecoin
+
+------------------------------
+
+=============================
+# Qt4 GUI for ECOin (Win32) #
+=============================
+
+(1.) Download the 'QT Windows SDK'_ and install it. You don't need the Symbian stuff, just the desktop Qt.
+
+(2.) Download and extract the 'dependencies archive'_  [#]_, or compile openssl, boost and dbcxx yourself.
+
+(3.) Copy the contents of the folder "deps" to "X:\\QtSDK\\mingw", replace X:\\ with the location where you installed the Qt SDK. 
+
+(4.) Open the .pro file in QT creator (with Desktop kit) and build as normal (ctrl-B)
+
+	An executable named 'ecoin-qt.exe' will be built.
+
+=============================
+
+For a list of command-line options:
+
+  ecoind.exe --help
+
+To start the ECOin daemon:
+
+  ecoind.exe -daemon
+
+For ECOin-QT Wallet
+
+  ecoin-qt.exe
+
+=============================
+

+ 21 - 0
ecoin/doc/qr-codes.txt

@@ -0,0 +1,21 @@
+
+ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+=====================================================
+# QR-Codes - ECOlogical P2P Crypto-Currency (ECOin) #
+=====================================================
+
+  + libqrencode may be used to generate QRCode images for payment requests.
+
+       It can be downloaded from: https://fukuchi.org/works/qrencode/index.html.en (or installed via your package manager). 
+
+       Pass the USE_QRCODE flag to qmake to control this:
+
+	+--------------+--------------------------------------------------------------------------+
+	| USE_QRCODE=0 | (the default) No QRCode support - libarcode not required                 |
+	+--------------+--------------------------------------------------------------------------+
+	| USE_QRCODE=1 | QRCode support enabled                                                   |
+	+--------------+--------------------------------------------------------------------------+
+
+=====================================================
+

+ 427 - 0
ecoin/ecoin-qt.pro

@@ -0,0 +1,427 @@
+
+# ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+TEMPLATE = app
+TARGET = ecoin-qt
+VERSION = 0.0.2
+INCLUDEPATH += src src/json src/qt
+DEFINES += QT_GUI BOOST_THREAD_USE_LIB BOOST_SPIRIT_THREADSAFE
+CONFIG += no_include_pwd
+CONFIG += thread
+OBJECTS_DIR = build
+MOC_DIR = build
+UI_DIR = build
+
+# use: qmake "RELEASE=1"
+contains(RELEASE, 1) {
+    # Mac: compile for maximum compatibility (10.5, 32-bit)
+    macx:QMAKE_CXXFLAGS += -mmacosx-version-min=10.5 -arch x86_64 -isysroot /Developer/SDKs/MacOSX10.5.sdk
+
+    !windows:!macx {
+        # Linux: static link
+        LIBS += -Wl,-Bstatic
+    }
+}
+
+!win32 {
+# for extra security against potential buffer overflows: enable GCCs Stack Smashing Protection
+QMAKE_CXXFLAGS *= -fstack-protector-all --param ssp-buffer-size=1
+QMAKE_LFLAGS *= -fstack-protector-all --param ssp-buffer-size=1
+# We need to exclude this for Windows cross compile with MinGW 4.2.x, as it will result in a non-working executable!
+# This can be enabled for Windows, when we switch to MinGW >= 4.4.x.
+}
+# for extra security on Windows: enable ASLR and DEP via GCC linker flags
+win32:QMAKE_LFLAGS *= -Wl,--dynamicbase -Wl,--nxcompat
+win32:QMAKE_LFLAGS += -static-libgcc -static-libstdc++
+
+# use: qmake "USE_QRCODE=1"
+# libqrencode (http://fukuchi.org/works/qrencode/index.en.html) must be installed for support
+contains(USE_QRCODE, 1) {
+    message(Building with QRCode support)
+    DEFINES += USE_QRCODE
+    LIBS += -lqrencode
+}
+
+# use: qmake "USE_UPNP=1" ( enabled by default; default)
+#  or: qmake "USE_UPNP=0" (disabled by default)
+#  or: qmake "USE_UPNP=-" (not supported)
+# miniupnpc (http://miniupnp.free.fr/files/) must be installed for support
+contains(USE_UPNP, -) {
+    message(Building without UPNP support)
+} else {
+    message(Building with UPNP support)
+    count(USE_UPNP, 0) {
+        USE_UPNP=1
+    }
+    DEFINES += USE_UPNP=$$USE_UPNP STATICLIB
+    INCLUDEPATH += $$MINIUPNPC_INCLUDE_PATH
+    LIBS += $$join(MINIUPNPC_LIB_PATH,,-L,) -lminiupnpc
+    win32:LIBS += -liphlpapi
+}
+
+# use: qmake "USE_DBUS=1"
+contains(USE_DBUS, 1) {
+    message(Building with DBUS (Freedesktop notifications) support)
+    DEFINES += USE_DBUS
+    QT += dbus
+}
+
+# use: qmake "USE_IPV6=1" ( enabled by default; default)
+#  or: qmake "USE_IPV6=0" (disabled by default)
+#  or: qmake "USE_IPV6=-" (not supported)
+contains(USE_IPV6, -) {
+    message(Building without IPv6 support)
+} else {
+    count(USE_IPV6, 0) {
+        USE_IPV6=1
+    }
+    DEFINES += USE_IPV6=$$USE_IPV6
+}
+
+contains(ECOIN_NEED_QT_PLUGINS, 1) {
+    DEFINES += ECOIN_NEED_QT_PLUGINS
+    QTPLUGIN += qcncodecs qjpcodecs qtwcodecs qkrcodecs qtaccessiblewidgets
+}
+
+INCLUDEPATH += src/leveldb/include src/leveldb/helpers
+LIBS += $$PWD/src/leveldb/libleveldb.a $$PWD/src/leveldb/libmemenv.a
+SOURCES += src/txdb-leveldb.cpp
+!win32 {
+    # we use QMAKE_CXXFLAGS_RELEASE even without RELEASE=1 because we use RELEASE to indicate linking preferences not -O preferences
+    genleveldb.commands = cd $$PWD/src/leveldb && CC=$$QMAKE_CC CXX=$$QMAKE_CXX $(MAKE) OPT=\"$$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_RELEASE\" libleveldb.a libmemenv.a
+} else {
+    # make an educated guess about what the ranlib command is called
+    isEmpty(QMAKE_RANLIB) {
+        QMAKE_RANLIB = $$replace(QMAKE_STRIP, strip, ranlib)
+    }
+    LIBS += -lshlwapi
+    genleveldb.commands = cd $$PWD/src/leveldb && CC=$$QMAKE_CC CXX=$$QMAKE_CXX TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) OPT=\"$$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_RELEASE\" libleveldb.a libmemenv.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libleveldb.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libmemenv.a
+}
+genleveldb.target = $$PWD/src/leveldb/libleveldb.a
+genleveldb.depends = FORCE
+PRE_TARGETDEPS += $$PWD/src/leveldb/libleveldb.a
+QMAKE_EXTRA_TARGETS += genleveldb
+# Gross ugly hack that depends on qmake internals, unfortunately there is no other way to do it.
+QMAKE_CLEAN += $$PWD/src/leveldb/libleveldb.a; cd $$PWD/src/leveldb ; $(MAKE) clean
+
+# regenerate src/build.h
+!windows|contains(USE_BUILD_INFO, 1) {
+    genbuild.depends = FORCE
+    genbuild.commands = cd $$PWD; /bin/sh share/genbuild.sh $$OUT_PWD/build/build.h
+    genbuild.target = $$OUT_PWD/build/build.h
+    PRE_TARGETDEPS += $$OUT_PWD/build/build.h
+    QMAKE_EXTRA_TARGETS += genbuild
+    DEFINES += HAVE_BUILD_INFO
+}
+
+contains(USE_O3, 1) {
+    message(Building O3 optimization flag)
+    QMAKE_CXXFLAGS_RELEASE -= -O2
+    QMAKE_CFLAGS_RELEASE -= -O2
+    QMAKE_CXXFLAGS += -O3
+    QMAKE_CFLAGS += -O3
+}
+
+*-g++-32 {
+    message("32 platform, adding -msse2 flag")
+
+    QMAKE_CXXFLAGS += -msse2
+    QMAKE_CFLAGS += -msse2
+}
+
+QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wextra -Wno-ignored-qualifiers -Wformat -Wformat-security -Wno-unused-parameter -Wstack-protector
+
+# Input
+DEPENDPATH += src src/json src/qt
+HEADERS += src/qt/ecoingui.h \
+    src/qt/transactiontablemodel.h \
+    src/qt/addresstablemodel.h \
+    src/qt/optionsdialog.h \
+    src/qt/coincontroldialog.h \
+    src/qt/coincontroltreewidget.h \
+    src/qt/sendcoinsdialog.h \
+    src/qt/addressbookpage.h \
+    src/qt/signverifymessagedialog.h \
+    src/qt/aboutdialog.h \
+    src/qt/editaddressdialog.h \
+    src/qt/ecoinaddressvalidator.h \
+    src/alert.h \
+    src/addrman.h \
+    src/base58.h \
+    src/bignum.h \
+    src/checkpoints.h \
+    src/compat.h \
+    src/coincontrol.h \
+    src/sync.h \
+    src/util.h \
+    src/uint256.h \
+    src/kernel.h \
+    src/scrypt.h \
+    src/pbkdf2.h \
+    src/zerocoin/Accumulator.h \
+    src/zerocoin/AccumulatorProofOfKnowledge.h \
+    src/zerocoin/Coin.h \
+    src/zerocoin/CoinSpend.h \
+    src/zerocoin/Commitment.h \
+    src/zerocoin/ParamGeneration.h \
+    src/zerocoin/Params.h \
+    src/zerocoin/SerialNumberSignatureOfKnowledge.h \
+    src/zerocoin/SpendMetaData.h \
+    src/zerocoin/ZeroTest.h \
+    src/zerocoin/Zerocoin.h \
+    src/serialize.h \
+    src/strlcpy.h \
+    src/main.h \
+    src/miner.h \
+    src/net.h \
+    src/key.h \
+    src/db.h \
+    src/txdb.h \
+    src/walletdb.h \
+    src/script.h \
+    src/init.h \
+    src/irc.h \
+    src/mruset.h \
+    src/json/json_spirit_writer_template.h \
+    src/json/json_spirit_writer.h \
+    src/json/json_spirit_value.h \
+    src/json/json_spirit_utils.h \
+    src/json/json_spirit_stream_reader.h \
+    src/json/json_spirit_reader_template.h \
+    src/json/json_spirit_reader.h \
+    src/json/json_spirit_error_position.h \
+    src/json/json_spirit.h \
+    src/qt/clientmodel.h \
+    src/qt/guiutil.h \
+    src/qt/transactionrecord.h \
+    src/qt/guiconstants.h \
+    src/qt/optionsmodel.h \
+    src/qt/monitoreddatamapper.h \
+    src/qt/transactiondesc.h \
+    src/qt/transactiondescdialog.h \
+    src/qt/ecoinamountfield.h \
+    src/wallet.h \
+    src/keystore.h \
+    src/qt/transactionfilterproxy.h \
+    src/qt/transactionview.h \
+    src/qt/walletmodel.h \
+    src/ecoinrpc.h \
+    src/qt/overviewpage.h \
+    src/qt/csvmodelwriter.h \
+    src/crypter.h \
+    src/qt/sendcoinsentry.h \
+    src/qt/qvalidatedlineedit.h \
+    src/qt/ecoinunits.h \
+    src/qt/qvaluecombobox.h \
+    src/qt/askpassphrasedialog.h \
+    src/protocol.h \
+    src/qt/notificator.h \
+    src/qt/qtipcserver.h \
+    src/allocators.h \
+    src/ui_interface.h \
+    src/qt/rpcconsole.h \
+    src/version.h \
+    src/netbase.h \
+    src/clientversion.h
+
+SOURCES += src/qt/ecoin.cpp src/qt/ecoingui.cpp \
+    src/qt/transactiontablemodel.cpp \
+    src/qt/addresstablemodel.cpp \
+    src/qt/optionsdialog.cpp \
+    src/qt/sendcoinsdialog.cpp \
+    src/qt/coincontroldialog.cpp \
+    src/qt/coincontroltreewidget.cpp \
+    src/qt/addressbookpage.cpp \
+    src/qt/signverifymessagedialog.cpp \
+    src/qt/aboutdialog.cpp \
+    src/qt/editaddressdialog.cpp \
+    src/qt/ecoinaddressvalidator.cpp \
+    src/alert.cpp \
+    src/version.cpp \
+    src/sync.cpp \
+    src/util.cpp \
+    src/netbase.cpp \
+    src/key.cpp \
+    src/script.cpp \
+    src/main.cpp \
+    src/miner.cpp \
+    src/init.cpp \
+    src/net.cpp \
+    src/irc.cpp \
+    src/checkpoints.cpp \
+    src/addrman.cpp \
+    src/db.cpp \
+    src/walletdb.cpp \
+    src/qt/clientmodel.cpp \
+    src/qt/guiutil.cpp \
+    src/qt/transactionrecord.cpp \
+    src/qt/optionsmodel.cpp \
+    src/qt/monitoreddatamapper.cpp \
+    src/qt/transactiondesc.cpp \
+    src/qt/transactiondescdialog.cpp \
+    src/qt/ecoinstrings.cpp \
+    src/qt/ecoinamountfield.cpp \
+    src/wallet.cpp \
+    src/keystore.cpp \
+    src/qt/transactionfilterproxy.cpp \
+    src/qt/transactionview.cpp \
+    src/qt/walletmodel.cpp \
+    src/ecoinrpc.cpp \
+    src/rpcdump.cpp \
+    src/rpcnet.cpp \
+    src/rpcmining.cpp \
+    src/rpcwallet.cpp \
+    src/rpcblockchain.cpp \
+    src/rpcrawtransaction.cpp \
+    src/qt/overviewpage.cpp \
+    src/qt/csvmodelwriter.cpp \
+    src/crypter.cpp \
+    src/qt/sendcoinsentry.cpp \
+    src/qt/qvalidatedlineedit.cpp \
+    src/qt/ecoinunits.cpp \
+    src/qt/qvaluecombobox.cpp \
+    src/qt/askpassphrasedialog.cpp \
+    src/protocol.cpp \
+    src/qt/notificator.cpp \
+    src/qt/qtipcserver.cpp \
+    src/qt/rpcconsole.cpp \
+    src/noui.cpp \
+    src/kernel.cpp \
+    src/scrypt-arm.S \
+    src/scrypt-x86.S \
+    src/scrypt-x86_64.S \
+    src/scrypt.cpp \
+    src/pbkdf2.cpp \
+    src/zerocoin/Accumulator.cpp \
+    src/zerocoin/AccumulatorProofOfKnowledge.cpp \
+    src/zerocoin/Coin.cpp \
+    src/zerocoin/CoinSpend.cpp \
+    src/zerocoin/Commitment.cpp \
+    src/zerocoin/ParamGeneration.cpp \
+    src/zerocoin/Params.cpp \
+    src/zerocoin/SerialNumberSignatureOfKnowledge.cpp \
+    src/zerocoin/SpendMetaData.cpp \
+    src/zerocoin/ZeroTest.cpp
+
+RESOURCES += \
+    src/qt/ecoin.qrc
+
+FORMS += \
+    src/qt/forms/coincontroldialog.ui \
+    src/qt/forms/sendcoinsdialog.ui \
+    src/qt/forms/addressbookpage.ui \
+    src/qt/forms/signverifymessagedialog.ui \
+    src/qt/forms/aboutdialog.ui \
+    src/qt/forms/editaddressdialog.ui \
+    src/qt/forms/transactiondescdialog.ui \
+    src/qt/forms/overviewpage.ui \
+    src/qt/forms/sendcoinsentry.ui \
+    src/qt/forms/askpassphrasedialog.ui \
+    src/qt/forms/rpcconsole.ui \
+    src/qt/forms/optionsdialog.ui
+
+contains(USE_QRCODE, 1) {
+HEADERS += src/qt/qrcodedialog.h
+SOURCES += src/qt/qrcodedialog.cpp
+FORMS += src/qt/forms/qrcodedialog.ui
+}
+
+CODECFORTR = UTF-8
+
+# for lrelease/lupdate
+# also add new translations to src/qt/ecoin.qrc under translations/
+TRANSLATIONS = $$files(src/qt/locale/ecoin_*.ts)
+
+isEmpty(QMAKE_LRELEASE) {
+    win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\\lrelease.exe
+    else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease
+}
+isEmpty(QM_DIR):QM_DIR = $$PWD/src/qt/locale
+# automatically build translations, so they can be included in resource file
+TSQM.name = lrelease ${QMAKE_FILE_IN}
+TSQM.input = TRANSLATIONS
+TSQM.output = $$QM_DIR/${QMAKE_FILE_BASE}.qm
+TSQM.commands = $$QMAKE_LRELEASE ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_OUT}
+TSQM.CONFIG = no_link
+QMAKE_EXTRA_COMPILERS += TSQM
+
+# "Other files" to show in Qt Creator
+OTHER_FILES += \
+    doc/*.rst doc/*.txt doc/README README.md res/ecoin-qt.rc
+
+# platform specific defaults, if not overridden on command line
+isEmpty(BOOST_LIB_SUFFIX) {
+    macx:BOOST_LIB_SUFFIX = -mt
+    windows:BOOST_LIB_SUFFIX = -mgw44-mt-1_53
+}
+
+isEmpty(BOOST_THREAD_LIB_SUFFIX) {
+    BOOST_THREAD_LIB_SUFFIX = $$BOOST_LIB_SUFFIX
+}
+
+isEmpty(BDB_LIB_PATH) {
+    macx:BDB_LIB_PATH = /opt/local/lib/db48
+}
+
+isEmpty(BDB_LIB_SUFFIX) {
+    macx:BDB_LIB_SUFFIX = -4.8
+}
+
+isEmpty(BDB_INCLUDE_PATH) {
+    macx:BDB_INCLUDE_PATH = /opt/local/include/db48
+}
+
+isEmpty(BOOST_LIB_PATH) {
+    macx:BOOST_LIB_PATH = /opt/local/lib
+}
+
+isEmpty(BOOST_INCLUDE_PATH) {
+    macx:BOOST_INCLUDE_PATH = /opt/local/include
+}
+
+windows:DEFINES += WIN32
+windows:RC_FILE = src/qt/res/ecoin-qt.rc
+
+windows:!contains(MINGW_THREAD_BUGFIX, 0) {
+    # At least qmake's win32-g++-cross profile is missing the -lmingwthrd
+    # thread-safety flag. GCC has -mthreads to enable this, but it doesn't
+    # work with static linking. -lmingwthrd must come BEFORE -lmingw, so
+    # it is prepended to QMAKE_LIBS_QT_ENTRY.
+    # It can be turned off with MINGW_THREAD_BUGFIX=0, just in case it causes
+    # any problems on some untested qmake profile now or in the future.
+    DEFINES += _MT BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
+    QMAKE_LIBS_QT_ENTRY = -lmingwthrd $$QMAKE_LIBS_QT_ENTRY
+}
+
+!windows:!macx {
+    DEFINES += LINUX
+    LIBS += -lrt
+}
+
+macx:HEADERS += src/qt/macdockiconhandler.h
+macx:OBJECTIVE_SOURCES += src/qt/macdockiconhandler.mm
+macx:LIBS += -framework Foundation -framework ApplicationServices -framework AppKit
+macx:DEFINES += MAC_OSX MSG_NOSIGNAL=0
+macx:ICON = src/qt/res/icons/ecoin.icns
+macx:TARGET = "Ecoin-Qt"
+macx:QMAKE_CFLAGS_THREAD += -pthread
+macx:QMAKE_LFLAGS_THREAD += -pthread
+macx:QMAKE_CXXFLAGS_THREAD += -pthread
+
+# Set libraries and includes at end, to use platform-defined defaults if not overridden
+INCLUDEPATH += $$BOOST_INCLUDE_PATH $$BDB_INCLUDE_PATH $$OPENSSL_INCLUDE_PATH $$QRENCODE_INCLUDE_PATH
+LIBS += $$join(BOOST_LIB_PATH,,-L,) $$join(BDB_LIB_PATH,,-L,) $$join(OPENSSL_LIB_PATH,,-L,) $$join(QRENCODE_LIB_PATH,,-L,)
+LIBS += -lssl -lcrypto -ldb_cxx$$BDB_LIB_SUFFIX
+# -lgdi32 has to happen after -lcrypto (see  #681)
+windows:LIBS += -lws2_32 -lshlwapi -lmswsock -lole32 -loleaut32 -luuid -lgdi32
+LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread$$BOOST_THREAD_LIB_SUFFIX
+windows:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX
+
+contains(RELEASE, 1) {
+    !windows:!macx {
+        # Linux: turn dynamic linking back on for c/c++ runtime libraries
+        LIBS += -Wl,-Bdynamic
+    }
+}
+
+system($$QMAKE_LRELEASE -silent $$_PRO_FILE_)

+ 30 - 0
ecoin/share/genbuild.sh

@@ -0,0 +1,30 @@
+#!/bin/sh
+# ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+if [ $# -gt 0 ]; then
+    FILE="$1"
+    shift
+    if [ -f "$FILE" ]; then
+        INFO="$(head -n 1 "$FILE")"
+    fi
+else
+    echo "Usage: $0 <filename>"
+    exit 1
+fi
+
+if [ -e "$(which git)" ]; then
+    git diff >/dev/null 2>/dev/null 
+    DESC="$(git describe --dirty 2>/dev/null)"
+    TIME="$(git log -n 1 --format="%ci")"
+fi
+
+if [ -n "$DESC" ]; then
+    NEWINFO="#define BUILD_DESC \"$DESC\""
+else
+    NEWINFO="// No build information available"
+fi
+
+if [ "$INFO" != "$NEWINFO" ]; then
+    echo "$NEWINFO" >"$FILE"
+    echo "#define BUILD_DATE \"$TIME\"" >>"$FILE"
+fi

BIN
ecoin/share/pixmaps/addressbook16.bmp


BIN
ecoin/share/pixmaps/addressbook16mask.bmp


BIN
ecoin/share/pixmaps/addressbook20.bmp


BIN
ecoin/share/pixmaps/addressbook20mask.bmp


BIN
ecoin/share/pixmaps/check.ico


BIN
ecoin/share/pixmaps/ecoin.ico


+ 608 - 0
ecoin/share/pixmaps/ecoin32.xpm

@@ -0,0 +1,608 @@
+/* XPM */
+static char * ecoin32_xpm[] = {
+"32 32 573 2",
+"  	c None",
+". 	c #171717",
+"+ 	c #171716",
+"@ 	c #161617",
+"# 	c #161616",
+"$ 	c #161715",
+"% 	c #151517",
+"& 	c #141616",
+"* 	c #141414",
+"= 	c #151515",
+"- 	c #161615",
+"; 	c #171615",
+"> 	c #171617",
+", 	c #0D0F13",
+"' 	c #101015",
+") 	c #332F15",
+"! 	c #5B4D1A",
+"~ 	c #665A1A",
+"{ 	c #685D1C",
+"] 	c #5E5119",
+"^ 	c #3F3717",
+"/ 	c #161515",
+"( 	c #0D0E15",
+"_ 	c #161717",
+": 	c #171616",
+"< 	c #0E0F14",
+"[ 	c #413A18",
+"} 	c #A99320",
+"| 	c #EAC725",
+"1 	c #FCD925",
+"2 	c #FFD623",
+"3 	c #FFD423",
+"4 	c #FFD422",
+"5 	c #FFD522",
+"6 	c #F4CB24",
+"7 	c #BB9F1E",
+"8 	c #5B4F18",
+"9 	c #111113",
+"0 	c #141516",
+"a 	c #101116",
+"b 	c #373118",
+"c 	c #CBAE1F",
+"d 	c #FFD824",
+"e 	c #FCD324",
+"f 	c #FBCC20",
+"g 	c #F5BD1C",
+"h 	c #F4B619",
+"i 	c #F4B316",
+"j 	c #F4B417",
+"k 	c #F5B617",
+"l 	c #F4BA18",
+"m 	c #FBC61F",
+"n 	c #FCCE1F",
+"o 	c #FFD220",
+"p 	c #E6BF1E",
+"q 	c #584C18",
+"r 	c #171718",
+"s 	c #0E0F12",
+"t 	c #7E6E1C",
+"u 	c #FED624",
+"v 	c #FDD123",
+"w 	c #F8C21E",
+"x 	c #F0AC14",
+"y 	c #F2AD13",
+"z 	c #F8BE1C",
+"A 	c #FBCE20",
+"B 	c #FCCE20",
+"C 	c #FECD1F",
+"D 	c #FCCC21",
+"E 	c #F9C31C",
+"F 	c #F2B116",
+"G 	c #F2AD17",
+"H 	c #F7BB1A",
+"I 	c #FCCB1E",
+"J 	c #FFD11E",
+"K 	c #AD911B",
+"L 	c #0F1014",
+"M 	c #A68E1D",
+"N 	c #FFD321",
+"O 	c #FBCD22",
+"P 	c #F1A711",
+"Q 	c #F5B819",
+"R 	c #FCC91C",
+"S 	c #FEC418",
+"T 	c #FCC015",
+"U 	c #FDBF14",
+"V 	c #65521C",
+"W 	c #63511E",
+"X 	c #FDBF15",
+"Y 	c #FCC115",
+"Z 	c #FDC418",
+"` 	c #FDC91A",
+" .	c #F9BE1B",
+"..	c #F1AD17",
+"+.	c #FBC41D",
+"@.	c #FEC91C",
+"#.	c #D4AE1B",
+"$.	c #191914",
+"%.	c #161816",
+"&.	c #0D0F14",
+"*.	c #9C861E",
+"=.	c #FFD123",
+"-.	c #F9C71E",
+";.	c #EEA411",
+">.	c #FBC91D",
+",.	c #FCC116",
+"'.	c #FDBE14",
+").	c #DDA716",
+"!.	c #8A6C1A",
+"~.	c #352A0D",
+"{.	c #2E250D",
+"].	c #FCBE14",
+"^.	c #91721D",
+"/.	c #DFA813",
+"(.	c #FDC016",
+"_.	c #FCC519",
+":.	c #F5B31B",
+"<.	c #F8BE19",
+"[.	c #FEC71D",
+"}.	c #D2AA19",
+"|.	c #161516",
+"1.	c #161716",
+"2.	c #151415",
+"3.	c #60551A",
+"4.	c #FFD221",
+"5.	c #FAC91E",
+"6.	c #FABE15",
+"7.	c #F9BB13",
+"8.	c #7E641C",
+"9.	c #010101",
+"0.	c #806419",
+"a.	c #33290B",
+"b.	c #2C230C",
+"c.	c #866919",
+"d.	c #000000",
+"e.	c #715919",
+"f.	c #FABC15",
+"g.	c #F5B71C",
+"h.	c #FEC618",
+"i.	c #A08316",
+"j.	c #0F1115",
+"k.	c #1E1D13",
+"l.	c #F3CA20",
+"m.	c #EEA410",
+"n.	c #FCC91D",
+"o.	c #886B1D",
+"p.	c #664E0D",
+"q.	c #FBBD16",
+"r.	c #6B5417",
+"s.	c #020200",
+"t.	c #010100",
+"u.	c #695215",
+"v.	c #FCBD14",
+"w.	c #53410E",
+"x.	c #7E631B",
+"y.	c #FBC217",
+"z.	c #F5B521",
+"A.	c #FAC117",
+"B.	c #FFCA18",
+"C.	c #443814",
+"D.	c #8E791A",
+"E.	c #FECC1D",
+"F.	c #F2B015",
+"G.	c #F0BC1C",
+"H.	c #CC9E1C",
+"I.	c #4B3B10",
+"J.	c #130F05",
+"K.	c #F6BA16",
+"L.	c #FDBE15",
+"M.	c #735A17",
+"N.	c #715916",
+"O.	c #F5B815",
+"P.	c #070705",
+"Q.	c #3E300C",
+"R.	c #F8BB14",
+"S.	c #C6981A",
+"T.	c #F8BE18",
+"U.	c #F5B621",
+"V.	c #FDC214",
+"W.	c #CFA415",
+"X.	c #0D0E14",
+"Y.	c #161613",
+"Z.	c #F1C31E",
+"`.	c #FCCA1C",
+" +	c #F1A912",
+".+	c #AB891D",
+"++	c #020202",
+"@+	c #58430C",
+"#+	c #8A6B18",
+"$+	c #030303",
+"%+	c #D09F1B",
+"&+	c #FCBD13",
+"*+	c #2B220C",
+"=+	c #29210C",
+"-+	c #DBA515",
+";+	c #92731F",
+">+	c #5B460E",
+",+	c #040402",
+"'+	c #AE881E",
+")+	c #F9BD1E",
+"!+	c #FBBC19",
+"~+	c #FFC515",
+"{+	c #3D3313",
+"]+	c #171715",
+"^+	c #4B4016",
+"/+	c #FFCE1B",
+"(+	c #F4B213",
+"_+	c #F8BC18",
+":+	c #EAB419",
+"<+	c #735912",
+"[+	c #0C0A04",
+"}+	c #060503",
+"|+	c #8E6F1B",
+"1+	c #FBBC14",
+"2+	c #372B0D",
+"3+	c #90711D",
+"4+	c #070604",
+"5+	c #0D0B04",
+"6+	c #715812",
+"7+	c #E4AB14",
+"8+	c #FCC214",
+"9+	c #F8BA21",
+"0+	c #FDBF12",
+"a+	c #947716",
+"b+	c #111215",
+"c+	c #131315",
+"d+	c #957C18",
+"e+	c #FDC81C",
+"f+	c #F2A911",
+"g+	c #FCC71B",
+"h+	c #F5BB19",
+"i+	c #C09112",
+"j+	c #67531C",
+"k+	c #030200",
+"l+	c #171205",
+"m+	c #A67F17",
+"n+	c #F4B815",
+"o+	c #362B0D",
+"p+	c #F3B715",
+"q+	c #A67F14",
+"r+	c #181307",
+"s+	c #6A5621",
+"t+	c #BD8F12",
+"u+	c #F3B716",
+"v+	c #FDBE13",
+"w+	c #F9BC28",
+"x+	c #FCBB0E",
+"y+	c #CA9B11",
+"z+	c #0D0F16",
+"A+	c #BD9818",
+"B+	c #FEC619",
+"C+	c #F2A70E",
+"D+	c #806315",
+"E+	c #050503",
+"F+	c #141003",
+"G+	c #3E310E",
+"H+	c #644D0E",
+"I+	c #3D300C",
+"J+	c #030302",
+"K+	c #473911",
+"L+	c #CB9A18",
+"M+	c #33280B",
+"N+	c #2B220B",
+"O+	c #CB9A16",
+"P+	c #483912",
+"Q+	c #3A2E0C",
+"R+	c #3E310F",
+"S+	c #161105",
+"T+	c #644E12",
+"U+	c #FCBE11",
+"V+	c #F8BD26",
+"W+	c #FDBA12",
+"X+	c #F3B70E",
+"Y+	c #111214",
+"Z+	c #171618",
+"`+	c #0C0D16",
+" @	c #CDA518",
+".@	c #FCC416",
+"+@	c #F2A50F",
+"@@	c #D8A620",
+"#@	c #B48A1A",
+"$@	c #E5AD17",
+"%@	c #9B7817",
+"&@	c #161103",
+"*@	c #0B0902",
+"=@	c #060502",
+"-@	c #181304",
+";@	c #9E7A19",
+">@	c #E7B01A",
+",@	c #BA8F1D",
+"'@	c #DEAA1D",
+")@	c #FABE22",
+"!@	c #FAB915",
+"~@	c #FFBF0E",
+"{@	c #1D1B13",
+"]@	c #0B0D15",
+"^@	c #D1A717",
+"/@	c #FCC215",
+"(@	c #F3A40E",
+"_@	c #FDC317",
+":@	c #D7A419",
+"<@	c #1F1B0E",
+"[@	c #181408",
+"}@	c #D6A218",
+"|@	c #FDBD13",
+"1@	c #FCBD12",
+"2@	c #FBBC13",
+"3@	c #FBBE21",
+"4@	c #FBB915",
+"5@	c #FFC00C",
+"6@	c #201D14",
+"7@	c #171816",
+"8@	c #0C0E16",
+"9@	c #C69C15",
+"0@	c #FCC113",
+"a@	c #F1A60D",
+"b@	c #FEC216",
+"c@	c #D5A31B",
+"d@	c #C89815",
+"e@	c #E4AC16",
+"f@	c #EDB214",
+"g@	c #F5B813",
+"h@	c #EEB314",
+"i@	c #B18717",
+"j@	c #2B220A",
+"k@	c #0F0C02",
+"l@	c #060501",
+"m@	c #040301",
+"n@	c #100D03",
+"o@	c #282009",
+"p@	c #AF8617",
+"q@	c #EEB315",
+"r@	c #E5AD16",
+"s@	c #CF9D15",
+"t@	c #D8A214",
+"u@	c #FCBC0F",
+"v@	c #FABF25",
+"w@	c #FCB612",
+"x@	c #FFBA0C",
+"y@	c #171613",
+"z@	c #181717",
+"A@	c #101217",
+"B@	c #A88713",
+"C@	c #FDC014",
+"D@	c #F3A80F",
+"E@	c #FDC315",
+"F@	c #856817",
+"G@	c #050401",
+"H@	c #2F2711",
+"I@	c #4B3A0E",
+"J@	c #88670E",
+"K@	c #4D3C0E",
+"L@	c #382C0B",
+"M@	c #B68B18",
+"N@	c #34290C",
+"O@	c #BA8E18",
+"P@	c #392D0B",
+"Q@	c #87660F",
+"R@	c #4B3B0F",
+"S@	c #312913",
+"T@	c #070601",
+"U@	c #6E5515",
+"V@	c #FCBA0D",
+"W@	c #FBC12F",
+"X@	c #FBB209",
+"Y@	c #DCA40C",
+"Z@	c #0E0F15",
+"`@	c #151617",
+" #	c #725D14",
+".#	c #FFC214",
+"+#	c #FBBB13",
+"@#	c #EAB117",
+"##	c #9F7B19",
+"$#	c #4F3F14",
+"%#	c #0D0B03",
+"&#	c #8E6D14",
+"*#	c #FCBE17",
+"=#	c #8D6C13",
+"-#	c #544418",
+";#	c #9E7918",
+">#	c #E8AC13",
+",#	c #FCB80A",
+"'#	c #F9BF2B",
+")#	c #FBB105",
+"!#	c #AE800D",
+"~#	c #252115",
+"{#	c #FDBF11",
+"]#	c #FBB70F",
+"^#	c #F6AF11",
+"/#	c #957113",
+"(#	c #271F09",
+"_#	c #0E0B02",
+":#	c #8A6C19",
+"<#	c #91721F",
+"[#	c #0F0C03",
+"}#	c #2C230A",
+"|#	c #987313",
+"1#	c #F0B010",
+"2#	c #FABA16",
+"3#	c #FBBB22",
+"4#	c #FFB704",
+"5#	c #675010",
+"6#	c #C89A10",
+"7#	c #FDBB0E",
+"8#	c #F2A210",
+"9#	c #A47F18",
+"0#	c #070602",
+"a#	c #4A390C",
+"b#	c #7B5F17",
+"c#	c #D19F1A",
+"d#	c #F7BA17",
+"e#	c #2C230B",
+"f#	c #271F0B",
+"g#	c #DCA615",
+"h#	c #7B611C",
+"i#	c #47360B",
+"j#	c #0A0802",
+"k#	c #AA7E17",
+"l#	c #FCC430",
+"m#	c #FCAE04",
+"n#	c #EDAB08",
+"o#	c #443714",
+"p#	c #FFBF0C",
+"q#	c #FBB50E",
+"r#	c #ECA614",
+"s#	c #B48815",
+"t#	c #FDBC11",
+"u#	c #554211",
+"v#	c #151105",
+"w#	c #F7BA16",
+"x#	c #8B6C18",
+"y#	c #020201",
+"z#	c #836617",
+"A#	c #F2B616",
+"B#	c #090804",
+"C#	c #4F3E10",
+"D#	c #AD7F10",
+"E#	c #FCBB1D",
+"F#	c #FAB81E",
+"G#	c #FFB106",
+"H#	c #85630C",
+"I#	c #121315",
+"J#	c #0F1116",
+"K#	c #AC8410",
+"L#	c #FEB70B",
+"M#	c #F8AD0F",
+"N#	c #F7B312",
+"O#	c #FDBA0F",
+"P#	c #705714",
+"Q#	c #624B0F",
+"R#	c #EEB416",
+"S#	c #816517",
+"T#	c #7D6219",
+"U#	c #ECB215",
+"V#	c #FCBD15",
+"W#	c #4E3D0E",
+"X#	c #FCB60C",
+"Y#	c #FBB70E",
+"Z#	c #FCC132",
+"`#	c #FBAC03",
+" $	c #E5A108",
+".$	c #141614",
+"+$	c #191915",
+"@$	c #E5AA0B",
+"#$	c #FCB409",
+"$$	c #F7AB11",
+"%$	c #F9B214",
+"&$	c #FCB80E",
+"*$	c #F7B611",
+"=$	c #FDBB11",
+"-$	c #9D7819",
+";$	c #6E5614",
+">$	c #32270B",
+",$	c #28200B",
+"'$	c #725915",
+")$	c #94731A",
+"!$	c #F5B713",
+"~$	c #FBB80E",
+"{$	c #FBB710",
+"]$	c #FAC133",
+"^$	c #FBAB03",
+"/$	c #FCAF06",
+"($	c #3D3111",
+"_$	c #151616",
+":$	c #2D2513",
+"<$	c #F1AF0A",
+"[$	c #FDB206",
+"}$	c #F9AE10",
+"|$	c #F7AF19",
+"1$	c #FCB70C",
+"2$	c #FDB70C",
+"3$	c #C6900D",
+"4$	c #765811",
+"5$	c #785D16",
+"6$	c #C79615",
+"7$	c #FCB90F",
+"8$	c #FCB40A",
+"9$	c #FBBD21",
+"0$	c #FBBD2D",
+"a$	c #FBAA01",
+"b$	c #FFB004",
+"c$	c #5D470E",
+"d$	c #121317",
+"e$	c #2A2413",
+"f$	c #E4A50A",
+"g$	c #FCB106",
+"h$	c #FCB008",
+"i$	c #F7AE1B",
+"j$	c #F9B41B",
+"k$	c #FDB50B",
+"l$	c #FCB507",
+"m$	c #FCB60B",
+"n$	c #634E16",
+"o$	c #604C17",
+"p$	c #FDB90E",
+"q$	c #FCB50A",
+"r$	c #FCB509",
+"s$	c #FBBC1B",
+"t$	c #FABF32",
+"u$	c #FBB114",
+"v$	c #FCA901",
+"w$	c #FAAC04",
+"x$	c #573E10",
+"y$	c #111316",
+"z$	c #A77A0D",
+"A$	c #FFB405",
+"B$	c #FDAE04",
+"C$	c #FCAD07",
+"D$	c #F9B01A",
+"E$	c #F8B525",
+"F$	c #FAB61F",
+"G$	c #FBB81D",
+"H$	c #FBB91D",
+"I$	c #FBB921",
+"J$	c #FABD2B",
+"K$	c #FCBA2A",
+"L$	c #FCAE0F",
+"M$	c #FCA701",
+"N$	c #FFAF01",
+"O$	c #CC8F07",
+"P$	c #2A2213",
+"Q$	c #121617",
+"R$	c #3B2F10",
+"S$	c #B8820B",
+"T$	c #FCAF04",
+"U$	c #FFAF03",
+"V$	c #FDAB01",
+"W$	c #FCAA03",
+"X$	c #FBAA03",
+"Y$	c #FDA904",
+"Z$	c #FAA803",
+"`$	c #FDA900",
+" %	c #FFAC01",
+".%	c #FFAD01",
+"+%	c #D18F07",
+"@%	c #5A430E",
+"#%	c #0E1016",
+"$%	c #0D1016",
+"%%	c #231E13",
+"&%	c #5F4511",
+"*%	c #9A7008",
+"=%	c #CE8E07",
+"-%	c #DB9A03",
+";%	c #DD9A04",
+">%	c #D49106",
+",%	c #AA7808",
+"'%	c #6C4D0D",
+")%	c #312812",
+"!%	c #0A0F17",
+"~%	c #0E1215",
+"{%	c #0F1117",
+"]%	c #0F1316",
+"^%	c #151516",
+". . . . . . . . . . . . . + . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . @ # $ % & * = - ; # . + . . . . . . . . . ",
+". . . . . . . . @ > . , ' ) ! ~ { ] ^ / ( _ _ : + . . . . . . . ",
+". . . . . . + # ; < [ } | 1 2 3 3 4 5 6 7 8 9 0 # . . . . . . . ",
+". . . . # # + a b c d e f g h i j k l m n o p q < r : . . . . . ",
+". . . . # > s t u v w x y z A B C D E F G H I J K = > . . . . . ",
+". . . > $ L M N O P Q R S T U V W X Y Z `  ...+.@.#.$.%.# . . . ",
+". . + # &.*.=.-.;.>.,.'.).!.'.~.{.].^./.'.(._.:.<.[.}.|.: 1.. . ",
+". + % 2.3.4.5.;.I 6.7.'.8.9.0.a.b.c.d.e.'.7.f.Z g. .h.i.j.# : . ",
+". # - k.l.f m.n.(.o.p.'.q.r.d.s.t.d.u.'.v.w.x.v.y.z.A.B.C._ @ . ",
+". > j.D.E.F.G.H.'.I.J.K.'.L.M.9.9.N.'.'.O.P.Q.R.S.T.U.V.W.X.# 1.",
+". ; Y.Z.`. +.+++@+#+$+%+'.&+'.*+=+&+'.'.-+d.;+>+,+'+)+!+~+{+]+# ",
+"# - ^+/+(+_+:+<+[+}+d.|+1+'.'.2+{.'.'.1+3+d.4+5+6+7+8+9+0+a+b+@ ",
+"- c+d+e+f+g+h+i+j+d.k+l+m+n+&+o+{.'.p+q+r+k+9.s+t+u+v+w+x+y+( @ ",
+"# z+A+B+C+B+D+E+F+G+H+I+J+K+L+M+N+O+P+9.Q+H+R+S+E+T+U+V+W+X+Y+Z+",
+"# `+ @.@+@Z @@#@$@].'.'.%@&@$+*@=@++-@;@'.'.'.>@,@'@].)@!@~@{@. ",
+"+ ]@^@/@(@_@'.'.'.].'.'.'.:@<@d.d.[@}@'.'.'.'.'.|@1@2@3@4@5@6@7@",
+"# 8@9@0@a@b@c@d@e@f@g@h@i@j@k@l@m@n@o@p@q@g@f@r@s@t@u@v@w@x@y@z@",
+". A@B@C@D@E@F@G@H@I@J@K@9.L@M@N@b.O@P@J+K@Q@R@S@T@U@V@W@X@Y@Z@. ",
+"`@0  #.#D@+#@###$#d.9.%#&#L.'.o+{.'.*#=#5+9.d.-#;#>#,#'#)#!#Z@# ",
+"_ . ~#{#]#^#h@/#(#_#d.:#'.'.'.o+{.'.'.'.<#d.[#}#|#1#2#3#4#5## # ",
+"_ `@&.6#7#8#9#0#a#b#9.c#'.'.d#e#f#d#'.'.g#d.h#i#j#k#l#m#n#/ # + ",
+". # # o#p#q#r#s#t#u#v#w#'.u+x#9.y#z#A#'.K.B#C#t#D#E#F#G#H#I## . ",
+". # = J#K#L#M#N#O#P#Q#&+R#S#9.l@m@++T#U#V#W#Q#X#Y#Z#`# $.$: # . ",
+". # 1.: +$@$#$$$%$&$*$=$-$++;$>$,$'$$+)$|@!$~${$]$^$/$($_$+ . . ",
+". . @ # # :$<$[$}$|$1$2$3$4$t#o+{.&+5$6$7$8$9$0$a$b$c$d$@ @ . . ",
+". . . # @ = e$f$g$h$i$j$k$l$m$n$o$p$q$r$s$t$u$v$w$x$y$> . . . . ",
+". . . . : _ = = z$A$B$C$D$E$F$G$H$I$J$K$L$M$N$O$P$Q$1.. . . . . ",
+". . . . . 1.1.; J#R$S$T$U$V$W$X$Y$Z$`$ %.%+%@%#%# > . . . . . . ",
+". . . . . . # # # _ $%%%&%*%=%-%;%>%,%'%)%!%# > 1.. . . . . . . ",
+". . . . . . . . . . . + # I#$%~%{%$%]%^%+ + . . . . . . . . . . ",
+". . . . . . . . . . . . # _$# 1._$# @ - # . . . . . . . . . . . "};

File diff suppressed because it is too large
+ 2645 - 0
ecoin/share/pixmaps/ecoin80.xpm


BIN
ecoin/share/pixmaps/favicon.ico


BIN
ecoin/share/pixmaps/nsis-header.bmp


BIN
ecoin/share/pixmaps/nsis-wizard.bmp


BIN
ecoin/share/pixmaps/send16.bmp


BIN
ecoin/share/pixmaps/send16mask.bmp


BIN
ecoin/share/pixmaps/send16masknoshadow.bmp


BIN
ecoin/share/pixmaps/send20.bmp


BIN
ecoin/share/pixmaps/send20mask.bmp


+ 58 - 0
ecoin/share/qt/extract_strings_qt.py

@@ -0,0 +1,58 @@
+#!/usr/bin/python
+# ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+from subprocess import Popen, PIPE
+import glob
+
+OUT_CPP="src/qt/ecoinstrings.cpp"
+EMPTY=['""']
+
+def parse_po(text):
+    messages = []
+    msgid = []
+    msgstr = []
+    in_msgid = False
+    in_msgstr = False
+    for line in text.split('\n'):
+        line = line.rstrip('\r')
+        if line.startswith('msgid '):
+            if in_msgstr:
+                messages.append((msgid, msgstr))
+                in_msgstr = False
+            in_msgid = True
+            msgid = [line[6:]]
+        elif line.startswith('msgstr '):
+            in_msgid = False
+            in_msgstr = True
+            msgstr = [line[7:]]
+        elif line.startswith('"'):
+            if in_msgid:
+                msgid.append(line)
+            if in_msgstr:
+                msgstr.append(line)
+    if in_msgstr:
+        messages.append((msgid, msgstr))
+    return messages
+
+files = glob.glob('src/*.cpp') + glob.glob('src/*.h') 
+
+child = Popen(['xgettext','--output=-','-n','--keyword=_'] + files, stdout=PIPE)
+(out, err) = child.communicate()
+
+messages = parse_po(out) 
+
+f = open(OUT_CPP, 'w')
+f.write("""#include <QtGlobal>
+// Automatically generated by extract_strings.py
+#ifdef __GNUC__
+#define UNUSED __attribute__((unused))
+#else
+#define UNUSED
+#endif
+""")
+f.write('static const char UNUSED *ecoin_strings[] = {')
+for (msgid, msgstr) in messages:
+    if msgid != EMPTY:
+        f.write('QT_TRANSLATE_NOOP("ecoin-core", %s),\n' % ('\n'.join(msgid)))
+f.write('};')
+f.close()

BIN
ecoin/share/qt/img/reload.xcf


+ 42 - 0
ecoin/share/qt/make_spinner.py

@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+# Requires imagemagick 6.7+
+from __future__ import division
+from os import path
+from PIL import Image
+from subprocess import Popen
+
+SRC='img/reload_scaled.png'
+DST='../../src/qt/res/movies/update_spinner.mng'
+TMPDIR='/tmp'
+TMPNAME='tmp-%03i.png'
+NUMFRAMES=35
+FRAMERATE=10.0
+CONVERT='convert'
+CLOCKWISE=True
+DSIZE=(16,16)
+
+im_src = Image.open(SRC)
+
+if CLOCKWISE:
+    im_src = im_src.transpose(Image.FLIP_LEFT_RIGHT)
+
+def frame_to_filename(frame):
+    return path.join(TMPDIR, TMPNAME % frame)
+
+frame_files = []
+for frame in xrange(NUMFRAMES):
+    rotation = (frame + 0.5) / NUMFRAMES * 360.0
+    if CLOCKWISE:
+        rotation = -rotation
+    im_new = im_src.rotate(rotation, Image.BICUBIC)
+    im_new.thumbnail(DSIZE, Image.ANTIALIAS)
+    outfile = frame_to_filename(frame)
+    im_new.save(outfile, 'png')
+    frame_files.append(outfile)
+
+p = Popen([CONVERT, "-delay", str(FRAMERATE), "-dispose", "2"] + frame_files + [DST])
+p.communicate()
+
+
+

+ 5 - 0
ecoin/share/qt/make_windows_icon.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+# ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+# Create multiresolution windows icon
+ICON_DST=../../src/qt/res/icons/ecoin.ico
+convert ../../src/qt/res/icons/ecoin-16.png ../../src/qt/res/icons/ecoin-32.png ../../src/qt/res/icons/ecoin-48.png ${ICON_DST}

+ 157 - 0
ecoin/share/setup.nsi

@@ -0,0 +1,157 @@
+
+# ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+Name Ecoin
+
+RequestExecutionLevel highest
+SetCompressor /SOLID lzma
+
+# General Symbol Definitions
+!define REGKEY "SOFTWARE\$(^Name)"
+!define VERSION 0.0.2
+!define COMPANY "Ecoin project"
+!define URL https://03c8.net
+
+# MUI Symbol Definitions
+!define MUI_ICON "../share/pixmaps/ecoin.ico"
+!define MUI_WELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp"
+!define MUI_HEADERIMAGE
+!define MUI_HEADERIMAGE_RIGHT
+!define MUI_HEADERIMAGE_BITMAP "../share/pixmaps/nsis-header.bmp"
+!define MUI_FINISHPAGE_NOAUTOCLOSE
+!define MUI_STARTMENUPAGE_REGISTRY_ROOT HKLM
+!define MUI_STARTMENUPAGE_REGISTRY_KEY ${REGKEY}
+!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME StartMenuGroup
+!define MUI_STARTMENUPAGE_DEFAULTFOLDER Ecoin
+#!define MUI_FINISHPAGE_RUN $INSTDIR\ecoin-qt.exe
+!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
+!define MUI_UNWELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp"
+!define MUI_UNFINISHPAGE_NOAUTOCLOSE
+
+# Included files
+!include Sections.nsh
+!include MUI2.nsh
+
+# Variables
+Var StartMenuGroup
+
+# Installer pages
+!insertmacro MUI_PAGE_WELCOME
+!insertmacro MUI_PAGE_DIRECTORY
+!insertmacro MUI_PAGE_STARTMENU Application $StartMenuGroup
+!insertmacro MUI_PAGE_INSTFILES
+!insertmacro MUI_PAGE_FINISH
+!insertmacro MUI_UNPAGE_CONFIRM
+!insertmacro MUI_UNPAGE_INSTFILES
+
+# Installer languages
+!insertmacro MUI_LANGUAGE English
+
+# Installer attributes
+OutFile ecoin-0.0.2-win32-setup.exe
+InstallDir $PROGRAMFILES\Ecoin
+CRCCheck on
+XPStyle on
+BrandingText " "
+ShowInstDetails show
+VIProductVersion 0.0.2.1
+VIAddVersionKey ProductName Ecoin
+VIAddVersionKey ProductVersion "${VERSION}"
+VIAddVersionKey CompanyName "${COMPANY}"
+VIAddVersionKey CompanyWebsite "${URL}"
+VIAddVersionKey FileVersion "${VERSION}"
+VIAddVersionKey FileDescription ""
+VIAddVersionKey LegalCopyright ""
+InstallDirRegKey HKCU "${REGKEY}" Path
+ShowUninstDetails show
+
+# Installer sections
+Section -Main SEC0000
+    SetOutPath $INSTDIR
+    SetOverwrite on
+    #File ../release/ecoin-qt.exe
+    File /oname=license.txt ../COPYING
+    File /oname=readme.txt ../doc/README_windows.txt
+    SetOutPath $INSTDIR\daemon
+    File ../src/ecoind.exe
+    SetOutPath $INSTDIR\src
+    File /r /x *.exe /x *.o ../src\*.*
+    SetOutPath $INSTDIR
+    WriteRegStr HKCU "${REGKEY}\Components" Main 1
+
+    # Remove old wxwidgets-based-bitcoin executable and locales:
+    #Delete /REBOOTOK $INSTDIR\ecoin.exe
+    #RMDir /r /REBOOTOK $INSTDIR\locale
+SectionEnd
+
+Section -post SEC0001
+    WriteRegStr HKCU "${REGKEY}" Path $INSTDIR
+    SetOutPath $INSTDIR
+    WriteUninstaller $INSTDIR\uninstall.exe
+    !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+    CreateDirectory $SMPROGRAMS\$StartMenuGroup
+    CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall Ecoin.lnk" $INSTDIR\uninstall.exe
+    !insertmacro MUI_STARTMENU_WRITE_END
+    WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)"
+    WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "${VERSION}"
+    WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}"
+    WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}"
+    WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\uninstall.exe
+    WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" UninstallString $INSTDIR\uninstall.exe
+    WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1
+    WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1
+SectionEnd
+
+# Macro for selecting uninstaller sections
+!macro SELECT_UNSECTION SECTION_NAME UNSECTION_ID
+    Push $R0
+    ReadRegStr $R0 HKCU "${REGKEY}\Components" "${SECTION_NAME}"
+    StrCmp $R0 1 0 next${UNSECTION_ID}
+    !insertmacro SelectSection "${UNSECTION_ID}"
+    GoTo done${UNSECTION_ID}
+next${UNSECTION_ID}:
+    !insertmacro UnselectSection "${UNSECTION_ID}"
+done${UNSECTION_ID}:
+    Pop $R0
+!macroend
+
+# Uninstaller sections
+Section /o -un.Main UNSEC0000
+    Delete /REBOOTOK $INSTDIR\license.txt
+    Delete /REBOOTOK $INSTDIR\readme.txt
+    RMDir /r /REBOOTOK $INSTDIR\daemon
+    RMDir /r /REBOOTOK $INSTDIR\src
+    DeleteRegValue HKCU "${REGKEY}\Components" Main
+SectionEnd
+
+Section -un.post UNSEC0001
+    DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)"
+    Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Uninstall Ecoin.lnk"
+    Delete /REBOOTOK $INSTDIR\uninstall.exe
+    Delete /REBOOTOK $INSTDIR\debug.log
+    Delete /REBOOTOK $INSTDIR\db.log
+    DeleteRegValue HKCU "${REGKEY}" StartMenuGroup
+    DeleteRegValue HKCU "${REGKEY}" Path
+    DeleteRegKey /IfEmpty HKCU "${REGKEY}\Components"
+    DeleteRegKey /IfEmpty HKCU "${REGKEY}"
+    DeleteRegKey HKCR "ecoin"
+    RmDir /REBOOTOK $SMPROGRAMS\$StartMenuGroup
+    RmDir /REBOOTOK $INSTDIR
+    Push $R0
+    StrCpy $R0 $StartMenuGroup 1
+    StrCmp $R0 ">" no_smgroup
+no_smgroup:
+    Pop $R0
+SectionEnd
+
+# Installer functions
+Function .onInit
+    InitPluginsDir
+FunctionEnd
+
+# Uninstaller functions
+Function un.onInit
+    ReadRegStr $INSTDIR HKCU "${REGKEY}" Path
+    !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuGroup
+    !insertmacro SELECT_UNSECTION Main ${UNSEC0000}
+FunctionEnd

+ 18 - 0
ecoin/share/ui.rc

@@ -0,0 +1,18 @@
+
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+ecoin                 ICON    "pixmaps/ecoin.ico"
+
+#include "wx/msw/wx.rc"
+
+check                 ICON    "pixmaps/check.ico"
+send16                BITMAP  "pixmaps/send16.bmp"
+send16mask            BITMAP  "pixmaps/send16mask.bmp"
+send16masknoshadow    BITMAP  "pixmaps/send16masknoshadow.bmp"
+send20                BITMAP  "pixmaps/send20.bmp"
+send20mask            BITMAP  "pixmaps/send20mask.bmp"
+addressbook16         BITMAP  "pixmaps/addressbook16.bmp"
+addressbook16mask     BITMAP  "pixmaps/addressbook16mask.bmp"
+addressbook20         BITMAP  "pixmaps/addressbook20.bmp"
+addressbook20mask     BITMAP  "pixmaps/addressbook20mask.bmp"
+favicon               ICON    "pixmaps/favicon.ico"

+ 525 - 0
ecoin/src/addrman.cpp

@@ -0,0 +1,525 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+#include "addrman.h"
+
+using namespace std;
+
+int CAddrInfo::GetTriedBucket(const std::vector<unsigned char> &nKey) const
+{
+    CDataStream ss1(SER_GETHASH, 0);
+    std::vector<unsigned char> vchKey = GetKey();
+    ss1 << nKey << vchKey;
+    uint64 hash1 = Hash(ss1.begin(), ss1.end()).Get64();
+
+    CDataStream ss2(SER_GETHASH, 0);
+    std::vector<unsigned char> vchGroupKey = GetGroup();
+    ss2 << nKey << vchGroupKey << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP);
+    uint64 hash2 = Hash(ss2.begin(), ss2.end()).Get64();
+    return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
+}
+
+int CAddrInfo::GetNewBucket(const std::vector<unsigned char> &nKey, const CNetAddr& src) const
+{
+    CDataStream ss1(SER_GETHASH, 0);
+    std::vector<unsigned char> vchGroupKey = GetGroup();
+    std::vector<unsigned char> vchSourceGroupKey = src.GetGroup();
+    ss1 << nKey << vchGroupKey << vchSourceGroupKey;
+    uint64 hash1 = Hash(ss1.begin(), ss1.end()).Get64();
+
+    CDataStream ss2(SER_GETHASH, 0);
+    ss2 << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP);
+    uint64 hash2 = Hash(ss2.begin(), ss2.end()).Get64();
+    return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
+}
+
+bool CAddrInfo::IsTerrible(int64 nNow) const
+{
+    if (nLastTry && nLastTry >= nNow-60) // never remove things tried the last minute
+        return false;
+
+    if (nTime > nNow + 10*60) // came in a flying DeLorean
+        return true;
+
+    if (nTime==0 || nNow-nTime > ADDRMAN_HORIZON_DAYS*86400) // not seen in over a month
+        return true;
+
+    if (nLastSuccess==0 && nAttempts>=ADDRMAN_RETRIES) // tried three times and never a success
+        return true;
+
+    if (nNow-nLastSuccess > ADDRMAN_MIN_FAIL_DAYS*86400 && nAttempts>=ADDRMAN_MAX_FAILURES) // 10 successive failures in the last week
+        return true;
+
+    return false;
+}
+
+double CAddrInfo::GetChance(int64 nNow) const
+{
+    double fChance = 1.0;
+
+    int64 nSinceLastSeen = nNow - nTime;
+    int64 nSinceLastTry = nNow - nLastTry;
+
+    if (nSinceLastSeen < 0) nSinceLastSeen = 0;
+    if (nSinceLastTry < 0) nSinceLastTry = 0;
+
+    fChance *= 600.0 / (600.0 + nSinceLastSeen);
+
+    // deprioritize very recent attempts away
+    if (nSinceLastTry < 60*10)
+        fChance *= 0.01;
+
+    // deprioritize 50% after each failed attempt
+    for (int n=0; n<nAttempts; n++)
+        fChance /= 1.5;
+
+    return fChance;
+}
+
+CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int *pnId)
+{
+    std::map<CNetAddr, int>::iterator it = mapAddr.find(addr);
+    if (it == mapAddr.end())
+        return NULL;
+    if (pnId)
+        *pnId = (*it).second;
+    std::map<int, CAddrInfo>::iterator it2 = mapInfo.find((*it).second);
+    if (it2 != mapInfo.end())
+        return &(*it2).second;
+    return NULL;
+}
+
+CAddrInfo* CAddrMan::Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId)
+{
+    int nId = nIdCount++;
+    mapInfo[nId] = CAddrInfo(addr, addrSource);
+    mapAddr[addr] = nId;
+    mapInfo[nId].nRandomPos = vRandom.size();
+    vRandom.push_back(nId);
+    if (pnId)
+        *pnId = nId;
+    return &mapInfo[nId];
+}
+
+void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
+{
+    if (nRndPos1 == nRndPos2)
+        return;
+
+    assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
+
+    int nId1 = vRandom[nRndPos1];
+    int nId2 = vRandom[nRndPos2];
+
+    assert(mapInfo.count(nId1) == 1);
+    assert(mapInfo.count(nId2) == 1);
+
+    mapInfo[nId1].nRandomPos = nRndPos2;
+    mapInfo[nId2].nRandomPos = nRndPos1;
+
+    vRandom[nRndPos1] = nId2;
+    vRandom[nRndPos2] = nId1;
+}
+
+int CAddrMan::SelectTried(int nKBucket)
+{
+    std::vector<int> &vTried = vvTried[nKBucket];
+
+    // random shuffle the first few elements (using the entire list)
+    // find the least recently tried among them
+    int64 nOldest = -1;
+    int nOldestPos = -1;
+    for (unsigned int i = 0; i < ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT && i < vTried.size(); i++)
+    {
+        int nPos = GetRandInt(vTried.size() - i) + i;
+        int nTemp = vTried[nPos];
+        vTried[nPos] = vTried[i];
+        vTried[i] = nTemp;
+        assert(nOldest == -1 || mapInfo.count(nTemp) == 1);
+        if (nOldest == -1 || mapInfo[nTemp].nLastSuccess < mapInfo[nOldest].nLastSuccess) {
+           nOldest = nTemp;
+           nOldestPos = nPos;
+        }
+    }
+
+    return nOldestPos;
+}
+
+int CAddrMan::ShrinkNew(int nUBucket)
+{
+    assert(nUBucket >= 0 && (unsigned int)nUBucket < vvNew.size());
+    std::set<int> &vNew = vvNew[nUBucket];
+
+    // first look for deletable items
+    for (std::set<int>::iterator it = vNew.begin(); it != vNew.end(); it++)
+    {
+        assert(mapInfo.count(*it));
+        CAddrInfo &info = mapInfo[*it];
+        if (info.IsTerrible())
+        {
+            if (--info.nRefCount == 0)
+            {
+                SwapRandom(info.nRandomPos, vRandom.size()-1);
+                vRandom.pop_back();
+                mapAddr.erase(info);
+                mapInfo.erase(*it);
+                nNew--;
+            }
+            vNew.erase(it);
+            return 0;
+        }
+    }
+
+    // otherwise, select four randomly, and pick the oldest of those to replace
+    int n[4] = {GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size())};
+    int nI = 0;
+    int nOldest = -1;
+    for (std::set<int>::iterator it = vNew.begin(); it != vNew.end(); it++)
+    {
+        if (nI == n[0] || nI == n[1] || nI == n[2] || nI == n[3])
+        {
+            assert(nOldest == -1 || mapInfo.count(*it) == 1);
+            if (nOldest == -1 || mapInfo[*it].nTime < mapInfo[nOldest].nTime)
+                nOldest = *it;
+        }
+        nI++;
+    }
+    assert(mapInfo.count(nOldest) == 1);
+    CAddrInfo &info = mapInfo[nOldest];
+    if (--info.nRefCount == 0)
+    {
+        SwapRandom(info.nRandomPos, vRandom.size()-1);
+        vRandom.pop_back();
+        mapAddr.erase(info);
+        mapInfo.erase(nOldest);
+        nNew--;
+    }
+    vNew.erase(nOldest);
+
+    return 1;
+}
+
+void CAddrMan::MakeTried(CAddrInfo& info, int nId, int nOrigin)
+{
+    assert(vvNew[nOrigin].count(nId) == 1);
+
+    // remove the entry from all new buckets
+    for (std::vector<std::set<int> >::iterator it = vvNew.begin(); it != vvNew.end(); it++)
+    {
+        if ((*it).erase(nId))
+            info.nRefCount--;
+    }
+    nNew--;
+
+    assert(info.nRefCount == 0);
+
+    // what tried bucket to move the entry to
+    int nKBucket = info.GetTriedBucket(nKey);
+    std::vector<int> &vTried = vvTried[nKBucket];
+
+    // first check whether there is place to just add it
+    if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE)
+    {
+        vTried.push_back(nId);
+        nTried++;
+        info.fInTried = true;
+        return;
+    }
+
+    // otherwise, find an item to evict
+    int nPos = SelectTried(nKBucket);
+
+    // find which new bucket it belongs to
+    assert(mapInfo.count(vTried[nPos]) == 1);
+    int nUBucket = mapInfo[vTried[nPos]].GetNewBucket(nKey);
+    std::set<int> &vNew = vvNew[nUBucket];
+
+    // remove the to-be-replaced tried entry from the tried set
+    CAddrInfo& infoOld = mapInfo[vTried[nPos]];
+    infoOld.fInTried = false;
+    infoOld.nRefCount = 1;
+    // do not update nTried, as we are going to move something else there immediately
+
+    // check whether there is place in that one,
+    if (vNew.size() < ADDRMAN_NEW_BUCKET_SIZE)
+    {
+        // if so, move it back there
+        vNew.insert(vTried[nPos]);
+    } else {
+        // otherwise, move it to the new bucket nId came from (there is certainly place there)
+        vvNew[nOrigin].insert(vTried[nPos]);
+    }
+    nNew++;
+
+    vTried[nPos] = nId;
+    // we just overwrote an entry in vTried; no need to update nTried
+    info.fInTried = true;
+    return;
+}
+
+void CAddrMan::Good_(const CService &addr, int64 nTime)
+{
+//    printf("Good: addr=%s\n", addr.ToString().c_str());
+
+    int nId;
+    CAddrInfo *pinfo = Find(addr, &nId);
+
+    // if not found, bail out
+    if (!pinfo)
+        return;
+
+    CAddrInfo &info = *pinfo;
+
+    // check whether we are talking about the exact same CService (including same port)
+    if (info != addr)
+        return;
+
+    // update info
+    info.nLastSuccess = nTime;
+    info.nLastTry = nTime;
+    info.nTime = nTime;
+    info.nAttempts = 0;
+
+    // if it is already in the tried set, don't do anything else
+    if (info.fInTried)
+        return;
+
+    // find a bucket it is in now
+    int nRnd = GetRandInt(vvNew.size());
+    int nUBucket = -1;
+    for (unsigned int n = 0; n < vvNew.size(); n++)
+    {
+        int nB = (n+nRnd) % vvNew.size();
+        std::set<int> &vNew = vvNew[nB];
+        if (vNew.count(nId))
+        {
+            nUBucket = nB;
+            break;
+        }
+    }
+
+    // if no bucket is found, something bad happened;
+    // TODO: maybe re-add the node, but for now, just bail out
+    if (nUBucket == -1) return;
+
+    printf("Moving %s to tried\n", addr.ToString().c_str());
+
+    // move nId to the tried tables
+    MakeTried(info, nId, nUBucket);
+}
+
+bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty)
+{
+    if (!addr.IsRoutable())
+        return false;
+
+    bool fNew = false;
+    int nId;
+    CAddrInfo *pinfo = Find(addr, &nId);
+
+    if (pinfo)
+    {
+        // periodically update nTime
+        bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60);
+        int64 nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60);
+        if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty))
+            pinfo->nTime = max((int64)0, addr.nTime - nTimePenalty);
+
+        // add services
+        pinfo->nServices |= addr.nServices;
+
+        // do not update if no new information is present
+        if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime))
+            return false;
+
+        // do not update if the entry was already in the "tried" table
+        if (pinfo->fInTried)
+            return false;
+
+        // do not update if the max reference count is reached
+        if (pinfo->nRefCount == ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
+            return false;
+
+        // stochastic test: previous nRefCount == N: 2^N times harder to increase it
+        int nFactor = 1;
+        for (int n=0; n<pinfo->nRefCount; n++)
+            nFactor *= 2;
+        if (nFactor > 1 && (GetRandInt(nFactor) != 0))
+            return false;
+    } else {
+        pinfo = Create(addr, source, &nId);
+        pinfo->nTime = max((int64)0, (int64)pinfo->nTime - nTimePenalty);
+//        printf("Added %s [nTime=%fhr]\n", pinfo->ToString().c_str(), (GetAdjustedTime() - pinfo->nTime) / 3600.0);
+        nNew++;
+        fNew = true;
+    }
+
+    int nUBucket = pinfo->GetNewBucket(nKey, source);
+    std::set<int> &vNew = vvNew[nUBucket];
+    if (!vNew.count(nId))
+    {
+        pinfo->nRefCount++;
+        if (vNew.size() == ADDRMAN_NEW_BUCKET_SIZE)
+            ShrinkNew(nUBucket);
+        vvNew[nUBucket].insert(nId);
+    }
+    return fNew;
+}
+
+void CAddrMan::Attempt_(const CService &addr, int64 nTime)
+{
+    CAddrInfo *pinfo = Find(addr);
+
+    // if not found, bail out
+    if (!pinfo)
+        return;
+
+    CAddrInfo &info = *pinfo;
+
+    // check whether we are talking about the exact same CService (including same port)
+    if (info != addr)
+        return;
+
+    // update info
+    info.nLastTry = nTime;
+    info.nAttempts++;
+}
+
+CAddress CAddrMan::Select_(int nUnkBias)
+{
+    if (size() == 0)
+        return CAddress();
+
+    double nCorTried = sqrt(nTried) * (100.0 - nUnkBias);
+    double nCorNew = sqrt(nNew) * nUnkBias;
+    if ((nCorTried + nCorNew)*GetRandInt(1<<30)/(1<<30) < nCorTried)
+    {
+        // use a tried node
+        double fChanceFactor = 1.0;
+        while(1)
+        {
+            int nKBucket = GetRandInt(vvTried.size());
+            std::vector<int> &vTried = vvTried[nKBucket];
+            if (vTried.size() == 0) continue;
+            int nPos = GetRandInt(vTried.size());
+            assert(mapInfo.count(vTried[nPos]) == 1);
+            CAddrInfo &info = mapInfo[vTried[nPos]];
+            if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30))
+                return info;
+            fChanceFactor *= 1.2;
+        }
+    } else {
+        // use a new node
+        double fChanceFactor = 1.0;
+        while(1)
+        {
+            int nUBucket = GetRandInt(vvNew.size());
+            std::set<int> &vNew = vvNew[nUBucket];
+            if (vNew.size() == 0) continue;
+            int nPos = GetRandInt(vNew.size());
+            std::set<int>::iterator it = vNew.begin();
+            while (nPos--)
+                it++;
+            assert(mapInfo.count(*it) == 1);
+            CAddrInfo &info = mapInfo[*it];
+            if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30))
+                return info;
+            fChanceFactor *= 1.2;
+        }
+    }
+}
+
+#ifdef DEBUG_ADDRMAN
+int CAddrMan::Check_()
+{
+    std::set<int> setTried;
+    std::map<int, int> mapNew;
+
+    if (vRandom.size() != nTried + nNew) return -7;
+
+    for (std::map<int, CAddrInfo>::iterator it = mapInfo.begin(); it != mapInfo.end(); it++)
+    {
+        int n = (*it).first;
+        CAddrInfo &info = (*it).second;
+        if (info.fInTried)
+        {
+
+            if (!info.nLastSuccess) return -1;
+            if (info.nRefCount) return -2;
+            setTried.insert(n);
+        } else {
+            if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS) return -3;
+            if (!info.nRefCount) return -4;
+            mapNew[n] = info.nRefCount;
+        }
+        if (mapAddr[info] != n) return -5;
+        if (info.nRandomPos<0 || info.nRandomPos>=vRandom.size() || vRandom[info.nRandomPos] != n) return -14;
+        if (info.nLastTry < 0) return -6;
+        if (info.nLastSuccess < 0) return -8;
+    }
+
+    if (setTried.size() != nTried) return -9;
+    if (mapNew.size() != nNew) return -10;
+
+    for (int n=0; n<vvTried.size(); n++)
+    {
+        std::vector<int> &vTried = vvTried[n];
+        for (std::vector<int>::iterator it = vTried.begin(); it != vTried.end(); it++)
+        {
+            if (!setTried.count(*it)) return -11;
+            setTried.erase(*it);
+        }
+    }
+
+    for (int n=0; n<vvNew.size(); n++)
+    {
+        std::set<int> &vNew = vvNew[n];
+        for (std::set<int>::iterator it = vNew.begin(); it != vNew.end(); it++)
+        {
+            if (!mapNew.count(*it)) return -12;
+            if (--mapNew[*it] == 0)
+                mapNew.erase(*it);
+        }
+    }
+
+    if (setTried.size()) return -13;
+    if (mapNew.size()) return -15;
+
+    return 0;
+}
+#endif
+
+void CAddrMan::GetAddr_(std::vector<CAddress> &vAddr)
+{
+    int nNodes = ADDRMAN_GETADDR_MAX_PCT*vRandom.size()/100;
+    if (nNodes > ADDRMAN_GETADDR_MAX)
+        nNodes = ADDRMAN_GETADDR_MAX;
+
+    // perform a random shuffle over the first nNodes elements of vRandom (selecting from all)
+    for (int n = 0; n<nNodes; n++)
+    {
+        int nRndPos = GetRandInt(vRandom.size() - n) + n;
+        SwapRandom(n, nRndPos);
+        assert(mapInfo.count(vRandom[n]) == 1);
+        vAddr.push_back(mapInfo[vRandom[n]]);
+    }
+}
+
+void CAddrMan::Connected_(const CService &addr, int64 nTime)
+{
+    CAddrInfo *pinfo = Find(addr);
+
+    // if not found, bail out
+    if (!pinfo)
+        return;
+
+    CAddrInfo &info = *pinfo;
+
+    // check whether we are talking about the exact same CService (including same port)
+    if (info != addr)
+        return;
+
+    // update info
+    int64 nUpdateInterval = 20 * 60;
+    if (nTime - info.nTime > nUpdateInterval)
+        info.nTime = nTime;
+}

+ 449 - 0
ecoin/src/addrman.h

@@ -0,0 +1,449 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef _ECOIN_ADDRMAN
+#define _ECOIN_ADDRMAN 1
+
+#include "netbase.h"
+#include "protocol.h"
+#include "util.h"
+#include "sync.h"
+#include <map>
+#include <vector>
+#include <openssl/rand.h>
+
+/** Extended statistics about a CAddress */
+class CAddrInfo : public CAddress
+{
+private:
+    // where knowledge about this address first came from
+    CNetAddr source;
+
+    // last successful connection by us
+    int64 nLastSuccess;
+
+    // last try whatsoever by us:
+    // int64 CAddress::nLastTry
+
+    // connection attempts since last successful attempt
+    int nAttempts;
+
+    // reference count in new sets (memory only)
+    int nRefCount;
+
+    // in tried set? (memory only)
+    bool fInTried;
+
+    // position in vRandom
+    int nRandomPos;
+
+    friend class CAddrMan;
+
+public:
+
+    IMPLEMENT_SERIALIZE(
+        CAddress* pthis = (CAddress*)(this);
+        READWRITE(*pthis);
+        READWRITE(source);
+        READWRITE(nLastSuccess);
+        READWRITE(nAttempts);
+    )
+
+    void Init()
+    {
+        nLastSuccess = 0;
+        nLastTry = 0;
+        nAttempts = 0;
+        nRefCount = 0;
+        fInTried = false;
+        nRandomPos = -1;
+    }
+
+    CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource)
+    {
+        Init();
+    }
+
+    CAddrInfo() : CAddress(), source()
+    {
+        Init();
+    }
+
+    // Calculate in which "tried" bucket this entry belongs
+    int GetTriedBucket(const std::vector<unsigned char> &nKey) const;
+
+    // Calculate in which "new" bucket this entry belongs, given a certain source
+    int GetNewBucket(const std::vector<unsigned char> &nKey, const CNetAddr& src) const;
+
+    // Calculate in which "new" bucket this entry belongs, using its default source
+    int GetNewBucket(const std::vector<unsigned char> &nKey) const
+    {
+        return GetNewBucket(nKey, source);
+    }
+
+    // Determine whether the statistics about this entry are bad enough so that it can just be deleted
+    bool IsTerrible(int64 nNow = GetAdjustedTime()) const;
+
+    // Calculate the relative chance this entry should be given when selecting nodes to connect to
+    double GetChance(int64 nNow = GetAdjustedTime()) const;
+
+};
+
+// total number of buckets for tried addresses
+#define ADDRMAN_TRIED_BUCKET_COUNT 64
+
+// maximum allowed number of entries in buckets for tried addresses
+#define ADDRMAN_TRIED_BUCKET_SIZE 64
+
+// total number of buckets for new addresses
+#define ADDRMAN_NEW_BUCKET_COUNT 256
+
+// maximum allowed number of entries in buckets for new addresses
+#define ADDRMAN_NEW_BUCKET_SIZE 64
+
+// over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
+#define ADDRMAN_TRIED_BUCKETS_PER_GROUP 4
+
+// over how many buckets entries with new addresses originating from a single group are spread
+#define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 32
+
+// in how many buckets for entries with new addresses a single address may occur
+#define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 4
+
+// how many entries in a bucket with tried addresses are inspected, when selecting one to replace
+#define ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT 4
+
+// how old addresses can maximally be
+#define ADDRMAN_HORIZON_DAYS 30
+
+// after how many failed attempts we give up on a new node
+#define ADDRMAN_RETRIES 3
+
+// how many successive failures are allowed ...
+#define ADDRMAN_MAX_FAILURES 10
+
+// ... in at least this many days
+#define ADDRMAN_MIN_FAIL_DAYS 7
+
+// the maximum percentage of nodes to return in a getaddr call
+#define ADDRMAN_GETADDR_MAX_PCT 23
+
+// the maximum number of nodes to return in a getaddr call
+#define ADDRMAN_GETADDR_MAX 2500
+
+/** Stochastical (IP) address manager */
+class CAddrMan
+{
+private:
+    // critical section to protect the inner data structures
+    mutable CCriticalSection cs;
+
+    // secret key to randomize bucket select with
+    std::vector<unsigned char> nKey;
+
+    // last used nId
+    int nIdCount;
+
+    // table with information about all nIds
+    std::map<int, CAddrInfo> mapInfo;
+
+    // find an nId based on its network address
+    std::map<CNetAddr, int> mapAddr;
+
+    // randomly-ordered vector of all nIds
+    std::vector<int> vRandom;
+
+    // number of "tried" entries
+    int nTried;
+
+    // list of "tried" buckets
+    std::vector<std::vector<int> > vvTried;
+
+    // number of (unique) "new" entries
+    int nNew;
+
+    // list of "new" buckets
+    std::vector<std::set<int> > vvNew;
+
+protected:
+    // Find an entry.
+    CAddrInfo* Find(const CNetAddr& addr, int *pnId = NULL);
+
+    // find an entry, creating it if necessary.
+    // nTime and nServices of found node is updated, if necessary.
+    CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = NULL);
+
+    // Swap two elements in vRandom.
+    void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2);
+
+    // Return position in given bucket to replace.
+    int SelectTried(int nKBucket);
+
+    // Remove an element from a "new" bucket.
+    // This is the only place where actual deletes occur.
+    // They are never deleted while in the "tried" table, only possibly evicted back to the "new" table.
+    int ShrinkNew(int nUBucket);
+
+    // Move an entry from the "new" table(s) to the "tried" table
+    // @pre vvUnkown[nOrigin].count(nId) != 0
+    void MakeTried(CAddrInfo& info, int nId, int nOrigin);
+
+    // Mark an entry "good", possibly moving it from "new" to "tried".
+    void Good_(const CService &addr, int64 nTime);
+
+    // Add an entry to the "new" table.
+    bool Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty);
+
+    // Mark an entry as attempted to connect.
+    void Attempt_(const CService &addr, int64 nTime);
+
+    // Select an address to connect to.
+    // nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100)
+    CAddress Select_(int nUnkBias);
+
+#ifdef DEBUG_ADDRMAN
+    // Perform consistency check. Returns an error code or zero.
+    int Check_();
+#endif
+
+    // Select several addresses at once.
+    void GetAddr_(std::vector<CAddress> &vAddr);
+
+    // Mark an entry as currently-connected-to.
+    void Connected_(const CService &addr, int64 nTime);
+
+public:
+    IMPLEMENT_SERIALIZE
+    (({
+        {
+            LOCK(cs);
+            unsigned char nVersion = 0;
+            READWRITE(nVersion);
+            READWRITE(nKey);
+            READWRITE(nNew);
+            READWRITE(nTried);
+
+            CAddrMan *am = const_cast<CAddrMan*>(this);
+            if (fWrite)
+            {
+                int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT;
+                READWRITE(nUBuckets);
+                std::map<int, int> mapUnkIds;
+                int nIds = 0;
+                for (std::map<int, CAddrInfo>::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++)
+                {
+                    if (nIds == nNew) break; // this means nNew was wrong, oh ow
+                    mapUnkIds[(*it).first] = nIds;
+                    CAddrInfo &info = (*it).second;
+                    if (info.nRefCount)
+                    {
+                        READWRITE(info);
+                        nIds++;
+                    }
+                }
+                nIds = 0;
+                for (std::map<int, CAddrInfo>::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++)
+                {
+                    if (nIds == nTried) break; // this means nTried was wrong, oh ow
+                    CAddrInfo &info = (*it).second;
+                    if (info.fInTried)
+                    {
+                        READWRITE(info);
+                        nIds++;
+                    }
+                }
+                for (std::vector<std::set<int> >::iterator it = am->vvNew.begin(); it != am->vvNew.end(); it++)
+                {
+                    const std::set<int> &vNew = (*it);
+                    int nSize = vNew.size();
+                    READWRITE(nSize);
+                    for (std::set<int>::iterator it2 = vNew.begin(); it2 != vNew.end(); it2++)
+                    {
+                        int nIndex = mapUnkIds[*it2];
+                        READWRITE(nIndex);
+                    }
+                }
+            } else {
+                int nUBuckets = 0;
+                READWRITE(nUBuckets);
+                am->nIdCount = 0;
+                am->mapInfo.clear();
+                am->mapAddr.clear();
+                am->vRandom.clear();
+                am->vvTried = std::vector<std::vector<int> >(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0));
+                am->vvNew = std::vector<std::set<int> >(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>());
+                for (int n = 0; n < am->nNew; n++)
+                {
+                    CAddrInfo &info = am->mapInfo[n];
+                    READWRITE(info);
+                    am->mapAddr[info] = n;
+                    info.nRandomPos = vRandom.size();
+                    am->vRandom.push_back(n);
+                    if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT)
+                    {
+                        am->vvNew[info.GetNewBucket(am->nKey)].insert(n);
+                        info.nRefCount++;
+                    }
+                }
+                am->nIdCount = am->nNew;
+                int nLost = 0;
+                for (int n = 0; n < am->nTried; n++)
+                {
+                    CAddrInfo info;
+                    READWRITE(info);
+                    std::vector<int> &vTried = am->vvTried[info.GetTriedBucket(am->nKey)];
+                    if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE)
+                    {
+                        info.nRandomPos = vRandom.size();
+                        info.fInTried = true;
+                        am->vRandom.push_back(am->nIdCount);
+                        am->mapInfo[am->nIdCount] = info;
+                        am->mapAddr[info] = am->nIdCount;
+                        vTried.push_back(am->nIdCount);
+                        am->nIdCount++;
+                    } else {
+                        nLost++;
+                    }
+                }
+                am->nTried -= nLost;
+                for (int b = 0; b < nUBuckets; b++)
+                {
+                    std::set<int> &vNew = am->vvNew[b];
+                    int nSize = 0;
+                    READWRITE(nSize);
+                    for (int n = 0; n < nSize; n++)
+                    {
+                        int nIndex = 0;
+                        READWRITE(nIndex);
+                        CAddrInfo &info = am->mapInfo[nIndex];
+                        if (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
+                        {
+                            info.nRefCount++;
+                            vNew.insert(nIndex);
+                        }
+                    }
+                }
+            }
+        }
+    });)
+
+    CAddrMan() : vRandom(0), vvTried(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0)), vvNew(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>())
+    {
+         nKey.resize(32);
+         RAND_bytes(&nKey[0], 32);
+
+         nIdCount = 0;
+         nTried = 0;
+         nNew = 0;
+    }
+
+    // Return the number of (unique) addresses in all tables.
+    int size()
+    {
+        return vRandom.size();
+    }
+
+    // Consistency check
+    void Check()
+    {
+#ifdef DEBUG_ADDRMAN
+        {
+            LOCK(cs);
+            int err;
+            if ((err=Check_()))
+                printf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
+        }
+#endif
+    }
+
+    // Add a single address.
+    bool Add(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty = 0)
+    {
+        bool fRet = false;
+        {
+            LOCK(cs);
+            Check();
+            fRet |= Add_(addr, source, nTimePenalty);
+            Check();
+        }
+        if (fRet)
+            printf("Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString().c_str(), nTried, nNew);
+        return fRet;
+    }
+
+    // Add multiple addresses.
+    bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64 nTimePenalty = 0)
+    {
+        int nAdd = 0;
+        {
+            LOCK(cs);
+            Check();
+            for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++)
+                nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0;
+            Check();
+        }
+        if (nAdd)
+            printf("Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew);
+        return nAdd > 0;
+    }
+
+    // Mark an entry as accessible.
+    void Good(const CService &addr, int64 nTime = GetAdjustedTime())
+    {
+        {
+            LOCK(cs);
+            Check();
+            Good_(addr, nTime);
+            Check();
+        }
+    }
+
+    // Mark an entry as connection attempted to.
+    void Attempt(const CService &addr, int64 nTime = GetAdjustedTime())
+    {
+        {
+            LOCK(cs);
+            Check();
+            Attempt_(addr, nTime);
+            Check();
+        }
+    }
+
+    // Choose an address to connect to.
+    // nUnkBias determines how much "new" entries are favored over "tried" ones (0-100).
+    CAddress Select(int nUnkBias = 50)
+    {
+        CAddress addrRet;
+        {
+            LOCK(cs);
+            Check();
+            addrRet = Select_(nUnkBias);
+            Check();
+        }
+        return addrRet;
+    }
+
+    // Return a bunch of addresses, selected at random.
+    std::vector<CAddress> GetAddr()
+    {
+        Check();
+        std::vector<CAddress> vAddr;
+        {
+            LOCK(cs);
+            GetAddr_(vAddr);
+        }
+        Check();
+        return vAddr;
+    }
+
+    // Mark an entry as currently-connected-to.
+    void Connected(const CService &addr, int64 nTime = GetAdjustedTime())
+    {
+        {
+            LOCK(cs);
+            Check();
+            Connected_(addr, nTime);
+            Check();
+        }
+    }
+};
+
+#endif

+ 228 - 0
ecoin/src/alert.cpp

@@ -0,0 +1,228 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+#include <boost/foreach.hpp>
+#include <map>
+#include "alert.h"
+#include "key.h"
+#include "net.h"
+#include "sync.h"
+#include "ui_interface.h"
+
+using namespace std;
+
+map<uint256, CAlert> mapAlerts;
+CCriticalSection cs_mapAlerts;
+
+static const char* pszMainKey = "045d0773e7224ee40c6e69755c0343022b8f125f218a5ec5f6e57b31c00e182e048807d7e66548148795c1dfa4c972b6792ad41efea33111c5fc0d6c86fc0383f1";
+static const char* pszTestKey = "044d5253cb7c6fcb7b790365ac5aeeef359fc52de23f1ce9bffc092c16dd0372444b75ba328ddf4ebb73257e7c882c9bf81d86d1938ad36ac280214ba72aa9aca6";
+
+void CUnsignedAlert::SetNull()
+{
+    nVersion = 1;
+    nRelayUntil = 0;
+    nExpiration = 0;
+    nID = 0;
+    nCancel = 0;
+    setCancel.clear();
+    nMinVer = 0;
+    nMaxVer = 0;
+    setSubVer.clear();
+    nPriority = 0;
+
+    strComment.clear();
+    strStatusBar.clear();
+    strReserved.clear();
+}
+
+std::string CUnsignedAlert::ToString() const
+{
+    std::string strSetCancel;
+    BOOST_FOREACH(int n, setCancel)
+        strSetCancel += strprintf("%d ", n);
+    std::string strSetSubVer;
+    BOOST_FOREACH(std::string str, setSubVer)
+        strSetSubVer += "\"" + str + "\" ";
+    return strprintf(
+        "CAlert(\n"
+        "    nVersion     = %d\n"
+        "    nRelayUntil  = %" PRI64d"\n"
+        "    nExpiration  = %" PRI64d"\n"
+        "    nID          = %d\n"
+        "    nCancel      = %d\n"
+        "    setCancel    = %s\n"
+        "    nMinVer      = %d\n"
+        "    nMaxVer      = %d\n"
+        "    setSubVer    = %s\n"
+        "    nPriority    = %d\n"
+        "    strComment   = \"%s\"\n"
+        "    strStatusBar = \"%s\"\n"
+        ")\n",
+        nVersion,
+        nRelayUntil,
+        nExpiration,
+        nID,
+        nCancel,
+        strSetCancel.c_str(),
+        nMinVer,
+        nMaxVer,
+        strSetSubVer.c_str(),
+        nPriority,
+        strComment.c_str(),
+        strStatusBar.c_str());
+}
+
+void CUnsignedAlert::print() const
+{
+    printf("%s", ToString().c_str());
+}
+
+void CAlert::SetNull()
+{
+    CUnsignedAlert::SetNull();
+    vchMsg.clear();
+    vchSig.clear();
+}
+
+bool CAlert::IsNull() const
+{
+    return (nExpiration == 0);
+}
+
+uint256 CAlert::GetHash() const
+{
+    return Hash(this->vchMsg.begin(), this->vchMsg.end());
+}
+
+bool CAlert::IsInEffect() const
+{
+    return (GetAdjustedTime() < nExpiration);
+}
+
+bool CAlert::Cancels(const CAlert& alert) const
+{
+    if (!IsInEffect())
+        return false; // this was a no-op before 31403
+    return (alert.nID <= nCancel || setCancel.count(alert.nID));
+}
+
+bool CAlert::AppliesTo(int nVersion, std::string strSubVerIn) const
+{
+    return (IsInEffect() &&
+            nMinVer <= nVersion && nVersion <= nMaxVer &&
+            (setSubVer.empty() || setSubVer.count(strSubVerIn)));
+}
+
+bool CAlert::AppliesToMe() const
+{
+    return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<std::string>()));
+}
+
+bool CAlert::RelayTo(CNode* pnode) const
+{
+    if (!IsInEffect())
+        return false;
+    // returns true if wasn't already contained in the set
+    if (pnode->setKnown.insert(GetHash()).second)
+    {
+        if (AppliesTo(pnode->nVersion, pnode->strSubVer) ||
+            AppliesToMe() ||
+            GetAdjustedTime() < nRelayUntil)
+        {
+            pnode->PushMessage("alert", *this);
+            return true;
+        }
+    }
+    return false;
+}
+
+bool CAlert::CheckSignature() const
+{
+    CKey key;
+    if (!key.SetPubKey(ParseHex(fTestNet ? pszTestKey : pszMainKey)))
+        return error("CAlert::CheckSignature() : SetPubKey failed");
+    if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
+        return error("CAlert::CheckSignature() : verify signature failed");
+
+    // Now unserialize the data
+    CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
+    sMsg >> *(CUnsignedAlert*)this;
+    return true;
+}
+
+CAlert CAlert::getAlertByHash(const uint256 &hash)
+{
+    CAlert retval;
+    {
+        LOCK(cs_mapAlerts);
+        map<uint256, CAlert>::iterator mi = mapAlerts.find(hash);
+        if(mi != mapAlerts.end())
+            retval = mi->second;
+    }
+    return retval;
+}
+
+bool CAlert::ProcessAlert()
+{
+    if (!CheckSignature())
+        return false;
+    if (!IsInEffect())
+        return false;
+
+    int maxInt = std::numeric_limits<int>::max();
+    if (nID == maxInt)
+    {
+        if (!(
+                nExpiration == maxInt &&
+                nCancel == (maxInt-1) &&
+                nMinVer == 0 &&
+                nMaxVer == maxInt &&
+                setSubVer.empty() &&
+                nPriority == maxInt &&
+                strStatusBar == "URGENT: Alert key compromised, upgrade required"
+                ))
+            return false;
+    }
+
+    {
+        LOCK(cs_mapAlerts);
+        // Cancel previous alerts
+        for (map<uint256, CAlert>::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();)
+        {
+            const CAlert& alert = (*mi).second;
+            if (Cancels(alert))
+            {
+                printf("cancelling alert %d\n", alert.nID);
+                uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
+                mapAlerts.erase(mi++);
+            }
+            else if (!alert.IsInEffect())
+            {
+                printf("expiring alert %d\n", alert.nID);
+                uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
+                mapAlerts.erase(mi++);
+            }
+            else
+                mi++;
+        }
+
+        // Check if this alert has been cancelled
+        BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
+        {
+            const CAlert& alert = item.second;
+            if (alert.Cancels(*this))
+            {
+                printf("alert already cancelled by %d\n", alert.nID);
+                return false;
+            }
+        }
+
+        // Add to mapAlerts
+        mapAlerts.insert(make_pair(GetHash(), *this));
+        // Notify UI if it applies to me
+        if(AppliesToMe())
+            uiInterface.NotifyAlertChanged(GetHash(), CT_NEW);
+    }
+
+    printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe());
+    return true;
+}

+ 88 - 0
ecoin/src/alert.h

@@ -0,0 +1,88 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+#ifndef _ECOINALERT_H_
+#define _ECOINALERT_H_ 1
+
+#include <set>
+#include <string>
+
+#include "uint256.h"
+#include "util.h"
+
+class CNode;
+class CUnsignedAlert
+{
+public:
+    int nVersion;
+    int64 nRelayUntil;      // when newer nodes stop relaying to newer nodes
+    int64 nExpiration;
+    int nID;
+    int nCancel;
+    std::set<int> setCancel;
+    int nMinVer;            // lowest version inclusive
+    int nMaxVer;            // highest version inclusive
+    std::set<std::string> setSubVer;  // empty matches all
+    int nPriority;
+
+    // Actions
+    std::string strComment;
+    std::string strStatusBar;
+    std::string strReserved;
+
+    IMPLEMENT_SERIALIZE
+    (
+        READWRITE(this->nVersion);
+        nVersion = this->nVersion;
+        READWRITE(nRelayUntil);
+        READWRITE(nExpiration);
+        READWRITE(nID);
+        READWRITE(nCancel);
+        READWRITE(setCancel);
+        READWRITE(nMinVer);
+        READWRITE(nMaxVer);
+        READWRITE(setSubVer);
+        READWRITE(nPriority);
+
+        READWRITE(strComment);
+        READWRITE(strStatusBar);
+        READWRITE(strReserved);
+    )
+
+    void SetNull();
+
+    std::string ToString() const;
+    void print() const;
+};
+
+class CAlert : public CUnsignedAlert
+{
+public:
+    std::vector<unsigned char> vchMsg;
+    std::vector<unsigned char> vchSig;
+
+    CAlert()
+    {
+        SetNull();
+    }
+
+    IMPLEMENT_SERIALIZE
+    (
+        READWRITE(vchMsg);
+        READWRITE(vchSig);
+    )
+
+    void SetNull();
+    bool IsNull() const;
+    uint256 GetHash() const;
+    bool IsInEffect() const;
+    bool Cancels(const CAlert& alert) const;
+    bool AppliesTo(int nVersion, std::string strSubVerIn) const;
+    bool AppliesToMe() const;
+    bool RelayTo(CNode* pnode) const;
+    bool CheckSignature() const;
+    bool ProcessAlert();
+
+    static CAlert getAlertByHash(const uint256 &hash);
+};
+
+#endif

+ 211 - 0
ecoin/src/allocators.h

@@ -0,0 +1,211 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef ECOIN_ALLOCATORS_H
+#define ECOIN_ALLOCATORS_H
+
+#include <string.h>
+#include <string>
+#include <boost/thread/mutex.hpp>
+#include <map>
+#ifdef WIN32
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0501
+#define WIN32_LEAN_AND_MEAN 1
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <windows.h>
+#else
+#include <sys/mman.h>
+#include <limits.h> // for PAGESIZE
+#include <unistd.h> // for sysconf
+#endif
+template <class Locker> class LockedPageManagerBase
+{
+public:
+    LockedPageManagerBase(size_t page_size):
+        page_size(page_size)
+    {
+        // Determine bitmask for extracting page from address
+        assert(!(page_size & (page_size-1))); // size must be power of two
+        page_mask = ~(page_size - 1);
+    }
+
+    // For all pages in affected range, increase lock count
+    void LockRange(void *p, size_t size)
+    {
+        boost::mutex::scoped_lock lock(mutex);
+        if(!size) return;
+        const size_t base_addr = reinterpret_cast<size_t>(p);
+        const size_t start_page = base_addr & page_mask;
+        const size_t end_page = (base_addr + size - 1) & page_mask;
+        for(size_t page = start_page; page <= end_page; page += page_size)
+        {
+            Histogram::iterator it = histogram.find(page);
+            if(it == histogram.end()) // Newly locked page
+            {
+                locker.Lock(reinterpret_cast<void*>(page), page_size);
+                histogram.insert(std::make_pair(page, 1));
+            }
+            else // Page was already locked; increase counter
+            {
+                it->second += 1;
+            }
+        }
+    }
+
+    // For all pages in affected range, decrease lock count
+    void UnlockRange(void *p, size_t size)
+    {
+        boost::mutex::scoped_lock lock(mutex);
+        if(!size) return;
+        const size_t base_addr = reinterpret_cast<size_t>(p);
+        const size_t start_page = base_addr & page_mask;
+        const size_t end_page = (base_addr + size - 1) & page_mask;
+        for(size_t page = start_page; page <= end_page; page += page_size)
+        {
+            Histogram::iterator it = histogram.find(page);
+            assert(it != histogram.end()); // Cannot unlock an area that was not locked
+            // Decrease counter for page, when it is zero, the page will be unlocked
+            it->second -= 1;
+            if(it->second == 0) // Nothing on the page anymore that keeps it locked
+            {
+                // Unlock page and remove the count from histogram
+                locker.Unlock(reinterpret_cast<void*>(page), page_size);
+                histogram.erase(it);
+            }
+        }
+    }
+
+    // Get number of locked pages for diagnostics
+    int GetLockedPageCount()
+    {
+        boost::mutex::scoped_lock lock(mutex);
+        return histogram.size();
+    }
+
+private:
+    Locker locker;
+    boost::mutex mutex;
+    size_t page_size, page_mask;
+    // map of page base address to lock count
+    typedef std::map<size_t,int> Histogram;
+    Histogram histogram;
+};
+
+/** Determine system page size in bytes */
+static inline size_t GetSystemPageSize()
+{
+    size_t page_size;
+#if defined(WIN32)
+    SYSTEM_INFO sSysInfo;
+    GetSystemInfo(&sSysInfo);
+    page_size = sSysInfo.dwPageSize;
+#elif defined(PAGESIZE) // defined in limits.h
+    page_size = PAGESIZE;
+#else // assume some POSIX OS
+    page_size = sysconf(_SC_PAGESIZE);
+#endif
+    return page_size;
+}
+class MemoryPageLocker
+{
+public:
+    bool Lock(const void *addr, size_t len)
+    {
+#ifdef WIN32
+        return VirtualLock(const_cast<void*>(addr), len);
+#else
+        return mlock(addr, len) == 0;
+#endif
+    }
+    bool Unlock(const void *addr, size_t len)
+    {
+#ifdef WIN32
+        return VirtualUnlock(const_cast<void*>(addr), len);
+#else
+        return munlock(addr, len) == 0;
+#endif
+    }
+};
+
+class LockedPageManager: public LockedPageManagerBase<MemoryPageLocker>
+{
+public:
+    static LockedPageManager instance; // instantiated in util.cpp
+private:
+    LockedPageManager():
+        LockedPageManagerBase<MemoryPageLocker>(GetSystemPageSize())
+    {}
+};
+
+template<typename T>
+struct secure_allocator : public std::allocator<T>
+{
+    typedef std::allocator<T> base;
+    typedef typename base::size_type size_type;
+    typedef typename base::difference_type  difference_type;
+    typedef typename base::pointer pointer;
+    typedef typename base::const_pointer const_pointer;
+    typedef typename base::reference reference;
+    typedef typename base::const_reference const_reference;
+    typedef typename base::value_type value_type;
+    secure_allocator() throw() {}
+    secure_allocator(const secure_allocator& a) throw() : base(a) {}
+    template <typename U>
+    secure_allocator(const secure_allocator<U>& a) throw() : base(a) {}
+    ~secure_allocator() throw() {}
+    template<typename _Other> struct rebind
+    { typedef secure_allocator<_Other> other; };
+
+    T* allocate(std::size_t n, const void *hint = 0)
+    {
+        T *p;
+        p = std::allocator<T>::allocate(n, hint);
+        if (p != NULL)
+            LockedPageManager::instance.LockRange(p, sizeof(T) * n);
+        return p;
+    }
+
+    void deallocate(T* p, std::size_t n)
+    {
+        if (p != NULL)
+        {
+            memset(p, 0, sizeof(T) * n);
+            LockedPageManager::instance.UnlockRange(p, sizeof(T) * n);
+        }
+        std::allocator<T>::deallocate(p, n);
+    }
+};
+
+template<typename T>
+struct zero_after_free_allocator : public std::allocator<T>
+{
+    typedef std::allocator<T> base;
+    typedef typename base::size_type size_type;
+    typedef typename base::difference_type  difference_type;
+    typedef typename base::pointer pointer;
+    typedef typename base::const_pointer const_pointer;
+    typedef typename base::reference reference;
+    typedef typename base::const_reference const_reference;
+    typedef typename base::value_type value_type;
+    zero_after_free_allocator() throw() {}
+    zero_after_free_allocator(const zero_after_free_allocator& a) throw() : base(a) {}
+    template <typename U>
+    zero_after_free_allocator(const zero_after_free_allocator<U>& a) throw() : base(a) {}
+    ~zero_after_free_allocator() throw() {}
+    template<typename _Other> struct rebind
+    { typedef zero_after_free_allocator<_Other> other; };
+
+    void deallocate(T* p, std::size_t n)
+    {
+        if (p != NULL)
+            memset(p, 0, sizeof(T) * n);
+        std::allocator<T>::deallocate(p, n);
+    }
+};
+
+typedef std::basic_string<char, std::char_traits<char>, secure_allocator<char> > SecureString;
+
+#endif

+ 434 - 0
ecoin/src/base58.h

@@ -0,0 +1,434 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef ECOIN_BASE58_H
+#define ECOIN_BASE58_H
+
+#include <string>
+#include <vector>
+#include "bignum.h"
+#include "key.h"
+#include "script.h"
+
+static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
+
+// Encode a byte sequence as a base58-encoded string
+inline std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend)
+{
+    CAutoBN_CTX pctx;
+    CBigNum bn58 = 58;
+    CBigNum bn0 = 0;
+
+    // Convert big endian data to little endian
+    // Extra zero at the end make sure bignum will interpret as a positive number
+    std::vector<unsigned char> vchTmp(pend-pbegin+1, 0);
+    reverse_copy(pbegin, pend, vchTmp.begin());
+
+    // Convert little endian data to bignum
+    CBigNum bn;
+    bn.setvch(vchTmp);
+
+    // Convert bignum to std::string
+    std::string str;
+    // Expected size increase from base58 conversion is approximately 137%
+    // use 138% to be safe
+    str.reserve((pend - pbegin) * 138 / 100 + 1);
+    CBigNum dv;
+    CBigNum rem;
+    while (bn > bn0)
+    {
+        if (!BN_div(&dv, &rem, &bn, &bn58, pctx))
+            throw bignum_error("EncodeBase58 : BN_div failed");
+        bn = dv;
+        unsigned int c = rem.getulong();
+        str += pszBase58[c];
+    }
+
+    // Leading zeroes encoded as base58 zeros
+    for (const unsigned char* p = pbegin; p < pend && *p == 0; p++)
+        str += pszBase58[0];
+
+    // Convert little endian std::string to big endian
+    reverse(str.begin(), str.end());
+    return str;
+}
+
+// Encode a byte vector as a base58-encoded string
+inline std::string EncodeBase58(const std::vector<unsigned char>& vch)
+{
+    return EncodeBase58(&vch[0], &vch[0] + vch.size());
+}
+
+// Decode a base58-encoded string psz into byte vector vchRet
+// returns true if decoding is successful
+inline bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet)
+{
+    CAutoBN_CTX pctx;
+    vchRet.clear();
+    CBigNum bn58 = 58;
+    CBigNum bn = 0;
+    CBigNum bnChar;
+    while (isspace(*psz))
+        psz++;
+
+    // Convert big endian string to bignum
+    for (const char* p = psz; *p; p++)
+    {
+        const char* p1 = strchr(pszBase58, *p);
+        if (p1 == NULL)
+        {
+            while (isspace(*p))
+                p++;
+            if (*p != '\0')
+                return false;
+            break;
+        }
+        bnChar.setulong(p1 - pszBase58);
+        if (!BN_mul(&bn, &bn, &bn58, pctx))
+            throw bignum_error("DecodeBase58 : BN_mul failed");
+        bn += bnChar;
+    }
+
+    // Get bignum as little endian data
+    std::vector<unsigned char> vchTmp = bn.getvch();
+
+    // Trim off sign byte if present
+    if (vchTmp.size() >= 2 && vchTmp.end()[-1] == 0 && vchTmp.end()[-2] >= 0x80)
+        vchTmp.erase(vchTmp.end()-1);
+
+    // Restore leading zeros
+    int nLeadingZeros = 0;
+    for (const char* p = psz; *p == pszBase58[0]; p++)
+        nLeadingZeros++;
+    vchRet.assign(nLeadingZeros + vchTmp.size(), 0);
+
+    // Convert little endian data to big endian
+    reverse_copy(vchTmp.begin(), vchTmp.end(), vchRet.end() - vchTmp.size());
+    return true;
+}
+
+// Decode a base58-encoded string str into byte vector vchRet
+// returns true if decoding is successful
+inline bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet)
+{
+    return DecodeBase58(str.c_str(), vchRet);
+}
+
+// Encode a byte vector to a base58-encoded string, including checksum
+inline std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn)
+{
+    // add 4-byte hash check to the end
+    std::vector<unsigned char> vch(vchIn);
+    uint256 hash = Hash(vch.begin(), vch.end());
+    vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4);
+    return EncodeBase58(vch);
+}
+
+// Decode a base58-encoded string psz that includes a checksum, into byte vector vchRet
+// returns true if decoding is successful
+inline bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet)
+{
+    if (!DecodeBase58(psz, vchRet))
+        return false;
+    if (vchRet.size() < 4)
+    {
+        vchRet.clear();
+        return false;
+    }
+    uint256 hash = Hash(vchRet.begin(), vchRet.end()-4);
+    if (memcmp(&hash, &vchRet.end()[-4], 4) != 0)
+    {
+        vchRet.clear();
+        return false;
+    }
+    vchRet.resize(vchRet.size()-4);
+    return true;
+}
+
+// Decode a base58-encoded string str that includes a checksum, into byte vector vchRet
+// returns true if decoding is successful
+inline bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet)
+{
+    return DecodeBase58Check(str.c_str(), vchRet);
+}
+
+/** Base class for all base58-encoded data */
+class CBase58Data
+{
+protected:
+    // the version byte
+    unsigned char nVersion;
+
+    // the actually encoded data
+    std::vector<unsigned char> vchData;
+
+    CBase58Data()
+    {
+        nVersion = 0;
+        vchData.clear();
+    }
+
+    ~CBase58Data()
+    {
+        // zero the memory, as it may contain sensitive data
+        if (!vchData.empty())
+            memset(&vchData[0], 0, vchData.size());
+    }
+
+    void SetData(int nVersionIn, const void* pdata, size_t nSize)
+    {
+        nVersion = nVersionIn;
+        vchData.resize(nSize);
+        if (!vchData.empty())
+            memcpy(&vchData[0], pdata, nSize);
+    }
+
+    void SetData(int nVersionIn, const unsigned char *pbegin, const unsigned char *pend)
+    {
+        SetData(nVersionIn, (void*)pbegin, pend - pbegin);
+    }
+
+public:
+    bool SetString(const char* psz)
+    {
+        std::vector<unsigned char> vchTemp;
+        DecodeBase58Check(psz, vchTemp);
+        if (vchTemp.empty())
+        {
+            vchData.clear();
+            nVersion = 0;
+            return false;
+        }
+        nVersion = vchTemp[0];
+        vchData.resize(vchTemp.size() - 1);
+        if (!vchData.empty())
+            memcpy(&vchData[0], &vchTemp[1], vchData.size());
+        memset(&vchTemp[0], 0, vchTemp.size());
+        return true;
+    }
+
+    bool SetString(const std::string& str)
+    {
+        return SetString(str.c_str());
+    }
+
+    std::string ToString() const
+    {
+        std::vector<unsigned char> vch(1, nVersion);
+        vch.insert(vch.end(), vchData.begin(), vchData.end());
+        return EncodeBase58Check(vch);
+    }
+
+    int CompareTo(const CBase58Data& b58) const
+    {
+        if (nVersion < b58.nVersion) return -1;
+        if (nVersion > b58.nVersion) return  1;
+        if (vchData < b58.vchData)   return -1;
+        if (vchData > b58.vchData)   return  1;
+        return 0;
+    }
+
+    bool operator==(const CBase58Data& b58) const { return CompareTo(b58) == 0; }
+    bool operator<=(const CBase58Data& b58) const { return CompareTo(b58) <= 0; }
+    bool operator>=(const CBase58Data& b58) const { return CompareTo(b58) >= 0; }
+    bool operator< (const CBase58Data& b58) const { return CompareTo(b58) <  0; }
+    bool operator> (const CBase58Data& b58) const { return CompareTo(b58) >  0; }
+};
+
+class CEcoinAddress;
+class CEcoinAddressVisitor : public boost::static_visitor<bool>
+{
+private:
+    CEcoinAddress *addr;
+public:
+    CEcoinAddressVisitor(CEcoinAddress *addrIn) : addr(addrIn) { }
+    bool operator()(const CKeyID &id) const;
+    bool operator()(const CScriptID &id) const;
+    bool operator()(const CNoDestination &no) const;
+};
+
+class CEcoinAddress : public CBase58Data
+{
+public:
+    enum
+    {
+        PUBKEY_ADDRESS = 33,
+        SCRIPT_ADDRESS = 20,
+        PUBKEY_ADDRESS_TEST = 92,
+        SCRIPT_ADDRESS_TEST = 196,
+    };
+
+    bool Set(const CKeyID &id) {
+        SetData(fTestNet ? PUBKEY_ADDRESS_TEST : PUBKEY_ADDRESS, &id, 20);
+        return true;
+    }
+
+    bool Set(const CScriptID &id) {
+        SetData(fTestNet ? SCRIPT_ADDRESS_TEST : SCRIPT_ADDRESS, &id, 20);
+        return true;
+    }
+
+    bool Set(const CTxDestination &dest)
+    {
+        return boost::apply_visitor(CEcoinAddressVisitor(this), dest);
+    }
+
+    bool IsValid() const
+    {
+        unsigned int nExpectedSize = 20;
+        bool fExpectTestNet = false;
+        switch(nVersion)
+        {
+            case PUBKEY_ADDRESS:
+                nExpectedSize = 20; // Hash of public key
+                fExpectTestNet = false;
+                break;
+            case SCRIPT_ADDRESS:
+                nExpectedSize = 20; // Hash of CScript
+                fExpectTestNet = false;
+                break;
+
+            case PUBKEY_ADDRESS_TEST:
+                nExpectedSize = 20;
+                fExpectTestNet = true;
+                break;
+            case SCRIPT_ADDRESS_TEST:
+                nExpectedSize = 20;
+                fExpectTestNet = true;
+                break;
+
+            default:
+                return false;
+        }
+        return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize;
+    }
+
+    CEcoinAddress()
+    {
+    }
+
+    CEcoinAddress(const CTxDestination &dest)
+    {
+        Set(dest);
+    }
+
+    CEcoinAddress(const std::string& strAddress)
+    {
+        SetString(strAddress);
+    }
+
+    CEcoinAddress(const char* pszAddress)
+    {
+        SetString(pszAddress);
+    }
+
+    CTxDestination Get() const {
+        if (!IsValid())
+            return CNoDestination();
+        switch (nVersion) {
+        case PUBKEY_ADDRESS:
+        case PUBKEY_ADDRESS_TEST: {
+            uint160 id;
+            memcpy(&id, &vchData[0], 20);
+            return CKeyID(id);
+        }
+        case SCRIPT_ADDRESS:
+        case SCRIPT_ADDRESS_TEST: {
+            uint160 id;
+            memcpy(&id, &vchData[0], 20);
+            return CScriptID(id);
+        }
+        }
+        return CNoDestination();
+    }
+
+    bool GetKeyID(CKeyID &keyID) const {
+        if (!IsValid())
+            return false;
+        switch (nVersion) {
+        case PUBKEY_ADDRESS:
+        case PUBKEY_ADDRESS_TEST: {
+            uint160 id;
+            memcpy(&id, &vchData[0], 20);
+            keyID = CKeyID(id);
+            return true;
+        }
+        default: return false;
+        }
+    }
+
+    bool IsScript() const {
+        if (!IsValid())
+            return false;
+        switch (nVersion) {
+        case SCRIPT_ADDRESS:
+        case SCRIPT_ADDRESS_TEST: {
+            return true;
+        }
+        default: return false;
+        }
+    }
+};
+
+bool inline CEcoinAddressVisitor::operator()(const CKeyID &id) const         { return addr->Set(id); }
+bool inline CEcoinAddressVisitor::operator()(const CScriptID &id) const      { return addr->Set(id); }
+bool inline CEcoinAddressVisitor::operator()(const CNoDestination &id) const { return false; }
+
+/** A base58-encoded secret key */
+class CEcoinSecret : public CBase58Data
+{
+public:
+    void SetSecret(const CSecret& vchSecret, bool fCompressed)
+    {
+        assert(vchSecret.size() == 32);
+        SetData(128 + (fTestNet ? CEcoinAddress::PUBKEY_ADDRESS_TEST : CEcoinAddress::PUBKEY_ADDRESS), &vchSecret[0], vchSecret.size());
+        if (fCompressed)
+            vchData.push_back(1);
+    }
+
+    CSecret GetSecret(bool &fCompressedOut)
+    {
+        CSecret vchSecret;
+        vchSecret.resize(32);
+        memcpy(&vchSecret[0], &vchData[0], 32);
+        fCompressedOut = vchData.size() == 33;
+        return vchSecret;
+    }
+
+    bool IsValid() const
+    {
+        bool fExpectTestNet = false;
+        switch(nVersion)
+        {
+            case (128 + CEcoinAddress::PUBKEY_ADDRESS):
+                break;
+
+            case (128 + CEcoinAddress::PUBKEY_ADDRESS_TEST):
+                fExpectTestNet = true;
+                break;
+
+            default:
+                return false;
+        }
+        return fExpectTestNet == fTestNet && (vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1));
+    }
+
+    bool SetString(const char* pszSecret)
+    {
+        return CBase58Data::SetString(pszSecret) && IsValid();
+    }
+
+    bool SetString(const std::string& strSecret)
+    {
+        return SetString(strSecret.c_str());
+    }
+
+    CEcoinSecret(const CSecret& vchSecret, bool fCompressed)
+    {
+        SetSecret(vchSecret, fCompressed);
+    }
+
+    CEcoinSecret()
+    {
+    }
+};
+
+#endif

+ 717 - 0
ecoin/src/bignum.h

@@ -0,0 +1,717 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef ECOIN_BIGNUM_H
+#define ECOIN_BIGNUM_H
+
+#include <stdexcept>
+#include <vector>
+#include <openssl/bn.h>
+
+#include "util.h" // for uint64
+
+/** Errors thrown by the bignum class */
+class bignum_error : public std::runtime_error
+{
+public:
+    explicit bignum_error(const std::string& str) : std::runtime_error(str) {}
+};
+
+
+/** RAII encapsulated BN_CTX (OpenSSL bignum context) */
+class CAutoBN_CTX
+{
+protected:
+    BN_CTX* pctx;
+    BN_CTX* operator=(BN_CTX* pnew) { return pctx = pnew; }
+
+public:
+    CAutoBN_CTX()
+    {
+        pctx = BN_CTX_new();
+        if (pctx == NULL)
+            throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned NULL");
+    }
+
+    ~CAutoBN_CTX()
+    {
+        if (pctx != NULL)
+            BN_CTX_free(pctx);
+    }
+
+    operator BN_CTX*() { return pctx; }
+    BN_CTX& operator*() { return *pctx; }
+    BN_CTX** operator&() { return &pctx; }
+    bool operator!() { return (pctx == NULL); }
+};
+
+
+/** C++ wrapper for BIGNUM (OpenSSL bignum) */
+class CBigNum : public BIGNUM
+{
+public:
+    CBigNum()
+    {
+        BN_init(this);
+    }
+
+    CBigNum(const CBigNum& b)
+    {
+        BN_init(this);
+        if (!BN_copy(this, &b))
+        {
+            BN_clear_free(this);
+            throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_copy failed");
+        }
+    }
+
+    CBigNum& operator=(const CBigNum& b)
+    {
+        if (!BN_copy(this, &b))
+            throw bignum_error("CBigNum::operator= : BN_copy failed");
+        return (*this);
+    }
+
+    ~CBigNum()
+    {
+        BN_clear_free(this);
+    }
+
+    //CBigNum(char n) is not portable.  Use 'signed char' or 'unsigned char'.
+    CBigNum(signed char n)      { BN_init(this); if (n >= 0) setulong(n); else setint64(n); }
+    CBigNum(short n)            { BN_init(this); if (n >= 0) setulong(n); else setint64(n); }
+    CBigNum(int n)              { BN_init(this); if (n >= 0) setulong(n); else setint64(n); }
+    CBigNum(long n)             { BN_init(this); if (n >= 0) setulong(n); else setint64(n); }
+    CBigNum(int64 n)            { BN_init(this); setint64(n); }
+    CBigNum(unsigned char n)    { BN_init(this); setulong(n); }
+    CBigNum(unsigned short n)   { BN_init(this); setulong(n); }
+    CBigNum(unsigned int n)     { BN_init(this); setulong(n); }
+    CBigNum(unsigned long n)    { BN_init(this); setulong(n); }
+    CBigNum(uint64 n)           { BN_init(this); setuint64(n); }
+    explicit CBigNum(uint256 n) { BN_init(this); setuint256(n); }
+
+    explicit CBigNum(const std::vector<unsigned char>& vch)
+    {
+        BN_init(this);
+        setvch(vch);
+    }
+
+    /** Generates a cryptographically secure random number between zero and range exclusive
+    * i.e. 0 < returned number < range
+    * @param range The upper bound on the number.
+    * @return
+    */
+    static CBigNum  randBignum(const CBigNum& range) {
+        CBigNum ret;
+        if(!BN_rand_range(&ret, &range)){
+            throw bignum_error("CBigNum:rand element : BN_rand_range failed");
+        }
+        return ret;
+    }
+
+    /** Generates a cryptographically secure random k-bit number
+    * @param k The bit length of the number.
+    * @return
+    */
+    static CBigNum RandKBitBigum(const uint32_t k){
+        CBigNum ret;
+        if(!BN_rand(&ret, k, -1, 0)){
+            throw bignum_error("CBigNum:rand element : BN_rand failed");
+        }
+        return ret;
+    }
+
+    /**Returns the size in bits of the underlying bignum.
+     *
+     * @return the size
+     */
+    int bitSize() const{
+        return  BN_num_bits(this);
+    }
+
+
+    void setulong(unsigned long n)
+    {
+        if (!BN_set_word(this, n))
+            throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed");
+    }
+
+    unsigned long getulong() const
+    {
+        return BN_get_word(this);
+    }
+
+    unsigned int getuint() const
+    {
+        return BN_get_word(this);
+    }
+
+    int getint() const
+    {
+        unsigned long n = BN_get_word(this);
+        if (!BN_is_negative(this))
+            return (n > (unsigned long)std::numeric_limits<int>::max() ? std::numeric_limits<int>::max() : n);
+        else
+            return (n > (unsigned long)std::numeric_limits<int>::max() ? std::numeric_limits<int>::min() : -(int)n);
+    }
+
+    void setint64(int64 sn)
+    {
+        unsigned char pch[sizeof(sn) + 6];
+        unsigned char* p = pch + 4;
+        bool fNegative;
+        uint64 n;
+
+        if (sn < (int64)0)
+        {
+            // Since the minimum signed integer cannot be represented as positive so long as its type is signed, and it's not well-defined what happens if you make it unsigned before negating it, we instead increment the negative integer by 1, convert it, then increment the (now positive) unsigned integer by 1 to compensate
+            n = -(sn + 1);
+            ++n;
+            fNegative = true;
+        } else {
+            n = sn;
+            fNegative = false;
+        }
+
+        bool fLeadingZeroes = true;
+        for (int i = 0; i < 8; i++)
+        {
+            unsigned char c = (n >> 56) & 0xff;
+            n <<= 8;
+            if (fLeadingZeroes)
+            {
+                if (c == 0)
+                    continue;
+                if (c & 0x80)
+                    *p++ = (fNegative ? 0x80 : 0);
+                else if (fNegative)
+                    c |= 0x80;
+                fLeadingZeroes = false;
+            }
+            *p++ = c;
+        }
+        unsigned int nSize = p - (pch + 4);
+        pch[0] = (nSize >> 24) & 0xff;
+        pch[1] = (nSize >> 16) & 0xff;
+        pch[2] = (nSize >> 8) & 0xff;
+        pch[3] = (nSize) & 0xff;
+        BN_mpi2bn(pch, p - pch, this);
+    }
+
+    uint64 getuint64()
+    {
+        unsigned int nSize = BN_bn2mpi(this, NULL);
+        if (nSize < 4)
+            return 0;
+        std::vector<unsigned char> vch(nSize);
+        BN_bn2mpi(this, &vch[0]);
+        if (vch.size() > 4)
+            vch[4] &= 0x7f;
+        uint64 n = 0;
+        for (unsigned int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--)
+            ((unsigned char*)&n)[i] = vch[j];
+        return n;
+    }
+
+    void setuint64(uint64 n)
+    {
+        unsigned char pch[sizeof(n) + 6];
+        unsigned char* p = pch + 4;
+        bool fLeadingZeroes = true;
+        for (int i = 0; i < 8; i++)
+        {
+            unsigned char c = (n >> 56) & 0xff;
+            n <<= 8;
+            if (fLeadingZeroes)
+            {
+                if (c == 0)
+                    continue;
+                if (c & 0x80)
+                    *p++ = 0;
+                fLeadingZeroes = false;
+            }
+            *p++ = c;
+        }
+        unsigned int nSize = p - (pch + 4);
+        pch[0] = (nSize >> 24) & 0xff;
+        pch[1] = (nSize >> 16) & 0xff;
+        pch[2] = (nSize >> 8) & 0xff;
+        pch[3] = (nSize) & 0xff;
+        BN_mpi2bn(pch, p - pch, this);
+    }
+
+    void setuint256(uint256 n)
+    {
+        unsigned char pch[sizeof(n) + 6];
+        unsigned char* p = pch + 4;
+        bool fLeadingZeroes = true;
+        unsigned char* pbegin = (unsigned char*)&n;
+        unsigned char* psrc = pbegin + sizeof(n);
+        while (psrc != pbegin)
+        {
+            unsigned char c = *(--psrc);
+            if (fLeadingZeroes)
+            {
+                if (c == 0)
+                    continue;
+                if (c & 0x80)
+                    *p++ = 0;
+                fLeadingZeroes = false;
+            }
+            *p++ = c;
+        }
+        unsigned int nSize = p - (pch + 4);
+        pch[0] = (nSize >> 24) & 0xff;
+        pch[1] = (nSize >> 16) & 0xff;
+        pch[2] = (nSize >> 8) & 0xff;
+        pch[3] = (nSize >> 0) & 0xff;
+        BN_mpi2bn(pch, p - pch, this);
+    }
+
+    uint256 getuint256() const
+    {
+        unsigned int nSize = BN_bn2mpi(this, NULL);
+        if (nSize < 4)
+            return 0;
+        std::vector<unsigned char> vch(nSize);
+        BN_bn2mpi(this, &vch[0]);
+        if (vch.size() > 4)
+            vch[4] &= 0x7f;
+        uint256 n = 0;
+        for (unsigned int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--)
+            ((unsigned char*)&n)[i] = vch[j];
+        return n;
+    }
+
+
+    void setvch(const std::vector<unsigned char>& vch)
+    {
+        std::vector<unsigned char> vch2(vch.size() + 4);
+        unsigned int nSize = vch.size();
+        // BIGNUM's byte stream format expects 4 bytes of
+        // big endian size data info at the front
+        vch2[0] = (nSize >> 24) & 0xff;
+        vch2[1] = (nSize >> 16) & 0xff;
+        vch2[2] = (nSize >> 8) & 0xff;
+        vch2[3] = (nSize >> 0) & 0xff;
+        // swap data to big endian
+        reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4);
+        BN_mpi2bn(&vch2[0], vch2.size(), this);
+    }
+
+    std::vector<unsigned char> getvch() const
+    {
+        unsigned int nSize = BN_bn2mpi(this, NULL);
+        if (nSize <= 4)
+            return std::vector<unsigned char>();
+        std::vector<unsigned char> vch(nSize);
+        BN_bn2mpi(this, &vch[0]);
+        vch.erase(vch.begin(), vch.begin() + 4);
+        reverse(vch.begin(), vch.end());
+        return vch;
+    }
+
+    CBigNum& SetCompact(unsigned int nCompact)
+    {
+        unsigned int nSize = nCompact >> 24;
+        std::vector<unsigned char> vch(4 + nSize);
+        vch[3] = nSize;
+        if (nSize >= 1) vch[4] = (nCompact >> 16) & 0xff;
+        if (nSize >= 2) vch[5] = (nCompact >> 8) & 0xff;
+        if (nSize >= 3) vch[6] = (nCompact >> 0) & 0xff;
+        BN_mpi2bn(&vch[0], vch.size(), this);
+        return *this;
+    }
+
+    unsigned int GetCompact() const
+    {
+        unsigned int nSize = BN_bn2mpi(this, NULL);
+        std::vector<unsigned char> vch(nSize);
+        nSize -= 4;
+        BN_bn2mpi(this, &vch[0]);
+        unsigned int nCompact = nSize << 24;
+        if (nSize >= 1) nCompact |= (vch[4] << 16);
+        if (nSize >= 2) nCompact |= (vch[5] << 8);
+        if (nSize >= 3) nCompact |= (vch[6] << 0);
+        return nCompact;
+    }
+
+    void SetHex(const std::string& str)
+    {
+        // skip 0x
+        const char* psz = str.c_str();
+        while (isspace(*psz))
+            psz++;
+        bool fNegative = false;
+        if (*psz == '-')
+        {
+            fNegative = true;
+            psz++;
+        }
+        if (psz[0] == '0' && tolower(psz[1]) == 'x')
+            psz += 2;
+        while (isspace(*psz))
+            psz++;
+
+        // hex string to bignum
+        static const signed char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 };
+        *this = 0;
+        while (isxdigit(*psz))
+        {
+            *this <<= 4;
+            int n = phexdigit[(unsigned char)*psz++];
+            *this += n;
+        }
+        if (fNegative)
+            *this = 0 - *this;
+    }
+
+    std::string ToString(int nBase=10) const
+    {
+        CAutoBN_CTX pctx;
+        CBigNum bnBase = nBase;
+        CBigNum bn0 = 0;
+        std::string str;
+        CBigNum bn = *this;
+        BN_set_negative(&bn, false);
+        CBigNum dv;
+        CBigNum rem;
+        if (BN_cmp(&bn, &bn0) == 0)
+            return "0";
+        while (BN_cmp(&bn, &bn0) > 0)
+        {
+            if (!BN_div(&dv, &rem, &bn, &bnBase, pctx))
+                throw bignum_error("CBigNum::ToString() : BN_div failed");
+            bn = dv;
+            unsigned int c = rem.getulong();
+            str += "0123456789abcdef"[c];
+        }
+        if (BN_is_negative(this))
+            str += "-";
+        reverse(str.begin(), str.end());
+        return str;
+    }
+
+    std::string GetHex() const
+    {
+        return ToString(16);
+    }
+
+    unsigned int GetSerializeSize(int nType=0, int nVersion=PROTOCOL_VERSION) const
+    {
+        return ::GetSerializeSize(getvch(), nType, nVersion);
+    }
+
+    template<typename Stream>
+    void Serialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) const
+    {
+        ::Serialize(s, getvch(), nType, nVersion);
+    }
+
+    template<typename Stream>
+    void Unserialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION)
+    {
+        std::vector<unsigned char> vch;
+        ::Unserialize(s, vch, nType, nVersion);
+        setvch(vch);
+    }
+
+    /**
+    * exponentiation with an int. this^e
+    * @param e the exponent as an int
+    * @return
+    */
+    CBigNum pow(const int e) const {
+        return this->pow(CBigNum(e));
+    }
+
+    /**
+     * exponentiation this^e
+     * @param e the exponent
+     * @return
+     */
+    CBigNum pow(const CBigNum& e) const {
+        CAutoBN_CTX pctx;
+        CBigNum ret;
+        if (!BN_exp(&ret, this, &e, pctx))
+            throw bignum_error("CBigNum::pow : BN_exp failed");
+        return ret;
+    }
+
+    /**
+     * modular multiplication: (this * b) mod m
+     * @param b operand
+     * @param m modulus
+     */
+    CBigNum mul_mod(const CBigNum& b, const CBigNum& m) const {
+        CAutoBN_CTX pctx;
+        CBigNum ret;
+        if (!BN_mod_mul(&ret, this, &b, &m, pctx))
+            throw bignum_error("CBigNum::mul_mod : BN_mod_mul failed");
+        
+        return ret;
+    }
+
+    /**
+     * modular exponentiation: this^e mod n
+     * @param e exponent
+     * @param m modulus
+     */
+    CBigNum pow_mod(const CBigNum& e, const CBigNum& m) const {
+        CAutoBN_CTX pctx;
+        CBigNum ret;
+        if( e < 0){
+            // g^-x = (g^-1)^x
+            CBigNum inv = this->inverse(m);
+            CBigNum posE = e * -1;
+            if (!BN_mod_exp(&ret, &inv, &posE, &m, pctx))
+                throw bignum_error("CBigNum::pow_mod: BN_mod_exp failed on negative exponent");
+        }else
+            if (!BN_mod_exp(&ret, this, &e, &m, pctx))
+                throw bignum_error("CBigNum::pow_mod : BN_mod_exp failed");
+
+        return ret;
+    }
+
+    /**
+    * Calculates the inverse of this element mod m.
+    * i.e. i such this*i = 1 mod m
+    * @param m the modu
+    * @return the inverse
+    */
+    CBigNum inverse(const CBigNum& m) const {
+        CAutoBN_CTX pctx;
+        CBigNum ret;
+        if (!BN_mod_inverse(&ret, this, &m, pctx))
+            throw bignum_error("CBigNum::inverse*= :BN_mod_inverse");
+        return ret;
+    }
+
+    /**
+     * Generates a random (safe) prime of numBits bits
+     * @param numBits the number of bits
+     * @param safe true for a safe prime
+     * @return the prime
+     */
+    static CBigNum generatePrime(const unsigned int numBits, bool safe = false) {
+        CBigNum ret;
+        if(!BN_generate_prime_ex(&ret, numBits, (safe == true), NULL, NULL, NULL))
+            throw bignum_error("CBigNum::generatePrime*= :BN_generate_prime_ex");
+        return ret;
+    }
+
+    /**
+     * Calculates the greatest common divisor (GCD) of two numbers.
+     * @param m the second element
+     * @return the GCD
+     */
+    CBigNum gcd( const CBigNum& b) const{
+        CAutoBN_CTX pctx;
+        CBigNum ret;
+        if (!BN_gcd(&ret, this, &b, pctx))
+            throw bignum_error("CBigNum::gcd*= :BN_gcd");
+        return ret;
+    }
+
+    /**
+    * Miller-Rabin primality test on this element
+    * @param checks: optional, the number of Miller-Rabin tests to run
+    * default causes error rate of 2^-80.
+    * @return true if prime
+    */
+    bool isPrime(const int checks=BN_prime_checks) const {
+        CAutoBN_CTX pctx;
+        int ret = BN_is_prime(this, checks, NULL, pctx, NULL);
+        if(ret < 0){
+            throw bignum_error("CBigNum::isPrime :BN_is_prime");
+        }
+        return ret;
+    }
+
+    bool isOne() const {
+        return BN_is_one(this);
+    }
+
+
+    bool operator!() const
+    {
+        return BN_is_zero(this);
+    }
+
+    CBigNum& operator+=(const CBigNum& b)
+    {
+        if (!BN_add(this, this, &b))
+            throw bignum_error("CBigNum::operator+= : BN_add failed");
+        return *this;
+    }
+
+    CBigNum& operator-=(const CBigNum& b)
+    {
+        *this = *this - b;
+        return *this;
+    }
+
+    CBigNum& operator*=(const CBigNum& b)
+    {
+        CAutoBN_CTX pctx;
+        if (!BN_mul(this, this, &b, pctx))
+            throw bignum_error("CBigNum::operator*= : BN_mul failed");
+        return *this;
+    }
+
+    CBigNum& operator/=(const CBigNum& b)
+    {
+        *this = *this / b;
+        return *this;
+    }
+
+    CBigNum& operator%=(const CBigNum& b)
+    {
+        *this = *this % b;
+        return *this;
+    }
+
+    CBigNum& operator<<=(unsigned int shift)
+    {
+        if (!BN_lshift(this, this, shift))
+            throw bignum_error("CBigNum:operator<<= : BN_lshift failed");
+        return *this;
+    }
+
+    CBigNum& operator>>=(unsigned int shift)
+    {
+        // Note: BN_rshift segfaults on 64-bit if 2^shift is greater than the number
+        //   if built on ubuntu 9.04 or 9.10, probably depends on version of OpenSSL
+        CBigNum a = 1;
+        a <<= shift;
+        if (BN_cmp(&a, this) > 0)
+        {
+            *this = 0;
+            return *this;
+        }
+
+        if (!BN_rshift(this, this, shift))
+            throw bignum_error("CBigNum:operator>>= : BN_rshift failed");
+        return *this;
+    }
+
+
+    CBigNum& operator++()
+    {
+        // prefix operator
+        if (!BN_add(this, this, BN_value_one()))
+            throw bignum_error("CBigNum::operator++ : BN_add failed");
+        return *this;
+    }
+
+    const CBigNum operator++(int)
+    {
+        // postfix operator
+        const CBigNum ret = *this;
+        ++(*this);
+        return ret;
+    }
+
+    CBigNum& operator--()
+    {
+        // prefix operator
+        CBigNum r;
+        if (!BN_sub(&r, this, BN_value_one()))
+            throw bignum_error("CBigNum::operator-- : BN_sub failed");
+        *this = r;
+        return *this;
+    }
+
+    const CBigNum operator--(int)
+    {
+        // postfix operator
+        const CBigNum ret = *this;
+        --(*this);
+        return ret;
+    }
+
+
+    friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b);
+    friend inline const CBigNum operator/(const CBigNum& a, const CBigNum& b);
+    friend inline const CBigNum operator%(const CBigNum& a, const CBigNum& b);
+    friend inline const CBigNum operator*(const CBigNum& a, const CBigNum& b);
+    friend inline bool operator<(const CBigNum& a, const CBigNum& b);
+};
+
+
+
+inline const CBigNum operator+(const CBigNum& a, const CBigNum& b)
+{
+    CBigNum r;
+    if (!BN_add(&r, &a, &b))
+        throw bignum_error("CBigNum::operator+ : BN_add failed");
+    return r;
+}
+
+inline const CBigNum operator-(const CBigNum& a, const CBigNum& b)
+{
+    CBigNum r;
+    if (!BN_sub(&r, &a, &b))
+        throw bignum_error("CBigNum::operator- : BN_sub failed");
+    return r;
+}
+
+inline const CBigNum operator-(const CBigNum& a)
+{
+    CBigNum r(a);
+    BN_set_negative(&r, !BN_is_negative(&r));
+    return r;
+}
+
+inline const CBigNum operator*(const CBigNum& a, const CBigNum& b)
+{
+    CAutoBN_CTX pctx;
+    CBigNum r;
+    if (!BN_mul(&r, &a, &b, pctx))
+        throw bignum_error("CBigNum::operator* : BN_mul failed");
+    return r;
+}
+
+inline const CBigNum operator/(const CBigNum& a, const CBigNum& b)
+{
+    CAutoBN_CTX pctx;
+    CBigNum r;
+    if (!BN_div(&r, NULL, &a, &b, pctx))
+        throw bignum_error("CBigNum::operator/ : BN_div failed");
+    return r;
+}
+
+inline const CBigNum operator%(const CBigNum& a, const CBigNum& b)
+{
+    CAutoBN_CTX pctx;
+    CBigNum r;
+    if (!BN_nnmod(&r, &a, &b, pctx))
+        throw bignum_error("CBigNum::operator% : BN_div failed");
+    return r;
+}
+
+inline const CBigNum operator<<(const CBigNum& a, unsigned int shift)
+{
+    CBigNum r;
+    if (!BN_lshift(&r, &a, shift))
+        throw bignum_error("CBigNum:operator<< : BN_lshift failed");
+    return r;
+}
+
+inline const CBigNum operator>>(const CBigNum& a, unsigned int shift)
+{
+    CBigNum r = a;
+    r >>= shift;
+    return r;
+}
+
+inline bool operator==(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) == 0); }
+inline bool operator!=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) != 0); }
+inline bool operator<=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) <= 0); }
+inline bool operator>=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) >= 0); }
+inline bool operator<(const CBigNum& a, const CBigNum& b)  { return (BN_cmp(&a, &b) < 0); }
+inline bool operator>(const CBigNum& a, const CBigNum& b)  { return (BN_cmp(&a, &b) > 0); }
+
+inline std::ostream& operator<<(std::ostream &strm, const CBigNum &b) { return strm << b.ToString(10); }
+
+typedef  CBigNum Bignum;
+
+#endif

+ 406 - 0
ecoin/src/checkpoints.cpp

@@ -0,0 +1,406 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#include <boost/assign/list_of.hpp> // for 'map_list_of()'
+#include <boost/foreach.hpp>
+#include "checkpoints.h"
+#include "txdb.h"
+#include "main.h"
+#include "uint256.h"
+
+namespace Checkpoints
+{
+    typedef std::map<int, std::pair<uint256, unsigned int> > MapCheckpoints;
+
+    static MapCheckpoints mapCheckpoints =
+        boost::assign::map_list_of
+        ( 0, std::make_pair(hashGenesisBlock, 1610833657) )
+    ;
+
+    static MapCheckpoints mapCheckpointsTestnet =
+        boost::assign::map_list_of
+        ( 0, std::make_pair(hashGenesisBlockTestNet, 1610833657) )
+    ;
+
+    bool CheckHardened(int nHeight, const uint256& hash)
+    {
+        MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
+
+        MapCheckpoints::const_iterator i = checkpoints.find(nHeight);
+        if (i == checkpoints.end()) return true;
+        return hash == i->second.first;
+    }
+
+    int GetTotalBlocksEstimate()
+    {
+        MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
+
+        return checkpoints.rbegin()->first;
+    }
+
+    int GetLastCheckpointTime()
+    {
+        MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
+
+        return checkpoints.rbegin()->second.second;
+    }
+
+    CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex)
+    {
+        MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
+
+        BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints)
+        {
+            const uint256& hash = i.second.first;
+            std::map<uint256, CBlockIndex*>::const_iterator t = mapBlockIndex.find(hash);
+            if (t != mapBlockIndex.end())
+                return t->second;
+        }
+        return NULL;
+    }
+
+    // ecoin: synchronized checkpoint (centrally broadcasted)
+    uint256 hashSyncCheckpoint = 0;
+    uint256 hashPendingCheckpoint = 0;
+    CSyncCheckpoint checkpointMessage;
+    CSyncCheckpoint checkpointMessagePending;
+    uint256 hashInvalidCheckpoint = 0;
+    CCriticalSection cs_hashSyncCheckpoint;
+
+    // ecoin: get last synchronized checkpoint
+    CBlockIndex* GetLastSyncCheckpoint()
+    {
+        LOCK(cs_hashSyncCheckpoint);
+        if (!mapBlockIndex.count(hashSyncCheckpoint))
+            error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
+        else
+            return mapBlockIndex[hashSyncCheckpoint];
+        return NULL;
+    }
+
+    // ecoin: only descendant of current sync-checkpoint is allowed
+    bool ValidateSyncCheckpoint(uint256 hashCheckpoint)
+    {
+        if (!mapBlockIndex.count(hashSyncCheckpoint))
+            return error("ValidateSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
+        if (!mapBlockIndex.count(hashCheckpoint))
+            return error("ValidateSyncCheckpoint: block index missing for received sync-checkpoint %s", hashCheckpoint.ToString().c_str());
+
+        CBlockIndex* pindexSyncCheckpoint = mapBlockIndex[hashSyncCheckpoint];
+        CBlockIndex* pindexCheckpointRecv = mapBlockIndex[hashCheckpoint];
+
+        if (pindexCheckpointRecv->nHeight <= pindexSyncCheckpoint->nHeight)
+        {
+            CBlockIndex* pindex = pindexSyncCheckpoint;
+            while (pindex->nHeight > pindexCheckpointRecv->nHeight)
+                if (!(pindex = pindex->pprev))
+                    return error("ValidateSyncCheckpoint: pprev null - block index structure failure");
+            if (pindex->GetBlockHash() != hashCheckpoint)
+            {
+                hashInvalidCheckpoint = hashCheckpoint;
+                return error("ValidateSyncCheckpoint: new sync-checkpoint %s is conflicting with current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str());
+            }
+            return false; // ignore older checkpoint
+        }
+
+        CBlockIndex* pindex = pindexCheckpointRecv;
+        while (pindex->nHeight > pindexSyncCheckpoint->nHeight)
+            if (!(pindex = pindex->pprev))
+                return error("ValidateSyncCheckpoint: pprev2 null - block index structure failure");
+        if (pindex->GetBlockHash() != hashSyncCheckpoint)
+        {
+            hashInvalidCheckpoint = hashCheckpoint;
+            return error("ValidateSyncCheckpoint: new sync-checkpoint %s is not a descendant of current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str());
+        }
+        return true;
+    }
+
+    bool WriteSyncCheckpoint(const uint256& hashCheckpoint)
+    {
+        CTxDB txdb;
+        txdb.TxnBegin();
+        if (!txdb.WriteSyncCheckpoint(hashCheckpoint))
+        {
+            txdb.TxnAbort();
+            return error("WriteSyncCheckpoint(): failed to write to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
+        }
+        if (!txdb.TxnCommit())
+            return error("WriteSyncCheckpoint(): failed to commit to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
+
+        Checkpoints::hashSyncCheckpoint = hashCheckpoint;
+        return true;
+    }
+
+    bool AcceptPendingSyncCheckpoint()
+    {
+        LOCK(cs_hashSyncCheckpoint);
+        if (hashPendingCheckpoint != 0 && mapBlockIndex.count(hashPendingCheckpoint))
+        {
+            if (!ValidateSyncCheckpoint(hashPendingCheckpoint))
+            {
+                hashPendingCheckpoint = 0;
+                checkpointMessagePending.SetNull();
+                return false;
+            }
+
+            CTxDB txdb;
+            CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint];
+            if (!pindexCheckpoint->IsInMainChain())
+            {
+                CBlock block;
+                if (!block.ReadFromDisk(pindexCheckpoint))
+                    return error("AcceptPendingSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
+                if (!block.SetBestChain(txdb, pindexCheckpoint))
+                {
+                    hashInvalidCheckpoint = hashPendingCheckpoint;
+                    return error("AcceptPendingSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
+                }
+            }
+
+            if (!WriteSyncCheckpoint(hashPendingCheckpoint))
+                return error("AcceptPendingSyncCheckpoint(): failed to write sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
+            hashPendingCheckpoint = 0;
+            checkpointMessage = checkpointMessagePending;
+            checkpointMessagePending.SetNull();
+            printf("AcceptPendingSyncCheckpoint : sync-checkpoint at %s\n", hashSyncCheckpoint.ToString().c_str());
+            // relay the checkpoint
+            if (!checkpointMessage.IsNull())
+            {
+                BOOST_FOREACH(CNode* pnode, vNodes)
+                    checkpointMessage.RelayTo(pnode);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    // Automatically select a suitable sync-checkpoint
+    uint256 AutoSelectSyncCheckpoint()
+    {
+        const CBlockIndex *pindex = pindexBest;
+        // Search backward for a block within max span and maturity window
+        while (pindex->pprev && (pindex->GetBlockTime() + CHECKPOINT_MAX_SPAN > pindexBest->GetBlockTime() || pindex->nHeight + 8 > pindexBest->nHeight))
+            pindex = pindex->pprev;
+        return pindex->GetBlockHash();
+    }
+
+    // Check against synchronized checkpoint
+    bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev)
+    {
+        if (fTestNet) return true; // Testnet has no checkpoints
+        int nHeight = pindexPrev->nHeight + 1;
+
+        LOCK(cs_hashSyncCheckpoint);
+        // sync-checkpoint should always be accepted block
+        assert(mapBlockIndex.count(hashSyncCheckpoint));
+        const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
+
+        if (nHeight > pindexSync->nHeight)
+        {
+            // trace back to same height as sync-checkpoint
+            const CBlockIndex* pindex = pindexPrev;
+            while (pindex->nHeight > pindexSync->nHeight)
+                if (!(pindex = pindex->pprev))
+                    return error("CheckSync: pprev null - block index structure failure");
+            if (pindex->nHeight < pindexSync->nHeight || pindex->GetBlockHash() != hashSyncCheckpoint)
+                return false; // only descendant of sync-checkpoint can pass check
+        }
+        if (nHeight == pindexSync->nHeight && hashBlock != hashSyncCheckpoint)
+            return false; // same height with sync-checkpoint
+        if (nHeight < pindexSync->nHeight && !mapBlockIndex.count(hashBlock))
+            return false; // lower height than sync-checkpoint
+        return true;
+    }
+
+    bool WantedByPendingSyncCheckpoint(uint256 hashBlock)
+    {
+        LOCK(cs_hashSyncCheckpoint);
+        if (hashPendingCheckpoint == 0)
+            return false;
+        if (hashBlock == hashPendingCheckpoint)
+            return true;
+        if (mapOrphanBlocks.count(hashPendingCheckpoint)
+            && hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint]))
+            return true;
+        return false;
+    }
+
+    // ecoin: reset synchronized checkpoint to last hardened checkpoint
+    bool ResetSyncCheckpoint()
+    {
+        LOCK(cs_hashSyncCheckpoint);
+        const uint256& hash = mapCheckpoints.rbegin()->second.first;
+        if (mapBlockIndex.count(hash) && !mapBlockIndex[hash]->IsInMainChain())
+        {
+            // checkpoint block accepted but not yet in main chain
+            printf("ResetSyncCheckpoint: SetBestChain to hardened checkpoint %s\n", hash.ToString().c_str());
+            CTxDB txdb;
+            CBlock block;
+            if (!block.ReadFromDisk(mapBlockIndex[hash]))
+                return error("ResetSyncCheckpoint: ReadFromDisk failed for hardened checkpoint %s", hash.ToString().c_str());
+            if (!block.SetBestChain(txdb, mapBlockIndex[hash]))
+            {
+                return error("ResetSyncCheckpoint: SetBestChain failed for hardened checkpoint %s", hash.ToString().c_str());
+            }
+        }
+        else if(!mapBlockIndex.count(hash))
+        {
+            // checkpoint block not yet accepted
+            hashPendingCheckpoint = hash;
+            checkpointMessagePending.SetNull();
+            printf("ResetSyncCheckpoint: pending for sync-checkpoint %s\n", hashPendingCheckpoint.ToString().c_str());
+        }
+
+        BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints)
+        {
+            const uint256& hash = i.second.first;
+            if (mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain())
+            {
+                if (!WriteSyncCheckpoint(hash))
+                    return error("ResetSyncCheckpoint: failed to write sync checkpoint %s", hash.ToString().c_str());
+                printf("ResetSyncCheckpoint: sync-checkpoint reset to %s\n", hashSyncCheckpoint.ToString().c_str());
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    void AskForPendingSyncCheckpoint(CNode* pfrom)
+    {
+        LOCK(cs_hashSyncCheckpoint);
+        if (pfrom && hashPendingCheckpoint != 0 && (!mapBlockIndex.count(hashPendingCheckpoint)) && (!mapOrphanBlocks.count(hashPendingCheckpoint)))
+            pfrom->AskFor(CInv(MSG_BLOCK, hashPendingCheckpoint));
+    }
+
+    bool SetCheckpointPrivKey(std::string strPrivKey)
+    {
+        // Test signing a sync-checkpoint with genesis block
+        CSyncCheckpoint checkpoint;
+        checkpoint.hashCheckpoint = !fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet;
+        CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
+        sMsg << (CUnsignedSyncCheckpoint)checkpoint;
+        checkpoint.vchMsg = std::vector<unsigned char>(sMsg.begin(), sMsg.end());
+
+        std::vector<unsigned char> vchPrivKey = ParseHex(strPrivKey);
+        CKey key;
+        key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
+        if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
+            return false;
+
+        // Test signing successful, proceed
+        CSyncCheckpoint::strMasterPrivKey = strPrivKey;
+        return true;
+    }
+
+    bool SendSyncCheckpoint(uint256 hashCheckpoint)
+    {
+        CSyncCheckpoint checkpoint;
+        checkpoint.hashCheckpoint = hashCheckpoint;
+        CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
+        sMsg << (CUnsignedSyncCheckpoint)checkpoint;
+        checkpoint.vchMsg = std::vector<unsigned char>(sMsg.begin(), sMsg.end());
+
+        if (CSyncCheckpoint::strMasterPrivKey.empty())
+            return error("SendSyncCheckpoint: Checkpoint master key unavailable.");
+        std::vector<unsigned char> vchPrivKey = ParseHex(CSyncCheckpoint::strMasterPrivKey);
+        CKey key;
+        key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
+        if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
+            return error("SendSyncCheckpoint: Unable to sign checkpoint, check private key?");
+
+        if(!checkpoint.ProcessSyncCheckpoint(NULL))
+        {
+            printf("WARNING: SendSyncCheckpoint: Failed to process checkpoint.\n");
+            return false;
+        }
+
+        // Relay checkpoint
+        {
+            LOCK(cs_vNodes);
+            BOOST_FOREACH(CNode* pnode, vNodes)
+                checkpoint.RelayTo(pnode);
+        }
+        return true;
+    }
+
+    // Is the sync-checkpoint outside maturity window?
+    bool IsMatureSyncCheckpoint()
+    {
+        LOCK(cs_hashSyncCheckpoint);
+        // sync-checkpoint should always be accepted block
+        assert(mapBlockIndex.count(hashSyncCheckpoint));
+        const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
+        return (nBestHeight >= pindexSync->nHeight + nCoinbaseMaturity ||
+                pindexSync->GetBlockTime() + nStakeMinAge < GetAdjustedTime());
+    }
+}
+
+// ecoin: sync-checkpoint master key
+const std::string CSyncCheckpoint::strMasterPubKey = "045d0773e7224ee40c6e69755c0343022b8f125f218a5ec5f6e57b31c00e182e048807d7e66548148795c1dfa4c972b6792ad41efea33111c5fc0d6c86fc0383f1";
+
+std::string CSyncCheckpoint::strMasterPrivKey = "";
+
+// ecoin: verify signature of sync-checkpoint message
+bool CSyncCheckpoint::CheckSignature()
+{
+    CKey key;
+    if (!key.SetPubKey(ParseHex(CSyncCheckpoint::strMasterPubKey)))
+        return error("CSyncCheckpoint::CheckSignature() : SetPubKey failed");
+    if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
+        return error("CSyncCheckpoint::CheckSignature() : verify signature failed");
+
+    // Now unserialize the data
+    CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
+    sMsg >> *(CUnsignedSyncCheckpoint*)this;
+    return true;
+}
+
+// ecoin: process synchronized checkpoint
+bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom)
+{
+    if (!CheckSignature())
+        return false;
+
+    LOCK(Checkpoints::cs_hashSyncCheckpoint);
+    if (!mapBlockIndex.count(hashCheckpoint))
+    {
+        // We haven't received the checkpoint chain, keep the checkpoint as pending
+        Checkpoints::hashPendingCheckpoint = hashCheckpoint;
+        Checkpoints::checkpointMessagePending = *this;
+        printf("ProcessSyncCheckpoint: pending for sync-checkpoint %s\n", hashCheckpoint.ToString().c_str());
+        // Ask this guy to fill in what we're missing
+        if (pfrom)
+        {
+            pfrom->PushGetBlocks(pindexBest, hashCheckpoint);
+            // ask directly as well in case rejected earlier by duplicate
+            // proof-of-stake because getblocks may not get it this time
+            pfrom->AskFor(CInv(MSG_BLOCK, mapOrphanBlocks.count(hashCheckpoint)? WantedByOrphan(mapOrphanBlocks[hashCheckpoint]) : hashCheckpoint));
+        }
+        return false;
+    }
+
+    if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint))
+        return false;
+
+    CTxDB txdb;
+    CBlockIndex* pindexCheckpoint = mapBlockIndex[hashCheckpoint];
+    if (!pindexCheckpoint->IsInMainChain())
+    {
+        // checkpoint chain received but not yet main chain
+        CBlock block;
+        if (!block.ReadFromDisk(pindexCheckpoint))
+            return error("ProcessSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashCheckpoint.ToString().c_str());
+        if (!block.SetBestChain(txdb, pindexCheckpoint))
+        {
+            Checkpoints::hashInvalidCheckpoint = hashCheckpoint;
+            return error("ProcessSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashCheckpoint.ToString().c_str());
+        }
+    }
+
+    if (!Checkpoints::WriteSyncCheckpoint(hashCheckpoint))
+        return error("ProcessSyncCheckpoint(): failed to write sync checkpoint %s", hashCheckpoint.ToString().c_str());
+    Checkpoints::checkpointMessage = *this;
+    Checkpoints::hashPendingCheckpoint = 0;
+    Checkpoints::checkpointMessagePending.SetNull();
+    printf("ProcessSyncCheckpoint: sync-checkpoint at %s\n", hashCheckpoint.ToString().c_str());
+    return true;
+}

+ 152 - 0
ecoin/src/checkpoints.h

@@ -0,0 +1,152 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef ECOIN_CHECKPOINT_H
+#define  ECOIN_CHECKPOINT_H
+#include <map>
+#include "net.h"
+#include "util.h"
+
+#define CHECKPOINT_MAX_SPAN (60) // max 1 hour before latest block
+
+#ifdef WIN32
+#undef STRICT
+#undef PERMISSIVE
+#undef ADVISORY
+#endif
+
+class uint256;
+class CBlockIndex;
+class CSyncCheckpoint;
+
+namespace Checkpoints
+{
+    enum CPMode
+    {
+        // Scrict checkpoints policy, perform conflicts verification and resolve conflicts
+        STRICT = 0,
+        // Advisory checkpoints policy, perform conflicts verification but don't try to resolve them
+        ADVISORY = 1,
+        // Permissive checkpoints policy, don't perform any checking
+        PERMISSIVE = 2
+    };
+
+    // Returns true if block passes checkpoint checks
+    bool CheckHardened(int nHeight, const uint256& hash);
+
+    // Return conservative estimate of total number of blocks, 0 if unknown
+    int GetTotalBlocksEstimate();
+
+    // Returns last CBlockIndex* in mapBlockIndex that is a checkpoint
+    CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex);
+
+    // Returns last checkpoint timestamp
+    int GetLastCheckpointTime();
+
+    extern uint256 hashSyncCheckpoint;
+    extern CSyncCheckpoint checkpointMessage;
+    extern uint256 hashInvalidCheckpoint;
+    extern CCriticalSection cs_hashSyncCheckpoint;
+
+    CBlockIndex* GetLastSyncCheckpoint();
+    bool WriteSyncCheckpoint(const uint256& hashCheckpoint);
+    bool AcceptPendingSyncCheckpoint();
+    uint256 AutoSelectSyncCheckpoint();
+    bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev);
+    bool WantedByPendingSyncCheckpoint(uint256 hashBlock);
+    bool ResetSyncCheckpoint();
+    void AskForPendingSyncCheckpoint(CNode* pfrom);
+    bool SetCheckpointPrivKey(std::string strPrivKey);
+    bool SendSyncCheckpoint(uint256 hashCheckpoint);
+    bool IsMatureSyncCheckpoint();
+}
+
+// ecoin: synchronized checkpoint
+class CUnsignedSyncCheckpoint
+{
+public:
+    int nVersion;
+    uint256 hashCheckpoint;      // checkpoint block
+
+    IMPLEMENT_SERIALIZE
+    (
+        READWRITE(this->nVersion);
+        nVersion = this->nVersion;
+        READWRITE(hashCheckpoint);
+    )
+
+    void SetNull()
+    {
+        nVersion = 1;
+        hashCheckpoint = 0;
+    }
+
+    std::string ToString() const
+    {
+        return strprintf(
+                "CSyncCheckpoint(\n"
+                "    nVersion       = %d\n"
+                "    hashCheckpoint = %s\n"
+                ")\n",
+            nVersion,
+            hashCheckpoint.ToString().c_str());
+    }
+
+    void print() const
+    {
+        printf("%s", ToString().c_str());
+    }
+};
+
+class CSyncCheckpoint : public CUnsignedSyncCheckpoint
+{
+public:
+    static const std::string strMasterPubKey;
+    static std::string strMasterPrivKey;
+
+    std::vector<unsigned char> vchMsg;
+    std::vector<unsigned char> vchSig;
+
+    CSyncCheckpoint()
+    {
+        SetNull();
+    }
+
+    IMPLEMENT_SERIALIZE
+    (
+        READWRITE(vchMsg);
+        READWRITE(vchSig);
+    )
+
+    void SetNull()
+    {
+        CUnsignedSyncCheckpoint::SetNull();
+        vchMsg.clear();
+        vchSig.clear();
+    }
+
+    bool IsNull() const
+    {
+        return (hashCheckpoint == 0);
+    }
+
+    uint256 GetHash() const
+    {
+        return SerializeHash(*this);
+    }
+
+    bool RelayTo(CNode* pnode) const
+    {
+        // returns true if wasn't already sent
+        if (pnode->hashCheckpointKnown != hashCheckpoint)
+        {
+            pnode->hashCheckpointKnown = hashCheckpoint;
+            pnode->PushMessage("checkpoint", *this);
+            return true;
+        }
+        return false;
+    }
+
+    bool CheckSignature();
+    bool ProcessSyncCheckpoint(CNode* pfrom);
+};
+
+#endif

+ 10 - 0
ecoin/src/clientversion.h

@@ -0,0 +1,10 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef CLIENTVERSION_H
+#define CLIENTVERSION_H
+#define CLIENT_VERSION_MAJOR       0
+#define CLIENT_VERSION_MINOR       7
+#define CLIENT_VERSION_REVISION    5
+#define CLIENT_VERSION_BUILD       7
+#define STRINGIZE(X) DO_STRINGIZE(X)
+#define DO_STRINGIZE(X) #X
+#endif // CLIENTVERSION_H

+ 57 - 0
ecoin/src/coincontrol.h

@@ -0,0 +1,57 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef COINCONTROL_H
+#define COINCONTROL_H
+
+class CCoinControl
+{
+public:
+    CTxDestination destChange;
+
+    CCoinControl()
+    {
+        SetNull();
+    }
+        
+    void SetNull()
+    {
+        destChange = CNoDestination();
+        setSelected.clear();
+    }
+    
+    bool HasSelected() const
+    {
+        return (setSelected.size() > 0);
+    }
+    
+    bool IsSelected(const uint256& hash, unsigned int n) const
+    {
+        COutPoint outpt(hash, n);
+        return (setSelected.count(outpt) > 0);
+    }
+    
+    void Select(COutPoint& output)
+    {
+        setSelected.insert(output);
+    }
+    
+    void UnSelect(COutPoint& output)
+    {
+        setSelected.erase(output);
+    }
+    
+    void UnSelectAll()
+    {
+        setSelected.clear();
+    }
+
+    void ListSelected(std::vector<COutPoint>& vOutpoints)
+    {
+        vOutpoints.assign(setSelected.begin(), setSelected.end());
+    }
+        
+private:
+    std::set<COutPoint> setSelected;
+
+};
+
+#endif // COINCONTROL_H

+ 60 - 0
ecoin/src/compat.h

@@ -0,0 +1,60 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef _ECOIN_COMPAT_H
+#define _ECOIN_COMPAT_H 1
+
+#ifdef WIN32
+#define _WIN32_WINNT 0x0501
+#define WIN32_LEAN_AND_MEAN 1
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <winsock2.h>
+#include <mswsock.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <ifaddrs.h>
+
+typedef u_int SOCKET;
+#endif
+
+#ifdef WIN32
+#define MSG_NOSIGNAL        0
+#define MSG_DONTWAIT        0
+typedef int socklen_t;
+#else
+#include "errno.h"
+#define WSAGetLastError()   errno
+#define WSAEINVAL           EINVAL
+#define WSAEALREADY         EALREADY
+#define WSAEWOULDBLOCK      EWOULDBLOCK
+#define WSAEMSGSIZE         EMSGSIZE
+#define WSAEINTR            EINTR
+#define WSAEINPROGRESS      EINPROGRESS
+#define WSAEADDRINUSE       EADDRINUSE
+#define WSAENOTSOCK         EBADF
+#define INVALID_SOCKET      (SOCKET)(~0)
+#define SOCKET_ERROR        -1
+#endif
+
+inline int myclosesocket(SOCKET& hSocket)
+{
+    if (hSocket == INVALID_SOCKET)
+        return WSAENOTSOCK;
+#ifdef WIN32
+    int ret = closesocket(hSocket);
+#else
+    int ret = close(hSocket);
+#endif
+    hSocket = INVALID_SOCKET;
+    return ret;
+}
+#define closesocket(s)      myclosesocket(s)
+
+#endif

+ 127 - 0
ecoin/src/crypter.cpp

@@ -0,0 +1,127 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <vector>
+#include <string>
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#include "crypter.h"
+#include "scrypt.h"
+
+bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod)
+{
+    if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE)
+        return false;
+
+    int i = 0;
+    if (nDerivationMethod == 0)
+    {
+        i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0],
+                          (unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV);
+    }
+
+    if (nDerivationMethod == 1)
+    {
+        uint256 scryptHash = scrypt_salted_multiround_hash((const void*)strKeyData.c_str(), strKeyData.size(), &chSalt[0], 8, nRounds);
+
+        i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0],
+                          (unsigned char *)&scryptHash, sizeof scryptHash, nRounds, chKey, chIV);
+        OPENSSL_cleanse(&scryptHash, sizeof scryptHash);
+    }
+
+
+    if (i != (int)WALLET_CRYPTO_KEY_SIZE)
+    {
+        OPENSSL_cleanse(&chKey, sizeof chKey);
+        OPENSSL_cleanse(&chIV, sizeof chIV);
+        return false;
+    }
+
+    fKeySet = true;
+    return true;
+}
+
+bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV)
+{
+    if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_KEY_SIZE)
+        return false;
+
+    memcpy(&chKey[0], &chNewKey[0], sizeof chKey);
+    memcpy(&chIV[0], &chNewIV[0], sizeof chIV);
+
+    fKeySet = true;
+    return true;
+}
+
+bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext)
+{
+    if (!fKeySet)
+        return false;
+    int nLen = vchPlaintext.size();
+    int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0;
+    vchCiphertext = std::vector<unsigned char> (nCLen);
+
+    EVP_CIPHER_CTX ctx;
+
+    bool fOk = true;
+
+    EVP_CIPHER_CTX_init(&ctx);
+    if (fOk) fOk = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV);
+    if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen);
+    if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0])+nCLen, &nFLen);
+    EVP_CIPHER_CTX_cleanup(&ctx);
+
+    if (!fOk) return false;
+
+    vchCiphertext.resize(nCLen + nFLen);
+    return true;
+}
+
+bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext)
+{
+    if (!fKeySet)
+        return false;
+
+    int nLen = vchCiphertext.size();
+    int nPLen = nLen, nFLen = 0;
+
+    vchPlaintext = CKeyingMaterial(nPLen);
+
+    EVP_CIPHER_CTX ctx;
+
+    bool fOk = true;
+
+    EVP_CIPHER_CTX_init(&ctx);
+    if (fOk) fOk = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV);
+    if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen);
+    if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0])+nPLen, &nFLen);
+    EVP_CIPHER_CTX_cleanup(&ctx);
+
+    if (!fOk) return false;
+
+    vchPlaintext.resize(nPLen + nFLen);
+    return true;
+}
+
+
+bool EncryptSecret(CKeyingMaterial& vMasterKey, const CSecret &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext)
+{
+    CCrypter cKeyCrypter;
+    std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE);
+    memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE);
+    if(!cKeyCrypter.SetKey(vMasterKey, chIV))
+        return false;
+    return cKeyCrypter.Encrypt((CKeyingMaterial)vchPlaintext, vchCiphertext);
+}
+
+bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CSecret& vchPlaintext)
+{
+    CCrypter cKeyCrypter;
+    std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE);
+    memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE);
+    if(!cKeyCrypter.SetKey(vMasterKey, chIV))
+        return false;
+    return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext));
+}

+ 101 - 0
ecoin/src/crypter.h

@@ -0,0 +1,101 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef __CRYPTER_H__
+#define __CRYPTER_H__
+
+#include "allocators.h" /* for SecureString */
+#include "key.h"
+#include "serialize.h"
+
+const unsigned int WALLET_CRYPTO_KEY_SIZE = 32;
+const unsigned int WALLET_CRYPTO_SALT_SIZE = 8;
+
+class CMasterKey
+{
+public:
+    std::vector<unsigned char> vchCryptedKey;
+    std::vector<unsigned char> vchSalt;
+    // 0 = EVP_sha512()
+    // 1 = scrypt()
+    unsigned int nDerivationMethod;
+    unsigned int nDeriveIterations;
+    std::vector<unsigned char> vchOtherDerivationParameters;
+
+    IMPLEMENT_SERIALIZE
+    (
+        READWRITE(vchCryptedKey);
+        READWRITE(vchSalt);
+        READWRITE(nDerivationMethod);
+        READWRITE(nDeriveIterations);
+        READWRITE(vchOtherDerivationParameters);
+    )
+    CMasterKey()
+    {
+        // 25000 rounds is just under 0.1 seconds on a 1.86 GHz Pentium M
+        // ie slightly lower than the lowest hardware we need bother supporting
+        nDeriveIterations = 25000;
+        nDerivationMethod = 1;
+        vchOtherDerivationParameters = std::vector<unsigned char>(0);
+    }
+
+    CMasterKey(unsigned int nDerivationMethodIndex)
+    {
+        switch (nDerivationMethodIndex)
+        {
+            case 0: // sha512
+            default:
+                nDeriveIterations = 25000;
+                nDerivationMethod = 0;
+                vchOtherDerivationParameters = std::vector<unsigned char>(0);
+            break;
+
+            case 1: // scrypt+sha512
+                nDeriveIterations = 10000;
+                nDerivationMethod = 1;
+                vchOtherDerivationParameters = std::vector<unsigned char>(0);
+            break;
+        }
+    }
+
+};
+
+typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
+
+class CCrypter
+{
+private:
+    unsigned char chKey[WALLET_CRYPTO_KEY_SIZE];
+    unsigned char chIV[WALLET_CRYPTO_KEY_SIZE];
+    bool fKeySet;
+
+public:
+    bool SetKeyFromPassphrase(const SecureString &strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod);
+    bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext);
+    bool Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext);
+    bool SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV);
+
+    void CleanKey()
+    {
+        OPENSSL_cleanse(&chKey, sizeof chKey);
+        OPENSSL_cleanse(&chIV, sizeof chIV);
+        fKeySet = false;
+    }
+
+    CCrypter()
+    {
+        fKeySet = false;
+        LockedPageManager::instance.LockRange(&chKey[0], sizeof chKey);
+        LockedPageManager::instance.LockRange(&chIV[0], sizeof chIV);
+    }
+
+    ~CCrypter()
+    {
+        CleanKey();
+        LockedPageManager::instance.UnlockRange(&chKey[0], sizeof chKey);
+        LockedPageManager::instance.UnlockRange(&chIV[0], sizeof chIV);
+    }
+};
+
+bool EncryptSecret(CKeyingMaterial& vMasterKey, const CSecret &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext);
+bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char> &vchCiphertext, const uint256& nIV, CSecret &vchPlaintext);
+
+#endif

+ 551 - 0
ecoin/src/db.cpp

@@ -0,0 +1,551 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+#include "db.h"
+#include "net.h"
+#include "util.h"
+#include "main.h"
+#include "ui_interface.h"
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
+
+#ifndef WIN32
+#include "sys/stat.h"
+#endif
+
+using namespace std;
+using namespace boost;
+
+unsigned int nWalletDBUpdated;
+
+CDBEnv bitdb;
+
+void CDBEnv::EnvShutdown()
+{
+    if (!fDbEnvInit)
+        return;
+
+    fDbEnvInit = false;
+    int ret = dbenv.close(0);
+    if (ret != 0)
+        printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret);
+    if (!fMockDb)
+        DbEnv(0).remove(strPath.c_str(), 0);
+}
+
+CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS)
+{
+    fDbEnvInit = false;
+    fMockDb = false;
+}
+
+CDBEnv::~CDBEnv()
+{
+    EnvShutdown();
+}
+
+void CDBEnv::Close()
+{
+    EnvShutdown();
+}
+
+bool CDBEnv::Open(boost::filesystem::path pathEnv_)
+{
+    if (fDbEnvInit)
+        return true;
+
+    if (fShutdown)
+        return false;
+
+    pathEnv = pathEnv_;
+    filesystem::path pathDataDir = pathEnv;
+    strPath = pathDataDir.string();
+    filesystem::path pathLogDir = pathDataDir / "database";
+    filesystem::create_directory(pathLogDir);
+    filesystem::path pathErrorFile = pathDataDir / "db.log";
+    printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str());
+
+    unsigned int nEnvFlags = 0;
+    if (GetBoolArg("-privdb", true))
+        nEnvFlags |= DB_PRIVATE;
+
+    int nDbCache = GetArg("-dbcache", 25);
+    dbenv.set_lg_dir(pathLogDir.string().c_str());
+    dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1);
+    dbenv.set_lg_bsize(1048576);
+    dbenv.set_lg_max(10485760);
+
+    dbenv.set_lk_max_locks(537000);
+
+    dbenv.set_lk_max_objects(10000);
+    dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug
+    dbenv.set_flags(DB_AUTO_COMMIT, 1);
+    dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1);
+#ifdef DB_LOG_AUTO_REMOVE
+    dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1);
+#endif
+    int ret = dbenv.open(strPath.c_str(),
+                     DB_CREATE     |
+                     DB_INIT_LOCK  |
+                     DB_INIT_LOG   |
+                     DB_INIT_MPOOL |
+                     DB_INIT_TXN   |
+                     DB_THREAD     |
+                     DB_RECOVER    |
+                     nEnvFlags,
+                     S_IRUSR | S_IWUSR);
+    if (ret != 0)
+        return error("CDB() : error %s (%d) opening database environment", DbEnv::strerror(ret), ret);
+
+    fDbEnvInit = true;
+    fMockDb = false;
+
+    return true;
+}
+
+void CDBEnv::MakeMock()
+{
+    if (fDbEnvInit)
+        throw runtime_error("CDBEnv::MakeMock(): already initialized");
+
+    if (fShutdown)
+        throw runtime_error("CDBEnv::MakeMock(): during shutdown");
+
+    printf("CDBEnv::MakeMock()\n");
+
+    dbenv.set_cachesize(1, 0, 1);
+    dbenv.set_lg_bsize(10485760*4);
+    dbenv.set_lg_max(10485760);
+    dbenv.set_lk_max_locks(10000);
+    dbenv.set_lk_max_objects(10000);
+    dbenv.set_flags(DB_AUTO_COMMIT, 1);
+#ifdef DB_LOG_IN_MEMORY
+    dbenv.log_set_config(DB_LOG_IN_MEMORY, 1);
+#endif
+    int ret = dbenv.open(NULL,
+                     DB_CREATE     |
+                     DB_INIT_LOCK  |
+                     DB_INIT_LOG   |
+                     DB_INIT_MPOOL |
+                     DB_INIT_TXN   |
+                     DB_THREAD     |
+                     DB_PRIVATE,
+                     S_IRUSR | S_IWUSR);
+    if (ret > 0)
+        throw runtime_error(strprintf("CDBEnv::MakeMock(): error %d opening database environment", ret));
+
+    fDbEnvInit = true;
+    fMockDb = true;
+}
+
+CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile))
+{
+    LOCK(cs_db);
+    assert(mapFileUseCount.count(strFile) == 0);
+
+    Db db(&dbenv, 0);
+    int result = db.verify(strFile.c_str(), NULL, NULL, 0);
+    if (result == 0)
+        return VERIFY_OK;
+    else if (recoverFunc == NULL)
+        return RECOVER_FAIL;
+
+    // Try to recover:
+    bool fRecovered = (*recoverFunc)(*this, strFile);
+    return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
+}
+
+bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
+                     std::vector<CDBEnv::KeyValPair >& vResult)
+{
+    LOCK(cs_db);
+    assert(mapFileUseCount.count(strFile) == 0);
+
+    u_int32_t flags = DB_SALVAGE;
+    if (fAggressive) flags |= DB_AGGRESSIVE;
+
+    stringstream strDump;
+
+    Db db(&dbenv, 0);
+    int result = db.verify(strFile.c_str(), NULL, &strDump, flags);
+    if (result != 0)
+    {
+        printf("ERROR: db salvage failed\n");
+        return false;
+    }
+
+    string strLine;
+    while (!strDump.eof() && strLine != "HEADER=END")
+        getline(strDump, strLine); // Skip past header
+
+    std::string keyHex, valueHex;
+    while (!strDump.eof() && keyHex != "DATA=END")
+    {
+        getline(strDump, keyHex);
+        if (keyHex != "DATA_END")
+        {
+            getline(strDump, valueHex);
+            vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex)));
+        }
+    }
+
+    return (result == 0);
+}
+
+void CDBEnv::CheckpointLSN(std::string strFile)
+{
+    dbenv.txn_checkpoint(0, 0, 0);
+    if (fMockDb)
+        return;
+    dbenv.lsn_reset(strFile.c_str(), 0);
+}
+
+
+CDB::CDB(const char *pszFile, const char* pszMode) :
+    pdb(NULL), activeTxn(NULL)
+{
+    int ret;
+    if (pszFile == NULL)
+        return;
+
+    fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
+    bool fCreate = strchr(pszMode, 'c');
+    unsigned int nFlags = DB_THREAD;
+    if (fCreate)
+        nFlags |= DB_CREATE;
+
+    {
+        LOCK(bitdb.cs_db);
+        if (!bitdb.Open(GetDataDir()))
+            throw runtime_error("env open failed");
+
+        strFile = pszFile;
+        ++bitdb.mapFileUseCount[strFile];
+        pdb = bitdb.mapDb[strFile];
+        if (pdb == NULL)
+        {
+            pdb = new Db(&bitdb.dbenv, 0);
+
+            bool fMockDb = bitdb.IsMock();
+            if (fMockDb)
+            {
+                DbMpoolFile*mpf = pdb->get_mpf();
+                ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
+                if (ret != 0)
+                    throw runtime_error(strprintf("CDB() : failed to configure for no temp file backing for database %s", pszFile));
+            }
+
+            ret = pdb->open(NULL,      // Txn pointer
+                            fMockDb ? NULL : pszFile,   // Filename
+                            "main",    // Logical db name
+                            DB_BTREE,  // Database type
+                            nFlags,    // Flags
+                            0);
+
+            if (ret != 0)
+            {
+                delete pdb;
+                pdb = NULL;
+                --bitdb.mapFileUseCount[strFile];
+                strFile = "";
+                throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
+            }
+
+            if (fCreate && !Exists(string("version")))
+            {
+                bool fTmp = fReadOnly;
+                fReadOnly = false;
+                WriteVersion(CLIENT_VERSION);
+                fReadOnly = fTmp;
+            }
+
+            bitdb.mapDb[strFile] = pdb;
+        }
+    }
+}
+
+static bool IsChainFile(std::string strFile)
+{
+    if (strFile == "blkindex.dat")
+        return true;
+
+    return false;
+}
+
+void CDB::Close()
+{
+    if (!pdb)
+        return;
+    if (activeTxn)
+        activeTxn->abort();
+    activeTxn = NULL;
+    pdb = NULL;
+
+    unsigned int nMinutes = 0;
+    if (fReadOnly)
+        nMinutes = 1;
+    if (IsChainFile(strFile))
+        nMinutes = 2;
+    if (IsChainFile(strFile) && IsInitialBlockDownload())
+        nMinutes = 5;
+
+    bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0);
+
+    {
+        LOCK(bitdb.cs_db);
+        --bitdb.mapFileUseCount[strFile];
+    }
+}
+
+void CDBEnv::CloseDb(const string& strFile)
+{
+    {
+        LOCK(cs_db);
+        if (mapDb[strFile] != NULL)
+        {
+            Db* pdb = mapDb[strFile];
+            pdb->close(0);
+            delete pdb;
+            mapDb[strFile] = NULL;
+        }
+    }
+}
+
+bool CDBEnv::RemoveDb(const string& strFile)
+{
+    this->CloseDb(strFile);
+
+    LOCK(cs_db);
+    int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
+    return (rc == 0);
+}
+
+bool CDB::Rewrite(const string& strFile, const char* pszSkip)
+{
+    while (!fShutdown)
+    {
+        {
+            LOCK(bitdb.cs_db);
+            if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0)
+            {
+                bitdb.CloseDb(strFile);
+                bitdb.CheckpointLSN(strFile);
+                bitdb.mapFileUseCount.erase(strFile);
+
+                bool fSuccess = true;
+                printf("Rewriting %s...\n", strFile.c_str());
+                string strFileRes = strFile + ".rewrite";
+                { // surround usage of db with extra {}
+                    CDB db(strFile.c_str(), "r");
+                    Db* pdbCopy = new Db(&bitdb.dbenv, 0);
+
+                    int ret = pdbCopy->open(NULL,                 // Txn pointer
+                                            strFileRes.c_str(),   // Filename
+                                            "main",    // Logical db name
+                                            DB_BTREE,  // Database type
+                                            DB_CREATE,    // Flags
+                                            0);
+                    if (ret > 0)
+                    {
+                        printf("Cannot create database file %s\n", strFileRes.c_str());
+                        fSuccess = false;
+                    }
+
+                    Dbc* pcursor = db.GetCursor();
+                    if (pcursor)
+                        while (fSuccess)
+                        {
+                            CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+                            CDataStream ssValue(SER_DISK, CLIENT_VERSION);
+                            int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
+                            if (ret == DB_NOTFOUND)
+                            {
+                                pcursor->close();
+                                break;
+                            }
+                            else if (ret != 0)
+                            {
+                                pcursor->close();
+                                fSuccess = false;
+                                break;
+                            }
+                            if (pszSkip &&
+                                strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
+                                continue;
+                            if (strncmp(&ssKey[0], "\x07version", 8) == 0)
+                            {
+                                // Update version:
+                                ssValue.clear();
+                                ssValue << CLIENT_VERSION;
+                            }
+                            Dbt datKey(&ssKey[0], ssKey.size());
+                            Dbt datValue(&ssValue[0], ssValue.size());
+                            int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
+                            if (ret2 > 0)
+                                fSuccess = false;
+                        }
+                    if (fSuccess)
+                    {
+                        db.Close();
+                        bitdb.CloseDb(strFile);
+                        if (pdbCopy->close(0))
+                            fSuccess = false;
+                        delete pdbCopy;
+                    }
+                }
+                if (fSuccess)
+                {
+                    Db dbA(&bitdb.dbenv, 0);
+                    if (dbA.remove(strFile.c_str(), NULL, 0))
+                        fSuccess = false;
+                    Db dbB(&bitdb.dbenv, 0);
+                    if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
+                        fSuccess = false;
+                }
+                if (!fSuccess)
+                    printf("Rewriting of %s FAILED!\n", strFileRes.c_str());
+                return fSuccess;
+            }
+        }
+        Sleep(100);
+    }
+    return false;
+}
+
+void CDBEnv::Flush(bool fShutdown)
+{
+    int64 nStart = GetTimeMillis();
+    printf("Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");
+    if (!fDbEnvInit)
+        return;
+    {
+        LOCK(cs_db);
+        map<string, int>::iterator mi = mapFileUseCount.begin();
+        while (mi != mapFileUseCount.end())
+        {
+            string strFile = (*mi).first;
+            int nRefCount = (*mi).second;
+            printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
+            if (nRefCount == 0)
+            {
+                CloseDb(strFile);
+                printf("%s checkpoint\n", strFile.c_str());
+                dbenv.txn_checkpoint(0, 0, 0);
+                if (!IsChainFile(strFile) || fDetachDB) {
+                    printf("%s detach\n", strFile.c_str());
+                    if (!fMockDb)
+                        dbenv.lsn_reset(strFile.c_str(), 0);
+                }
+                printf("%s closed\n", strFile.c_str());
+                mapFileUseCount.erase(mi++);
+            }
+            else
+                mi++;
+        }
+        printf("DBFlush(%s)%s ended %15" PRI64d"ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart);
+        if (fShutdown)
+        {
+            char** listp;
+            if (mapFileUseCount.empty())
+            {
+                dbenv.log_archive(&listp, DB_ARCH_REMOVE);
+                Close();
+            }
+        }
+    }
+}
+
+CAddrDB::CAddrDB()
+{
+    pathAddr = GetDataDir() / "peers.dat";
+}
+
+bool CAddrDB::Write(const CAddrMan& addr)
+{
+    // generate random temporary filename
+    unsigned short randv = 0;
+    RAND_bytes((unsigned char *)&randv, sizeof(randv));
+    std::string tmpfn = strprintf("peers.dat.%04x", randv);
+
+    // serialize addresses, checksum data up to that point, then append csum
+    CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
+    ssPeers << FLATDATA(pchMessageStart);
+    ssPeers << addr;
+    uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
+    ssPeers << hash;
+
+    // open temp output file, and associate with CAutoFile
+    boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
+    FILE *file = fopen(pathTmp.string().c_str(), "wb");
+    CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION);
+    if (!fileout)
+        return error("CAddrman::Write() : open failed");
+
+    // write and commit header, data
+    try {
+        fileout << ssPeers;
+    }
+    catch (std::exception &e) {
+        return error("CAddrman::Write() : I/O error");
+    }
+    FileCommit(fileout);
+    fileout.fclose();
+
+    // replace existing peers.dat, if any, with new peers.dat.XXXX
+    if (!RenameOver(pathTmp, pathAddr))
+        return error("CAddrman::Write() : Rename-into-place failed");
+
+    return true;
+}
+
+bool CAddrDB::Read(CAddrMan& addr)
+{
+    // open input file, and associate with CAutoFile
+    FILE *file = fopen(pathAddr.string().c_str(), "rb");
+    CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION);
+    if (!filein)
+        return error("CAddrman::Read() : open failed");
+
+    // use file size to size memory buffer
+    int fileSize = GetFilesize(filein);
+    int dataSize = fileSize - sizeof(uint256);
+    if ( dataSize < 0 ) dataSize = 0;
+    vector<unsigned char> vchData;
+    vchData.resize(dataSize);
+    uint256 hashIn;
+
+    // read data and checksum from file
+    try {
+        filein.read((char *)&vchData[0], dataSize);
+        filein >> hashIn;
+    }
+    catch (std::exception &e) {
+        return error("CAddrman::Read() 2 : I/O error or stream data corrupted");
+    }
+    filein.fclose();
+
+    CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION);
+
+    // verify stored checksum matches input data
+    uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end());
+    if (hashIn != hashTmp)
+        return error("CAddrman::Read() : checksum mismatch; data corrupted");
+
+    unsigned char pchMsgTmp[4];
+    try {
+        // de-serialize file header (pchMessageStart magic number) and
+        ssPeers >> FLATDATA(pchMsgTmp);
+
+        // verify the network matches ours
+        if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp)))
+            return error("CAddrman::Read() : invalid network magic number");
+
+        // de-serialize address data into one CAddrMan object
+        ssPeers >> addr;
+    }
+    catch (std::exception &e) {
+        return error("CAddrman::Read() : I/O error or stream data corrupted");
+    }
+
+    return true;
+}
+

+ 283 - 0
ecoin/src/db.h

@@ -0,0 +1,283 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef ECOIN_DB_H
+#define ECOIN_DB_H
+
+#include "main.h"
+#include <map>
+#include <string>
+#include <vector>
+#include <db_cxx.h>
+
+class CAddress;
+class CAddrMan;
+class CBlockLocator;
+class CDiskBlockIndex;
+class CDiskTxPos;
+class CMasterKey;
+class COutPoint;
+class CTxIndex;
+class CWallet;
+class CWalletTx;
+
+extern unsigned int nWalletDBUpdated;
+
+void ThreadFlushWalletDB(void* parg);
+bool BackupWallet(const CWallet& wallet, const std::string& strDest);
+
+class CDBEnv
+{
+private:
+    bool fDetachDB;
+    bool fDbEnvInit;
+    bool fMockDb;
+    boost::filesystem::path pathEnv;
+    std::string strPath;
+    void EnvShutdown();
+
+public:
+    mutable CCriticalSection cs_db;
+    DbEnv dbenv;
+    std::map<std::string, int> mapFileUseCount;
+    std::map<std::string, Db*> mapDb;
+    CDBEnv();
+    ~CDBEnv();
+    void MakeMock();
+    bool IsMock() { return fMockDb; };
+
+    enum VerifyResult { VERIFY_OK, RECOVER_OK, RECOVER_FAIL };
+    VerifyResult Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile));
+    typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
+    bool Salvage(std::string strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
+
+    bool Open(boost::filesystem::path pathEnv_);
+    void Close();
+    void Flush(bool fShutdown);
+    void CheckpointLSN(std::string strFile);
+    void SetDetach(bool fDetachDB_) { fDetachDB = fDetachDB_; }
+    bool GetDetach() { return fDetachDB; }
+
+    void CloseDb(const std::string& strFile);
+    bool RemoveDb(const std::string& strFile);
+
+    DbTxn *TxnBegin(int flags=DB_TXN_WRITE_NOSYNC)
+    {
+        DbTxn* ptxn = NULL;
+        int ret = dbenv.txn_begin(NULL, &ptxn, flags);
+        if (!ptxn || ret != 0)
+            return NULL;
+        return ptxn;
+    }
+};
+
+extern CDBEnv bitdb;
+
+class CDB
+{
+protected:
+    Db* pdb;
+    std::string strFile;
+    DbTxn *activeTxn;
+    bool fReadOnly;
+    explicit CDB(const char* pszFile, const char* pszMode="r+");
+    ~CDB() { Close(); }
+public:
+    void Close();
+private:
+    CDB(const CDB&);
+    void operator=(const CDB&);
+
+protected:
+    template<typename K, typename T>
+    bool Read(const K& key, T& value)
+    {
+        if (!pdb)
+            return false;
+
+        CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+        ssKey.reserve(1000);
+        ssKey << key;
+        Dbt datKey(&ssKey[0], ssKey.size());
+
+        Dbt datValue;
+        datValue.set_flags(DB_DBT_MALLOC);
+        int ret = pdb->get(activeTxn, &datKey, &datValue, 0);
+        memset(datKey.get_data(), 0, datKey.get_size());
+        if (datValue.get_data() == NULL)
+            return false;
+
+        try {
+            CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
+            ssValue >> value;
+        }
+        catch (std::exception &e) {
+            return false;
+        }
+
+        memset(datValue.get_data(), 0, datValue.get_size());
+        free(datValue.get_data());
+        return (ret == 0);
+    }
+
+    template<typename K, typename T>
+    bool Write(const K& key, const T& value, bool fOverwrite=true)
+    {
+        if (!pdb)
+            return false;
+        if (fReadOnly)
+            assert(!"Write called on database in read-only mode");
+
+        CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+        ssKey.reserve(1000);
+        ssKey << key;
+        Dbt datKey(&ssKey[0], ssKey.size());
+
+        CDataStream ssValue(SER_DISK, CLIENT_VERSION);
+        ssValue.reserve(10000);
+        ssValue << value;
+        Dbt datValue(&ssValue[0], ssValue.size());
+
+        int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
+
+        memset(datKey.get_data(), 0, datKey.get_size());
+        memset(datValue.get_data(), 0, datValue.get_size());
+        return (ret == 0);
+    }
+
+    template<typename K>
+    bool Erase(const K& key)
+    {
+        if (!pdb)
+            return false;
+        if (fReadOnly)
+            assert(!"Erase called on database in read-only mode");
+
+        CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+        ssKey.reserve(1000);
+        ssKey << key;
+        Dbt datKey(&ssKey[0], ssKey.size());
+
+        int ret = pdb->del(activeTxn, &datKey, 0);
+
+        memset(datKey.get_data(), 0, datKey.get_size());
+        return (ret == 0 || ret == DB_NOTFOUND);
+    }
+
+    template<typename K>
+    bool Exists(const K& key)
+    {
+        if (!pdb)
+            return false;
+
+        CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+        ssKey.reserve(1000);
+        ssKey << key;
+        Dbt datKey(&ssKey[0], ssKey.size());
+
+        int ret = pdb->exists(activeTxn, &datKey, 0);
+
+        memset(datKey.get_data(), 0, datKey.get_size());
+        return (ret == 0);
+    }
+
+    Dbc* GetCursor()
+    {
+        if (!pdb)
+            return NULL;
+        Dbc* pcursor = NULL;
+        int ret = pdb->cursor(NULL, &pcursor, 0);
+        if (ret != 0)
+            return NULL;
+        return pcursor;
+    }
+
+    int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags=DB_NEXT)
+    {
+        Dbt datKey;
+        if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE)
+        {
+            datKey.set_data(&ssKey[0]);
+            datKey.set_size(ssKey.size());
+        }
+        Dbt datValue;
+        if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE)
+        {
+            datValue.set_data(&ssValue[0]);
+            datValue.set_size(ssValue.size());
+        }
+        datKey.set_flags(DB_DBT_MALLOC);
+        datValue.set_flags(DB_DBT_MALLOC);
+        int ret = pcursor->get(&datKey, &datValue, fFlags);
+        if (ret != 0)
+            return ret;
+        else if (datKey.get_data() == NULL || datValue.get_data() == NULL)
+            return 99999;
+
+        ssKey.SetType(SER_DISK);
+        ssKey.clear();
+        ssKey.write((char*)datKey.get_data(), datKey.get_size());
+        ssValue.SetType(SER_DISK);
+        ssValue.clear();
+        ssValue.write((char*)datValue.get_data(), datValue.get_size());
+
+        memset(datKey.get_data(), 0, datKey.get_size());
+        memset(datValue.get_data(), 0, datValue.get_size());
+        free(datKey.get_data());
+        free(datValue.get_data());
+        return 0;
+    }
+
+public:
+    bool TxnBegin()
+    {
+        if (!pdb || activeTxn)
+            return false;
+        DbTxn* ptxn = bitdb.TxnBegin();
+        if (!ptxn)
+            return false;
+        activeTxn = ptxn;
+        return true;
+    }
+
+    bool TxnCommit()
+    {
+        if (!pdb || !activeTxn)
+            return false;
+        int ret = activeTxn->commit(0);
+        activeTxn = NULL;
+        return (ret == 0);
+    }
+
+    bool TxnAbort()
+    {
+        if (!pdb || !activeTxn)
+            return false;
+        int ret = activeTxn->abort();
+        activeTxn = NULL;
+        return (ret == 0);
+    }
+
+    bool ReadVersion(int& nVersion)
+    {
+        nVersion = 0;
+        return Read(std::string("version"), nVersion);
+    }
+
+    bool WriteVersion(int nVersion)
+    {
+        return Write(std::string("version"), nVersion);
+    }
+
+    bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL);
+};
+
+class CAddrDB
+{
+private:
+    boost::filesystem::path pathAddr;
+public:
+    CAddrDB();
+    bool Write(const CAddrMan& addr);
+    bool Read(CAddrMan& addr);
+};
+
+#endif // ECOIN_DB_H

File diff suppressed because it is too large
+ 1225 - 0
ecoin/src/ecoinrpc.cpp


+ 179 - 0
ecoin/src/ecoinrpc.h

@@ -0,0 +1,179 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+#ifndef _ECOINRPC_H_
+#define _ECOINRPC_H_ 1
+#include <string>
+#include <list>
+#include <map>
+
+class CBlockIndex;
+
+#include "json/json_spirit_reader_template.h"
+#include "json/json_spirit_writer_template.h"
+#include "json/json_spirit_utils.h"
+#include "util.h"
+#include "checkpoints.h"
+
+enum HTTPStatusCode
+{
+    HTTP_OK                    = 200,
+    HTTP_BAD_REQUEST           = 400,
+    HTTP_UNAUTHORIZED          = 401,
+    HTTP_FORBIDDEN             = 403,
+    HTTP_NOT_FOUND             = 404,
+    HTTP_INTERNAL_SERVER_ERROR = 500,
+};
+
+enum RPCErrorCode
+{
+    // Standard JSON-RPC 2.0 errors
+    RPC_INVALID_REQUEST  = -32600,
+    RPC_METHOD_NOT_FOUND = -32601,
+    RPC_INVALID_PARAMS   = -32602,
+    RPC_INTERNAL_ERROR   = -32603,
+    RPC_PARSE_ERROR      = -32700,
+
+    // General application defined errors
+    RPC_MISC_ERROR                  = -1,  // std::exception thrown in command handling
+    RPC_FORBIDDEN_BY_SAFE_MODE      = -2,  // Server is in safe mode, and command is not allowed in safe mode
+    RPC_TYPE_ERROR                  = -3,  // Unexpected type was passed as parameter
+    RPC_INVALID_ADDRESS_OR_KEY      = -5,  // Invalid address or key
+    RPC_OUT_OF_MEMORY               = -7,  // Ran out of memory during operation
+    RPC_INVALID_PARAMETER           = -8,  // Invalid, missing or duplicate parameter
+    RPC_DATABASE_ERROR              = -20, // Database error
+    RPC_DESERIALIZATION_ERROR       = -22, // Error parsing or validating structure in raw format
+
+    // P2P client errors
+    RPC_CLIENT_NOT_CONNECTED        = -9,  // Ecoin is not connected
+    RPC_CLIENT_IN_INITIAL_DOWNLOAD  = -10, // Still downloading initial blocks
+
+    // Wallet errors
+    RPC_WALLET_ERROR                = -4,  // Unspecified problem with wallet (key not found etc.)
+    RPC_WALLET_INSUFFICIENT_FUNDS   = -6,  // Not enough funds in wallet or account
+    RPC_WALLET_INVALID_ACCOUNT_NAME = -11, // Invalid account name
+    RPC_WALLET_KEYPOOL_RAN_OUT      = -12, // Keypool ran out, call keypoolrefill first
+    RPC_WALLET_UNLOCK_NEEDED        = -13, // Enter the wallet passphrase with walletpassphrase first
+    RPC_WALLET_PASSPHRASE_INCORRECT = -14, // The wallet passphrase entered was incorrect
+    RPC_WALLET_WRONG_ENC_STATE      = -15, // Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
+    RPC_WALLET_ENCRYPTION_FAILED    = -16, // Failed to encrypt the wallet
+    RPC_WALLET_ALREADY_UNLOCKED     = -17, // Wallet is already unlocked
+};
+
+json_spirit::Object JSONRPCError(int code, const std::string& message);
+
+void ThreadRPCServer(void* parg);
+int CommandLineRPC(int argc, char *argv[]);
+
+json_spirit::Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams);
+
+void RPCTypeCheck(const json_spirit::Array& params,
+                  const std::list<json_spirit::Value_type>& typesExpected, bool fAllowNull=false);
+void RPCTypeCheck(const json_spirit::Object& o,
+                  const std::map<std::string, json_spirit::Value_type>& typesExpected, bool fAllowNull=false);
+
+typedef json_spirit::Value(*rpcfn_type)(const json_spirit::Array& params, bool fHelp);
+
+class CRPCCommand
+{
+public:
+    std::string name;
+    rpcfn_type actor;
+    bool okSafeMode;
+    bool unlocked;
+};
+
+class CRPCTable
+{
+private:
+    std::map<std::string, const CRPCCommand*> mapCommands;
+public:
+    CRPCTable();
+    const CRPCCommand* operator[](std::string name) const;
+    std::string help(std::string name) const;
+    json_spirit::Value execute(const std::string &method, const json_spirit::Array &params) const;
+};
+
+extern const CRPCTable tableRPC;
+extern int64 nWalletUnlockTime;
+extern int64 AmountFromValue(const json_spirit::Value& value);
+extern json_spirit::Value ValueFromAmount(int64 amount);
+extern double GetDifficulty(const CBlockIndex* blockindex = NULL);
+extern double GetPoWMHashPS();
+extern double GetPoSKernelPS();
+extern std::string HexBits(unsigned int nBits);
+extern std::string HelpRequiringPassphrase();
+extern void EnsureWalletIsUnlocked();
+extern uint256 ParseHashV(const json_spirit::Value& v, std::string strName);
+extern uint256 ParseHashO(const json_spirit::Object& o, std::string strKey);
+extern std::vector<unsigned char> ParseHexV(const json_spirit::Value& v, std::string strName);
+extern std::vector<unsigned char> ParseHexO(const json_spirit::Object& o, std::string strKey);
+extern json_spirit::Value getconnectioncount(const json_spirit::Array& params, bool fHelp); // in rpcnet.cpp
+extern json_spirit::Value getpeerinfo(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value dumpwallet(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp
+extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value sendalert(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getsubsidy(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getstakinginfo(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getwork(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getworkex(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getblocktemplate(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value submitblock(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getnewaddress(const json_spirit::Array& params, bool fHelp); // in rpcwallet.cpp
+extern json_spirit::Value getaccountaddress(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value setaccount(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getaccount(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getaddressesbyaccount(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value sendtoaddress(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value signmessage(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value verifymessage(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getreceivedbyaddress(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getreceivedbyaccount(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getbalance(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value movecmd(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value sendfrom(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value sendmany(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value addmultisigaddress(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value addredeemscript(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value listreceivedbyaddress(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value listreceivedbyaccount(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value listtransactions(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value listaddressgroupings(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value listaccounts(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value listsinceblock(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value gettransaction(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value backupwallet(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value keypoolrefill(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value walletpassphrase(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value walletpassphrasechange(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value walletlock(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value encryptwallet(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value validateaddress(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getinfo(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value reservebalance(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value checkwallet(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value repairwallet(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value resendtx(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value makekeypair(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value validatepubkey(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getnewpubkey(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp
+extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value createrawtransaction(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value decoderawtransaction(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value decodescript(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value signrawtransaction(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value sendrawtransaction(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getbestblockhash(const json_spirit::Array& params, bool fHelp); // in rpcblockchain.cpp
+extern json_spirit::Value getblockcount(const json_spirit::Array& params, bool fHelp); // in rpcblockchain.cpp
+extern json_spirit::Value getdifficulty(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value settxfee(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getrawmempool(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getblockhash(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getblock(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getblockbynumber(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getcheckpoint(const json_spirit::Array& params, bool fHelp);
+
+#endif

+ 837 - 0
ecoin/src/init.cpp

@@ -0,0 +1,837 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#include "txdb.h"
+#include "walletdb.h"
+#include "ecoinrpc.h"
+#include "net.h"
+#include "init.h"
+#include "util.h"
+#include "ui_interface.h"
+#include "checkpoints.h"
+#include "zerocoin/ZeroTest.h"
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/convenience.hpp>
+#include <boost/interprocess/sync/file_lock.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <openssl/crypto.h>
+
+#ifndef WIN32
+#include <signal.h>
+#endif
+
+using namespace std;
+using namespace boost;
+
+CWallet* pwalletMain;
+CClientUIInterface uiInterface;
+std::string strWalletFileName;
+bool fConfChange;
+bool fEnforceCanonical;
+unsigned int nNodeLifespan;
+unsigned int nDerivationMethodIndex;
+unsigned int nMinerSleep;
+bool fUseFastIndex;
+bool fUseFastStakeMiner;
+enum Checkpoints::CPMode CheckpointsMode;
+
+void ExitTimeout(void* parg)
+{
+#ifdef WIN32
+    Sleep(5000);
+    ExitProcess(0);
+#endif
+}
+
+void StartShutdown()
+{
+#ifdef QT_GUI
+    uiInterface.QueueShutdown();
+#else
+    NewThread(Shutdown, NULL);
+#endif
+}
+
+void Shutdown(void* parg)
+{
+    static CCriticalSection cs_Shutdown;
+    static bool fTaken;
+    RenameThread("ecoin-shutoff");
+
+    bool fFirstThread = false;
+    {
+        TRY_LOCK(cs_Shutdown, lockShutdown);
+        if (lockShutdown)
+        {
+            fFirstThread = !fTaken;
+            fTaken = true;
+        }
+    }
+    static bool fExit;
+    if (fFirstThread)
+    {
+        fShutdown = true;
+        nTransactionsUpdated++;
+        bitdb.Flush(false);
+        StopNode();
+        bitdb.Flush(true);
+        boost::filesystem::remove(GetPidFile());
+        UnregisterWallet(pwalletMain);
+        delete pwalletMain;
+        NewThread(ExitTimeout, NULL);
+        Sleep(50);
+        printf("Ecoin exited\n\n");
+        fExit = true;
+#ifndef QT_GUI
+        exit(0);
+#endif
+    }
+    else
+    {
+        while (!fExit)
+            Sleep(500);
+        Sleep(100);
+        ExitThread(0);
+    }
+}
+
+void HandleSIGTERM(int)
+{
+    fRequestShutdown = true;
+}
+
+void HandleSIGHUP(int)
+{
+    fReopenDebugLog = true;
+}
+
+#if !defined(QT_GUI)
+bool AppInit(int argc, char* argv[])
+{
+    bool fRet = false;
+    try
+    {
+        ParseParameters(argc, argv);
+        if (!boost::filesystem::is_directory(GetDataDir(false)))
+        {
+            fprintf(stderr, "Error: Specified directory does not exist\n");
+            Shutdown(NULL);
+        }
+        ReadConfigFile(mapArgs, mapMultiArgs);
+
+        if (mapArgs.count("-?") || mapArgs.count("--help"))
+        {
+            std::string strUsage = _("Ecoin version") + " " + FormatFullVersion() + "\n\n" +
+                _("Usage:") + "\n" +
+                  "  ecoind [options]                     " + "\n" +
+                  "  ecoind [options] <command> [params]  " + _("Send command to -server or ecoind") + "\n" +
+                  "  ecoind [options] help                " + _("List commands") + "\n" +
+                  "  ecoind [options] help <command>      " + _("Get help for a command") + "\n";
+
+            strUsage += "\n" + HelpMessage();
+
+            fprintf(stdout, "%s", strUsage.c_str());
+            return false;
+        }
+
+        for (int i = 1; i < argc; i++)
+            if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "ecoin:"))
+                fCommandLine = true;
+
+        if (fCommandLine)
+        {
+            int ret = CommandLineRPC(argc, argv);
+            exit(ret);
+        }
+
+        fRet = AppInit2();
+    }
+    catch (std::exception& e) {
+        PrintException(&e, "AppInit()");
+    } catch (...) {
+        PrintException(NULL, "AppInit()");
+    }
+    if (!fRet)
+        Shutdown(NULL);
+    return fRet;
+}
+
+extern void noui_connect();
+int main(int argc, char* argv[])
+{
+    bool fRet = false;
+
+    noui_connect();
+
+    fRet = AppInit(argc, argv);
+
+    if (fRet && fDaemon)
+        return 0;
+
+    return 1;
+}
+#endif
+
+bool static InitError(const std::string &str)
+{
+    uiInterface.ThreadSafeMessageBox(str, _("Ecoin"), CClientUIInterface::OK | CClientUIInterface::MODAL);
+    return false;
+}
+
+bool static InitWarning(const std::string &str)
+{
+    uiInterface.ThreadSafeMessageBox(str, _("Ecoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL);
+    return true;
+}
+
+
+bool static Bind(const CService &addr, bool fError = true) {
+    if (IsLimited(addr))
+        return false;
+    std::string strError;
+    if (!BindListenPort(addr, strError)) {
+        if (fError)
+            return InitError(strError);
+        return false;
+    }
+    return true;
+}
+
+std::string HelpMessage()
+{
+    string strUsage = _("Options:") + "\n" +
+        "  -?                     " + _("This help message") + "\n" +
+        "  -conf=<file>           " + _("Specify configuration file (default: ecoin.conf)") + "\n" +
+        "  -pid=<file>            " + _("Specify pid file (default: ecoind.pid)") + "\n" +
+        "  -datadir=<dir>         " + _("Specify data directory") + "\n" +
+        "  -wallet=<dir>          " + _("Specify wallet file (within data directory)") + "\n" +
+        "  -dbcache=<n>           " + _("Set database cache size in megabytes (default: 25)") + "\n" +
+        "  -dblogsize=<n>         " + _("Set database disk log size in megabytes (default: 100)") + "\n" +
+        "  -timeout=<n>           " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n" +
+        "  -proxy=<ip:port>       " + _("Connect through socks proxy") + "\n" +
+        "  -socks=<n>             " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" +
+        "  -tor=<ip:port>         " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n"
+        "  -dns                   " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" +
+        "  -port=<port>           " + _("Listen for connections on <port> (default: 7408 or testnet: 17408)") + "\n" +
+        "  -maxconnections=<n>    " + _("Maintain at most <n> connections to peers (default: 125)") + "\n" +
+        "  -addnode=<ip>          " + _("Add a node to connect to and attempt to keep the connection open") + "\n" +
+        "  -connect=<ip>          " + _("Connect only to the specified node(s)") + "\n" +
+        "  -seednode=<ip>         " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" +
+        "  -externalip=<ip>       " + _("Specify your own public address") + "\n" +
+        "  -onlynet=<net>         " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n" +
+        "  -discover              " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n" +
+        "  -irc                   " + _("Find peers using internet relay chat (default: 1)") + "\n" +
+        "  -listen                " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n" +
+        "  -bind=<addr>           " + _("Bind to given address. Use [host]:port notation for IPv6") + "\n" +
+        "  -dnsseed               " + _("Find peers using DNS lookup (default: 1)") + "\n" +
+        "  -staking               " + _("Stake your coins to support network and gain reward (default: 1)") + "\n" +
+        "  -cppolicy              " + _("Sync checkpoints policy (default: strict)") + "\n" +
+        "  -banscore=<n>          " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" +
+        "  -bantime=<n>           " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" +
+        "  -maxreceivebuffer=<n>  " + _("Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)") + "\n" +
+        "  -maxsendbuffer=<n>     " + _("Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)") + "\n" +
+#ifdef USE_UPNP
+#if USE_UPNP
+        "  -upnp                  " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n" +
+#else
+        "  -upnp                  " + _("Use UPnP to map the listening port (default: 0)") + "\n" +
+#endif
+#endif
+        "  -detachdb              " + _("Detach block and address databases. Increases shutdown time (default: 0)") + "\n" +
+        "  -paytxfee=<amt>        " + _("Fee per KB to add to transactions you send") + "\n" +
+        "  -mininput=<amt>        " + _("When creating transactions, ignore inputs with value less than this (default: 0.01)") + "\n" +
+#ifdef QT_GUI
+        "  -server                " + _("Accept command line and JSON-RPC commands") + "\n" +
+#endif
+#if !defined(WIN32) && !defined(QT_GUI)
+        "  -daemon                " + _("Run in the background as a daemon and accept commands") + "\n" +
+#endif
+        "  -testnet               " + _("Use the test network") + "\n" +
+        "  -debug                 " + _("Output extra debugging information. Implies all other -debug* options") + "\n" +
+        "  -debugnet              " + _("Output extra network debugging information") + "\n" +
+        "  -logtimestamps         " + _("Prepend debug output with timestamp") + "\n" +
+        "  -shrinkdebugfile       " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n" +
+        "  -printtoconsole        " + _("Send trace/debug info to console instead of debug.log file") + "\n" +
+#ifdef WIN32
+        "  -printtodebugger       " + _("Send trace/debug info to debugger") + "\n" +
+#endif
+        "  -rpcuser=<user>        " + _("Username for JSON-RPC connections") + "\n" +
+        "  -rpcpassword=<pw>      " + _("Password for JSON-RPC connections") + "\n" +
+        "  -rpcport=<port>        " + _("Listen for JSON-RPC connections on <port> (default: 7474 or testnet: 17474)") + "\n" +
+        "  -rpcallowip=<ip>       " + _("Allow JSON-RPC connections from specified IP address") + "\n" +
+        "  -rpcconnect=<ip>       " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n" +
+        "  -blocknotify=<cmd>     " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" +
+        "  -walletnotify=<cmd>    " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" +
+        "  -confchange            " + _("Require a confirmations for change (default: 0)") + "\n" +
+        "  -enforcecanonical      " + _("Enforce transaction scripts to use canonical PUSH operators (default: 1)") + "\n" +
+        "  -upgradewallet         " + _("Upgrade wallet to latest format") + "\n" +
+        "  -keypool=<n>           " + _("Set key pool size to <n> (default: 100)") + "\n" +
+        "  -rescan                " + _("Rescan the block chain for missing wallet transactions") + "\n" +
+        "  -salvagewallet         " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" +
+        "  -checkblocks=<n>       " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" +
+        "  -checklevel=<n>        " + _("How thorough the block verification is (0-6, default: 1)") + "\n" +
+        "  -loadblock=<file>      " + _("Imports blocks from external blk000?.dat file") + "\n" +
+
+        "\n" + _("Block creation options:") + "\n" +
+        "  -blockminsize=<n>      "   + _("Set minimum block size in bytes (default: 0)") + "\n" +
+        "  -blockmaxsize=<n>      "   + _("Set maximum block size in bytes (default: 250000)") + "\n" +
+        "  -blockprioritysize=<n> "   + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n" +
+
+        "\n" + _("SSL options: (see the Ecoin Wiki for SSL setup instructions)") + "\n" +
+        "  -rpcssl                                  " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n" +
+        "  -rpcsslcertificatechainfile=<file.cert>  " + _("Server certificate file (default: server.cert)") + "\n" +
+        "  -rpcsslprivatekeyfile=<file.pem>         " + _("Server private key (default: server.pem)") + "\n" +
+        "  -rpcsslciphers=<ciphers>                 " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)") + "\n";
+
+    return strUsage;
+}
+
+bool AppInit2()
+{
+#ifdef _MSC_VER
+    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
+    _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
+#endif
+#if _MSC_VER >= 1400
+    // Disable confusing "helpful" text message on abort, Ctrl-C
+    _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
+#endif
+#ifdef WIN32
+#ifndef PROCESS_DEP_ENABLE
+#define PROCESS_DEP_ENABLE 0x00000001
+#endif
+    typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD);
+    PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy");
+    if (setProcDEPPol != NULL) setProcDEPPol(PROCESS_DEP_ENABLE);
+#endif
+#ifndef WIN32
+    umask(077);
+    struct sigaction sa;
+    sa.sa_handler = HandleSIGTERM;
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = 0;
+    sigaction(SIGTERM, &sa, NULL);
+    sigaction(SIGINT, &sa, NULL);
+    struct sigaction sa_hup;
+    sa_hup.sa_handler = HandleSIGHUP;
+    sigemptyset(&sa_hup.sa_mask);
+    sa_hup.sa_flags = 0;
+    sigaction(SIGHUP, &sa_hup, NULL);
+#endif
+    nNodeLifespan = GetArg("-addrlifespan", 7);
+    fUseFastIndex = GetBoolArg("-fastindex", true);
+    nMinerSleep = GetArg("-minersleep", 500);
+
+    CheckpointsMode = Checkpoints::STRICT;
+    std::string strCpMode = GetArg("-cppolicy", "strict");
+
+    if(strCpMode == "strict")
+        CheckpointsMode = Checkpoints::STRICT;
+
+    if(strCpMode == "advisory")
+        CheckpointsMode = Checkpoints::ADVISORY;
+
+    if(strCpMode == "permissive")
+        CheckpointsMode = Checkpoints::PERMISSIVE;
+
+    nDerivationMethodIndex = 0;
+
+    fTestNet = GetBoolArg("-testnet");
+    if (fTestNet) {
+        SoftSetBoolArg("-irc", true);
+    }
+
+    if (mapArgs.count("-bind")) {
+        SoftSetBoolArg("-listen", true);
+    }
+
+    if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) {
+        SoftSetBoolArg("-dnsseed", false);
+        SoftSetBoolArg("-listen", false);
+    }
+
+    if (mapArgs.count("-proxy")) {
+        SoftSetBoolArg("-listen", false);
+    }
+
+    if (!GetBoolArg("-listen", true)) {
+        SoftSetBoolArg("-upnp", false);
+        SoftSetBoolArg("-discover", false);
+    }
+
+    if (mapArgs.count("-externalip")) {
+        SoftSetBoolArg("-discover", false);
+    }
+
+    if (GetBoolArg("-salvagewallet")) {
+        SoftSetBoolArg("-rescan", true);
+    }
+
+    fDebug = GetBoolArg("-debug");
+
+    if (fDebug)
+        fDebugNet = true;
+    else
+        fDebugNet = GetBoolArg("-debugnet");
+
+    bitdb.SetDetach(GetBoolArg("-detachdb", false));
+
+#if !defined(WIN32) && !defined(QT_GUI)
+    fDaemon = GetBoolArg("-daemon");
+#else
+    fDaemon = false;
+#endif
+
+    if (fDaemon)
+        fServer = true;
+    else
+        fServer = GetBoolArg("-server");
+
+#if !defined(QT_GUI)
+    fServer = true;
+#endif
+    fPrintToConsole = GetBoolArg("-printtoconsole");
+    fPrintToDebugger = GetBoolArg("-printtodebugger");
+    fLogTimestamps = GetBoolArg("-logtimestamps");
+
+    if (mapArgs.count("-timeout"))
+    {
+        int nNewTimeout = GetArg("-timeout", 5000);
+        if (nNewTimeout > 0 && nNewTimeout < 600000)
+            nConnectTimeout = nNewTimeout;
+    }
+
+    const char* pszP2SH = "/P2SH/";
+    COINBASE_FLAGS << std::vector<unsigned char>(pszP2SH, pszP2SH+strlen(pszP2SH));
+
+    if (mapArgs.count("-paytxfee"))
+    {
+        if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee))
+            return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s'"), mapArgs["-paytxfee"].c_str()));
+        if (nTransactionFee > 0.25 * COIN)
+            InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction."));
+    }
+
+    fConfChange = GetBoolArg("-confchange", false);
+    fEnforceCanonical = GetBoolArg("-enforcecanonical", true);
+
+    if (mapArgs.count("-mininput"))
+    {
+        if (!ParseMoney(mapArgs["-mininput"], nMinimumInputValue))
+            return InitError(strprintf(_("Invalid amount for -mininput=<amount>: '%s'"), mapArgs["-mininput"].c_str()));
+    }
+
+    std::string strDataDir = GetDataDir().string();
+    std::string strWalletFileName = GetArg("-wallet", "wallet.dat");
+
+    if (strWalletFileName != boost::filesystem::basename(strWalletFileName) + boost::filesystem::extension(strWalletFileName))
+        return InitError(strprintf(_("Wallet %s resides outside data directory %s."), strWalletFileName.c_str(), strDataDir.c_str()));
+
+    boost::filesystem::path pathLockFile = GetDataDir() / ".lock";
+    FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist.
+    if (file) fclose(file);
+    static boost::interprocess::file_lock lock(pathLockFile.string().c_str());
+    if (!lock.try_lock())
+        return InitError(strprintf(_("Cannot obtain a lock on data directory %s.  Ecoin is probably already running."), strDataDir.c_str()));
+
+#if !defined(WIN32) && !defined(QT_GUI)
+    if (fDaemon)
+    {
+        // Daemonize
+        pid_t pid = fork();
+        if (pid < 0)
+        {
+            fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
+            return false;
+        }
+        if (pid > 0)
+        {
+            CreatePidFile(GetPidFile(), pid);
+            return true;
+        }
+
+        pid_t sid = setsid();
+        if (sid < 0)
+            fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
+    }
+#endif
+
+    if (GetBoolArg("-shrinkdebugfile", !fDebug))
+        ShrinkDebugFile();
+    printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
+    printf("Ecoin version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str());
+    printf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION));
+    if (!fLogTimestamps)
+        printf("Startup time: %s\n", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
+    printf("Default data directory %s\n", GetDefaultDataDir().string().c_str());
+    printf("Used data directory %s\n", strDataDir.c_str());
+    std::ostringstream strErrors;
+
+    if (fDaemon)
+        fprintf(stdout, "Ecoin server starting\n");
+
+    int64 nStart;
+
+    uiInterface.InitMessage(_("Verifying database integrity..."));
+
+    if (!bitdb.Open(GetDataDir()))
+    {
+        string msg = strprintf(_("Error initializing database environment %s!"
+                                 " To recover, BACKUP THAT DIRECTORY, then remove"
+                                 " everything from it except for wallet.dat."), strDataDir.c_str());
+        return InitError(msg);
+    }
+
+    if (GetBoolArg("-salvagewallet"))
+    {
+        // Recover readable keypairs:
+        if (!CWalletDB::Recover(bitdb, strWalletFileName, true))
+            return false;
+    }
+
+    if (filesystem::exists(GetDataDir() / strWalletFileName))
+    {
+        CDBEnv::VerifyResult r = bitdb.Verify(strWalletFileName, CWalletDB::Recover);
+        if (r == CDBEnv::RECOVER_OK)
+        {
+            string msg = strprintf(_("Warning: wallet.dat corrupt, data salvaged!"
+                                     " Original wallet.dat saved as wallet.{timestamp}.bak in %s; if"
+                                     " your balance or transactions are incorrect you should"
+                                     " restore from a backup."), strDataDir.c_str());
+            uiInterface.ThreadSafeMessageBox(msg, _("Ecoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL);
+        }
+        if (r == CDBEnv::RECOVER_FAIL)
+            return InitError(_("wallet.dat corrupt, salvage failed"));
+    }
+
+    int nSocksVersion = GetArg("-socks", 5);
+
+    if (nSocksVersion != 4 && nSocksVersion != 5)
+        return InitError(strprintf(_("Unknown -socks proxy version requested: %i"), nSocksVersion));
+
+    if (mapArgs.count("-onlynet")) {
+        std::set<enum Network> nets;
+        BOOST_FOREACH(std::string snet, mapMultiArgs["-onlynet"]) {
+            enum Network net = ParseNetwork(snet);
+            if (net == NET_UNROUTABLE)
+                return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet.c_str()));
+            nets.insert(net);
+        }
+        for (int n = 0; n < NET_MAX; n++) {
+            enum Network net = (enum Network)n;
+            if (!nets.count(net))
+                SetLimited(net);
+        }
+    }
+#if defined(USE_IPV6)
+#if ! USE_IPV6
+    else
+        SetLimited(NET_IPV6);
+#endif
+#endif
+
+    CService addrProxy;
+    bool fProxy = false;
+    if (mapArgs.count("-proxy")) {
+        addrProxy = CService(mapArgs["-proxy"], 9050);
+        if (!addrProxy.IsValid())
+            return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"].c_str()));
+
+        if (!IsLimited(NET_IPV4))
+            SetProxy(NET_IPV4, addrProxy, nSocksVersion);
+        if (nSocksVersion > 4) {
+#ifdef USE_IPV6
+            if (!IsLimited(NET_IPV6))
+                SetProxy(NET_IPV6, addrProxy, nSocksVersion);
+#endif
+            SetNameProxy(addrProxy, nSocksVersion);
+        }
+        fProxy = true;
+    }
+
+    if (!(mapArgs.count("-tor") && mapArgs["-tor"] == "0") && (fProxy || mapArgs.count("-tor"))) {
+        CService addrOnion;
+        if (!mapArgs.count("-tor"))
+            addrOnion = addrProxy;
+        else
+            addrOnion = CService(mapArgs["-tor"], 9050);
+        if (!addrOnion.IsValid())
+            return InitError(strprintf(_("Invalid -tor address: '%s'"), mapArgs["-tor"].c_str()));
+        SetProxy(NET_TOR, addrOnion, 5);
+        SetReachable(NET_TOR);
+    }
+
+    fNoListen = !GetBoolArg("-listen", true);
+    fDiscover = GetBoolArg("-discover", true);
+    fNameLookup = GetBoolArg("-dns", true);
+#ifdef USE_UPNP
+    fUseUPnP = GetBoolArg("-upnp", USE_UPNP);
+#endif
+    bool fBound = false;
+    if (!fNoListen)
+    {
+        std::string strError;
+        if (mapArgs.count("-bind")) {
+            BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) {
+                CService addrBind;
+                if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false))
+                    return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind.c_str()));
+                fBound |= Bind(addrBind);
+            }
+        } else {
+            struct in_addr inaddr_any;
+            inaddr_any.s_addr = INADDR_ANY;
+#ifdef USE_IPV6
+            if (!IsLimited(NET_IPV6))
+                fBound |= Bind(CService(in6addr_any, GetListenPort()), false);
+#endif
+            if (!IsLimited(NET_IPV4))
+                fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound);
+        }
+        if (!fBound)
+            return InitError(_("Failed to listen on any port. Use -listen=0 if you want this."));
+    }
+
+    if (mapArgs.count("-externalip"))
+    {
+        BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"]) {
+            CService addrLocal(strAddr, GetListenPort(), fNameLookup);
+            if (!addrLocal.IsValid())
+                return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr.c_str()));
+            AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL);
+        }
+    }
+
+    if (mapArgs.count("-reservebalance")) // ecoin: reserve balance amount
+    {
+        int64 nReserveBalance = 0;
+        if (!ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
+        {
+            InitError(_("Invalid amount for -reservebalance=<amount>"));
+            return false;
+        }
+    }
+
+    if (mapArgs.count("-checkpointkey")) // ecoin: checkpoint master priv key
+    {
+        if (!Checkpoints::SetCheckpointPrivKey(GetArg("-checkpointkey", "")))
+            InitError(_("Unable to sign checkpoint, wrong checkpointkey?\n"));
+    }
+
+    BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"])
+        AddOneShot(strDest);
+
+    if (!bitdb.Open(GetDataDir()))
+    {
+        string msg = strprintf(_("Error initializing database environment %s!"
+                                 " To recover, BACKUP THAT DIRECTORY, then remove"
+                                 " everything from it except for wallet.dat."), strDataDir.c_str());
+        return InitError(msg);
+    }
+
+    if (GetBoolArg("-loadblockindextest"))
+    {
+        CTxDB txdb("r");
+        txdb.LoadBlockIndex();
+        PrintBlockTree();
+        return false;
+    }
+
+    uiInterface.InitMessage(_("Loading block index..."));
+    printf("Loading block index...\n");
+    nStart = GetTimeMillis();
+    if (!LoadBlockIndex())
+        return InitError(_("Error loading blkindex.dat"));
+
+    if (fRequestShutdown)
+    {
+        printf("Shutdown requested. Exiting.\n");
+        return false;
+    }
+    printf(" block index %15" PRI64d"ms\n", GetTimeMillis() - nStart);
+
+    if (GetBoolArg("-printblockindex") || GetBoolArg("-printblocktree"))
+    {
+        PrintBlockTree();
+        return false;
+    }
+
+    if (mapArgs.count("-printblock"))
+    {
+        string strMatch = mapArgs["-printblock"];
+        int nFound = 0;
+        for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)
+        {
+            uint256 hash = (*mi).first;
+            if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0)
+            {
+                CBlockIndex* pindex = (*mi).second;
+                CBlock block;
+                block.ReadFromDisk(pindex);
+                block.BuildMerkleTree();
+                block.print();
+                printf("\n");
+                nFound++;
+            }
+        }
+        if (nFound == 0)
+            printf("No blocks matching %s were found\n", strMatch.c_str());
+        return false;
+    }
+
+    if (GetBoolArg("-zerotest", false))
+    {
+        printf("\n=== ZeroCoin tests start ===\n");
+        Test_RunAllTests();
+        printf("=== ZeroCoin tests end ===\n\n");
+    }
+
+    uiInterface.InitMessage(_("Loading wallet..."));
+    printf("Loading wallet...\n");
+    nStart = GetTimeMillis();
+    bool fFirstRun = true;
+    pwalletMain = new CWallet(strWalletFileName);
+    DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun);
+    if (nLoadWalletRet != DB_LOAD_OK)
+    {
+        if (nLoadWalletRet == DB_CORRUPT)
+            strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n";
+        else if (nLoadWalletRet == DB_NONCRITICAL_ERROR)
+        {
+            string msg(_("Warning: error reading wallet.dat! All keys read correctly, but transaction data"
+                         " or address book entries might be missing or incorrect."));
+            uiInterface.ThreadSafeMessageBox(msg, _("Ecoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL);
+        }
+        else if (nLoadWalletRet == DB_TOO_NEW)
+            strErrors << _("Error loading wallet.dat: Wallet requires newer version of Ecoin") << "\n";
+        else if (nLoadWalletRet == DB_NEED_REWRITE)
+        {
+            strErrors << _("Wallet needed to be rewritten: restart Ecoin to complete") << "\n";
+            printf("%s", strErrors.str().c_str());
+            return InitError(strErrors.str());
+        }
+        else
+            strErrors << _("Error loading wallet.dat") << "\n";
+    }
+
+    if (GetBoolArg("-upgradewallet", fFirstRun))
+    {
+        int nMaxVersion = GetArg("-upgradewallet", 0);
+        if (nMaxVersion == 0) // the -upgradewallet without argument case
+        {
+            printf("Performing wallet upgrade to %i\n", FEATURE_LATEST);
+            nMaxVersion = CLIENT_VERSION;
+            pwalletMain->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately
+        }
+        else
+            printf("Allowing wallet upgrade up to %i\n", nMaxVersion);
+        if (nMaxVersion < pwalletMain->GetVersion())
+            strErrors << _("Cannot downgrade wallet") << "\n";
+        pwalletMain->SetMaxVersion(nMaxVersion);
+    }
+
+    if (fFirstRun)
+    {
+        RandAddSeedPerfmon();
+
+        CPubKey newDefaultKey;
+        if (!pwalletMain->GetKeyFromPool(newDefaultKey, false))
+            strErrors << _("Cannot initialize keypool") << "\n";
+        pwalletMain->SetDefaultKey(newDefaultKey);
+        if (!pwalletMain->SetAddressBookName(pwalletMain->vchDefaultKey.GetID(), ""))
+            strErrors << _("Cannot write default address") << "\n";
+    }
+
+    printf("%s", strErrors.str().c_str());
+    printf(" wallet      %15" PRI64d"ms\n", GetTimeMillis() - nStart);
+
+    RegisterWallet(pwalletMain);
+
+    CBlockIndex *pindexRescan = pindexBest;
+    if (GetBoolArg("-rescan"))
+        pindexRescan = pindexGenesisBlock;
+    else
+    {
+        CWalletDB walletdb(strWalletFileName);
+        CBlockLocator locator;
+        if (walletdb.ReadBestBlock(locator))
+            pindexRescan = locator.GetBlockIndex();
+    }
+    if (pindexBest != pindexRescan && pindexBest && pindexRescan && pindexBest->nHeight > pindexRescan->nHeight)
+    {
+        uiInterface.InitMessage(_("Rescanning..."));
+        printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight);
+        nStart = GetTimeMillis();
+        pwalletMain->ScanForWalletTransactions(pindexRescan, true);
+        printf(" rescan      %15" PRI64d"ms\n", GetTimeMillis() - nStart);
+    }
+
+    if (mapArgs.count("-loadblock"))
+    {
+        uiInterface.InitMessage(_("Importing blockchain data file."));
+
+        BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"])
+        {
+            FILE *file = fopen(strFile.c_str(), "rb");
+            if (file)
+                LoadExternalBlockFile(file);
+        }
+        exit(0);
+    }
+
+    filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat";
+    if (filesystem::exists(pathBootstrap)) {
+        uiInterface.InitMessage(_("Importing bootstrap blockchain data file."));
+
+        FILE *file = fopen(pathBootstrap.string().c_str(), "rb");
+        if (file) {
+            filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old";
+            LoadExternalBlockFile(file);
+            RenameOver(pathBootstrap, pathBootstrapOld);
+        }
+    }
+
+    uiInterface.InitMessage(_("Loading addresses..."));
+    printf("Loading addresses...\n");
+    nStart = GetTimeMillis();
+
+    {
+        CAddrDB adb;
+        if (!adb.Read(addrman))
+            printf("Invalid or missing peers.dat; recreating\n");
+    }
+
+    printf("Loaded %i addresses from peers.dat  %" PRI64d"ms\n",
+           addrman.size(), GetTimeMillis() - nStart);
+
+    if (!CheckDiskSpace())
+        return false;
+
+    RandAddSeedPerfmon();
+
+    printf("mapBlockIndex.size() = %" PRIszu"\n",   mapBlockIndex.size());
+    printf("nBestHeight = %d\n",            nBestHeight);
+    printf("setKeyPool.size() = %" PRIszu"\n",      pwalletMain->setKeyPool.size());
+    printf("mapWallet.size() = %" PRIszu"\n",       pwalletMain->mapWallet.size());
+    printf("mapAddressBook.size() = %" PRIszu"\n",  pwalletMain->mapAddressBook.size());
+
+    if (!NewThread(StartNode, NULL))
+        InitError(_("Error: could not start node"));
+
+    if (fServer)
+        NewThread(ThreadRPCServer, NULL);
+
+    uiInterface.InitMessage(_("Done loading"));
+    printf("Done loading\n");
+
+    if (!strErrors.str().empty())
+        return InitError(strErrors.str());
+
+    pwalletMain->ReacceptWalletTransactions();
+
+#if !defined(QT_GUI)
+    while (1)
+        Sleep(5000);
+#endif
+
+    return true;
+}

+ 14 - 0
ecoin/src/init.h

@@ -0,0 +1,14 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef ECOIN_INIT_H
+#define ECOIN_INIT_H
+
+#include "wallet.h"
+
+extern CWallet* pwalletMain;
+extern std::string strWalletFileName;
+void StartShutdown();
+void Shutdown(void* parg);
+bool AppInit2();
+std::string HelpMessage();
+
+#endif

+ 364 - 0
ecoin/src/irc.cpp

@@ -0,0 +1,364 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#include "irc.h"
+#include "net.h"
+#include "strlcpy.h"
+#include "base58.h"
+
+using namespace std;
+using namespace boost;
+
+int nGotIRCAddresses = 0;
+
+void ThreadIRCSeed2(void* parg);
+
+#pragma pack(push, 1)
+struct ircaddr
+{
+    struct in_addr ip;
+    short port;
+};
+
+#pragma pack(pop)
+string EncodeAddress(const CService& addr)
+{
+    struct ircaddr tmp;
+    if (addr.GetInAddr(&tmp.ip))
+    {
+        tmp.port = htons(addr.GetPort());
+
+        vector<unsigned char> vch(UBEGIN(tmp), UEND(tmp));
+        return string("u") + EncodeBase58Check(vch);
+    }
+    return "";
+}
+
+bool DecodeAddress(string str, CService& addr)
+{
+    vector<unsigned char> vch;
+    if (!DecodeBase58Check(str.substr(1), vch))
+        return false;
+
+    struct ircaddr tmp;
+    if (vch.size() != sizeof(tmp))
+        return false;
+    memcpy(&tmp, &vch[0], sizeof(tmp));
+
+    addr = CService(tmp.ip, ntohs(tmp.port));
+    return true;
+}
+
+static bool Send(SOCKET hSocket, const char* pszSend)
+{
+    if (strstr(pszSend, "PONG") != pszSend)
+        printf("IRC SENDING: %s\n", pszSend);
+    const char* psz = pszSend;
+    const char* pszEnd = psz + strlen(psz);
+    while (psz < pszEnd)
+    {
+        int ret = send(hSocket, psz, pszEnd - psz, MSG_NOSIGNAL);
+        if (ret < 0)
+            return false;
+        psz += ret;
+    }
+    return true;
+}
+
+bool RecvLineIRC(SOCKET hSocket, string& strLine)
+{
+    while (true)
+    {
+        bool fRet = RecvLine(hSocket, strLine);
+        if (fRet)
+        {
+            if (fShutdown)
+                return false;
+            vector<string> vWords;
+            ParseString(strLine, ' ', vWords);
+            if (vWords.size() >= 1 && vWords[0] == "PING")
+            {
+                strLine[1] = 'O';
+                strLine += '\r';
+                Send(hSocket, strLine.c_str());
+                continue;
+            }
+        }
+        return fRet;
+    }
+}
+
+int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL, const char* psz4=NULL)
+{
+    while (true)
+    {
+        string strLine;
+        strLine.reserve(10000);
+        if (!RecvLineIRC(hSocket, strLine))
+            return 0;
+        printf("IRC %s\n", strLine.c_str());
+        if (psz1 && strLine.find(psz1) != string::npos)
+            return 1;
+        if (psz2 && strLine.find(psz2) != string::npos)
+            return 2;
+        if (psz3 && strLine.find(psz3) != string::npos)
+            return 3;
+        if (psz4 && strLine.find(psz4) != string::npos)
+            return 4;
+    }
+}
+
+bool Wait(int nSeconds)
+{
+    if (fShutdown)
+        return false;
+    printf("IRC waiting %d seconds to reconnect\n", nSeconds);
+    for (int i = 0; i < nSeconds; i++)
+    {
+        if (fShutdown)
+            return false;
+        Sleep(1000);
+    }
+    return true;
+}
+
+bool RecvCodeLine(SOCKET hSocket, const char* psz1, string& strRet)
+{
+    strRet.clear();
+    while (true)
+    {
+        string strLine;
+        if (!RecvLineIRC(hSocket, strLine))
+            return false;
+
+        vector<string> vWords;
+        ParseString(strLine, ' ', vWords);
+        if (vWords.size() < 2)
+            continue;
+
+        if (vWords[1] == psz1)
+        {
+            printf("IRC %s\n", strLine.c_str());
+            strRet = strLine;
+            return true;
+        }
+    }
+}
+
+bool GetIPFromIRC(SOCKET hSocket, string strMyName, CNetAddr& ipRet)
+{
+    Send(hSocket, strprintf("USERHOST %s\r", strMyName.c_str()).c_str());
+
+    string strLine;
+    if (!RecvCodeLine(hSocket, "302", strLine))
+        return false;
+
+    vector<string> vWords;
+    ParseString(strLine, ' ', vWords);
+    if (vWords.size() < 4)
+        return false;
+
+    string str = vWords[3];
+    if (str.rfind("@") == string::npos)
+        return false;
+    string strHost = str.substr(str.rfind("@")+1);
+
+    printf("GetIPFromIRC() got userhost %s\n", strHost.c_str());
+    CNetAddr addr(strHost, true);
+    if (!addr.IsValid())
+        return false;
+    ipRet = addr;
+
+    return true;
+}
+
+
+void ThreadIRCSeed(void* parg)
+{
+    RenameThread("ecoin-ircseed");
+
+    try
+    {
+        ThreadIRCSeed2(parg);
+    }
+    catch (std::exception& e) {
+        PrintExceptionContinue(&e, "ThreadIRCSeed()");
+    } catch (...) {
+        PrintExceptionContinue(NULL, "ThreadIRCSeed()");
+    }
+    printf("ThreadIRCSeed exited\n");
+}
+
+void ThreadIRCSeed2(void* parg)
+{
+    if (IsLimited(NET_IPV4))
+        return;
+
+    if (mapArgs.count("-connect") && fNoListen)
+        return;
+
+    if (!GetBoolArg("-irc", true))
+        return;
+
+    printf("ThreadIRCSeed started\n");
+    int nErrorWait = 10;
+    int nRetryWait = 10;
+    int nNameRetry = 0;
+
+    while (!fShutdown)
+    {
+        CService addrConnect("162.213.39.42", 6697); // irc.freenode.net (10/01/2021)
+
+        CService addrIRC("irc.freenode.net", 6697, true);
+        if (addrIRC.IsValid())
+            addrConnect = addrIRC;
+
+        SOCKET hSocket;
+        if (!ConnectSocket(addrConnect, hSocket))
+        {
+            printf("IRC connect failed\n");
+            nErrorWait = nErrorWait * 11 / 10;
+            if (Wait(nErrorWait += 60))
+                continue;
+            else
+                return;
+        }
+
+        if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname", "ignoring hostname"))
+        {
+            closesocket(hSocket);
+            hSocket = INVALID_SOCKET;
+            nErrorWait = nErrorWait * 11 / 10;
+            if (Wait(nErrorWait += 60))
+                continue;
+            else
+                return;
+        }
+
+        CNetAddr addrIPv4("1.2.3.4"); // arbitrary IPv4 address to make GetLocal prefer IPv4 addresses
+        CService addrLocal;
+        string strMyName;
+        if (!fNoListen && GetLocal(addrLocal, &addrIPv4) && nNameRetry<3)
+            strMyName = EncodeAddress(GetLocalAddress(&addrConnect));
+        if (strMyName == "")
+            strMyName = strprintf("x%" PRI64u"", GetRand(1000000000));
+        Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
+        Send(hSocket, strprintf("USER %s 8 * : %s\r", strMyName.c_str(), strMyName.c_str()).c_str());
+        int nRet = RecvUntil(hSocket, " 004 ", " 433 ");
+        if (nRet != 1)
+        {
+            closesocket(hSocket);
+            hSocket = INVALID_SOCKET;
+            if (nRet == 2)
+            {
+                printf("IRC name already in use\n");
+                nNameRetry++;
+                Wait(10);
+                continue;
+            }
+            nErrorWait = nErrorWait * 11 / 10;
+            if (Wait(nErrorWait += 60))
+                continue;
+            else
+                return;
+        }
+        nNameRetry = 0;
+        Sleep(500);
+
+        CNetAddr addrFromIRC;
+        if (GetIPFromIRC(hSocket, strMyName, addrFromIRC))
+        {
+            printf("GetIPFromIRC() returned %s\n", addrFromIRC.ToString().c_str());
+            // Don't use our IP as our nick if we're not listening
+            if (!fNoListen && addrFromIRC.IsRoutable())
+            {
+                // IRC lets you to re-nick
+                AddLocal(addrFromIRC, LOCAL_IRC);
+                strMyName = EncodeAddress(GetLocalAddress(&addrConnect));
+                Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
+            }
+        }
+
+        if (fTestNet) {
+            Send(hSocket, "JOIN #ecoinTEST2\r");
+            Send(hSocket, "WHO #ecoinTEST2\r");
+        } else {
+            // Channel number is always 0 for initial release
+            int channel_number = 0;
+            Send(hSocket, strprintf("JOIN #ecoin%02d\r", channel_number).c_str());
+            Send(hSocket, strprintf("WHO #ecoin%02d\r", channel_number).c_str());
+        }
+
+        int64 nStart = GetTime();
+        string strLine;
+        strLine.reserve(10000);
+        while (!fShutdown && RecvLineIRC(hSocket, strLine))
+        {
+            if (strLine.empty() || strLine.size() > 900 || strLine[0] != ':')
+                continue;
+
+            vector<string> vWords;
+            ParseString(strLine, ' ', vWords);
+            if (vWords.size() < 2)
+                continue;
+
+            char pszName[10000];
+            pszName[0] = '\0';
+
+            if (vWords[1] == "352" && vWords.size() >= 8)
+            {
+                strlcpy(pszName, vWords[7].c_str(), sizeof(pszName));
+                printf("IRC got who\n");
+            }
+
+            if (vWords[1] == "JOIN" && vWords[0].size() > 1)
+            {
+                strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName));
+                if (strchr(pszName, '!'))
+                    *strchr(pszName, '!') = '\0';
+                printf("IRC got join\n");
+            }
+
+            if (pszName[0] == 'u')
+            {
+                CAddress addr;
+                if (DecodeAddress(pszName, addr))
+                {
+                    addr.nTime = GetAdjustedTime();
+                    if (addrman.Add(addr, addrConnect, 51 * 60))
+                        printf("IRC got new address: %s\n", addr.ToString().c_str());
+                    nGotIRCAddresses++;
+                }
+                else
+                {
+                    printf("IRC decode failed\n");
+                }
+            }
+        }
+        closesocket(hSocket);
+        hSocket = INVALID_SOCKET;
+
+        if (GetTime() - nStart > 20 * 60)
+        {
+            nErrorWait /= 3;
+            nRetryWait /= 3;
+        }
+
+        nRetryWait = nRetryWait * 11 / 10;
+        if (!Wait(nRetryWait += 60))
+            return;
+    }
+}
+
+#ifdef TEST
+int main(int argc, char *argv[])
+{
+    WSADATA wsadata;
+    if (WSAStartup(MAKEWORD(2,2), &wsadata) != NO_ERROR)
+    {
+        printf("Error at WSAStartup()\n");
+        return false;
+    }
+    ThreadIRCSeed(NULL);
+    WSACleanup();
+    return 0;
+}
+#endif

+ 8 - 0
ecoin/src/irc.h

@@ -0,0 +1,8 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef ECOIN_IRC_H
+#define ECOIN_IRC_H
+
+void ThreadIRCSeed(void* parg);
+extern int nGotIRCAddresses;
+
+#endif

+ 24 - 0
ecoin/src/json/LICENSE.txt

@@ -0,0 +1,24 @@
+The MIT License
+
+Copyright (c) 2007 - 2009 John W. Wilkinson
+
+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.

+ 14 - 0
ecoin/src/json/json_spirit.h

@@ -0,0 +1,14 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef JSON_SPIRIT
+#define JSON_SPIRIT
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+#include "json_spirit_value.h"
+#include "json_spirit_reader.h"
+#include "json_spirit_writer.h"
+#include "json_spirit_utils.h"
+
+#endif

+ 46 - 0
ecoin/src/json/json_spirit_error_position.h

@@ -0,0 +1,46 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef JSON_SPIRIT_ERROR_POSITION
+#define JSON_SPIRIT_ERROR_POSITION
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+#include <string>
+
+namespace json_spirit
+{
+    struct Error_position
+    {
+        Error_position();
+        Error_position( unsigned int line, unsigned int column, const std::string& reason );
+        bool operator==( const Error_position& lhs ) const;
+        unsigned int line_;
+        unsigned int column_;
+        std::string reason_;
+    };
+
+    inline Error_position::Error_position()
+    :   line_( 0 )
+    ,   column_( 0 )
+    {
+    }
+
+    inline Error_position::Error_position( unsigned int line, unsigned int column, const std::string& reason )
+    :   line_( line )
+    ,   column_( column )
+    ,   reason_( reason )
+    {
+    }
+
+    inline bool Error_position::operator==( const Error_position& lhs ) const
+    {
+        if( this == &lhs ) return true;
+
+        return ( reason_ == lhs.reason_ ) &&
+               ( line_   == lhs.line_ ) &&
+               ( column_ == lhs.column_ ); 
+}
+}
+
+#endif

+ 134 - 0
ecoin/src/json/json_spirit_reader.cpp

@@ -0,0 +1,134 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+
+#include "json_spirit_reader.h"
+#include "json_spirit_reader_template.h"
+
+using namespace json_spirit;
+
+bool json_spirit::read( const std::string& s, Value& value )
+{
+    return read_string( s, value );
+}
+
+void json_spirit::read_or_throw( const std::string& s, Value& value )
+{
+    read_string_or_throw( s, value );
+}
+
+bool json_spirit::read( std::istream& is, Value& value )
+{
+    return read_stream( is, value );
+}
+
+void json_spirit::read_or_throw( std::istream& is, Value& value )
+{
+    read_stream_or_throw( is, value );
+}
+
+bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value )
+{
+    return read_range( begin, end, value );
+}
+
+void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value )
+{
+    begin = read_range_or_throw( begin, end, value );
+}
+
+#ifndef BOOST_NO_STD_WSTRING
+
+bool json_spirit::read( const std::wstring& s, wValue& value )
+{
+    return read_string( s, value );
+}
+
+void json_spirit::read_or_throw( const std::wstring& s, wValue& value )
+{
+    read_string_or_throw( s, value );
+}
+
+bool json_spirit::read( std::wistream& is, wValue& value )
+{
+    return read_stream( is, value );
+}
+
+void json_spirit::read_or_throw( std::wistream& is, wValue& value )
+{
+    read_stream_or_throw( is, value );
+}
+
+bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value )
+{
+    return read_range( begin, end, value );
+}
+
+void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value )
+{
+    begin = read_range_or_throw( begin, end, value );
+}
+
+#endif
+
+bool json_spirit::read( const std::string& s, mValue& value )
+{
+    return read_string( s, value );
+}
+
+void json_spirit::read_or_throw( const std::string& s, mValue& value )
+{
+    read_string_or_throw( s, value );
+}
+
+bool json_spirit::read( std::istream& is, mValue& value )
+{
+    return read_stream( is, value );
+}
+
+void json_spirit::read_or_throw( std::istream& is, mValue& value )
+{
+    read_stream_or_throw( is, value );
+}
+
+bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value )
+{
+    return read_range( begin, end, value );
+}
+
+void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value )
+{
+    begin = read_range_or_throw( begin, end, value );
+}
+
+#ifndef BOOST_NO_STD_WSTRING
+
+bool json_spirit::read( const std::wstring& s, wmValue& value )
+{
+    return read_string( s, value );
+}
+
+void json_spirit::read_or_throw( const std::wstring& s, wmValue& value )
+{
+    read_string_or_throw( s, value );
+}
+
+bool json_spirit::read( std::wistream& is, wmValue& value )
+{
+    return read_stream( is, value );
+}
+
+void json_spirit::read_or_throw( std::wistream& is, wmValue& value )
+{
+    read_stream_or_throw( is, value );
+}
+
+bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value )
+{
+    return read_range( begin, end, value );
+}
+
+void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value )
+{
+    begin = read_range_or_throw( begin, end, value );
+}
+
+#endif

+ 58 - 0
ecoin/src/json/json_spirit_reader.h

@@ -0,0 +1,58 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef JSON_SPIRIT_READER
+#define JSON_SPIRIT_READER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+#include "json_spirit_value.h"
+#include "json_spirit_error_position.h"
+#include <iostream>
+
+namespace json_spirit
+{
+    // functions to reads a JSON values
+
+    bool read( const std::string& s, Value& value );
+    bool read( std::istream& is,     Value& value );
+    bool read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value );
+
+    void read_or_throw( const std::string& s, Value& value );  
+    void read_or_throw( std::istream& is,     Value& value );
+    void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value );
+
+#ifndef BOOST_NO_STD_WSTRING
+
+    bool read( const std::wstring& s, wValue& value );
+    bool read( std::wistream&  is,    wValue& value );
+    bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value );    
+
+    void read_or_throw( const std::wstring& s, wValue& value );
+    void read_or_throw( std::wistream& is,     wValue& value );
+    void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value );
+
+#endif
+
+    bool read( const std::string& s, mValue& value );
+    bool read( std::istream& is,     mValue& value );
+    bool read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value );
+
+    void read_or_throw( const std::string& s, mValue& value );  
+    void read_or_throw( std::istream& is,     mValue& value );
+    void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value );
+
+#ifndef BOOST_NO_STD_WSTRING
+
+    bool read( const std::wstring& s, wmValue& value );
+    bool read( std::wistream& is,     wmValue& value );
+    bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value );    
+
+    void read_or_throw( const std::wstring& s, wmValue& value );
+    void read_or_throw( std::wistream& is,     wmValue& value );
+    void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value );
+
+#endif
+}
+
+#endif

+ 592 - 0
ecoin/src/json/json_spirit_reader_template.h

@@ -0,0 +1,592 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef JSON_SPIRIT_READER_TEMPLATE
+#define JSON_SPIRIT_READER_TEMPLATE
+
+#include "json_spirit_value.h"
+#include "json_spirit_error_position.h"
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/version.hpp>
+
+#if BOOST_VERSION >= 103800
+    #include <boost/spirit/include/classic_core.hpp>
+    #include <boost/spirit/include/classic_confix.hpp>
+    #include <boost/spirit/include/classic_escape_char.hpp>
+    #include <boost/spirit/include/classic_multi_pass.hpp>
+    #include <boost/spirit/include/classic_position_iterator.hpp>
+    #define spirit_namespace boost::spirit::classic
+#else
+    #include <boost/spirit/core.hpp>
+    #include <boost/spirit/utility/confix.hpp>
+    #include <boost/spirit/utility/escape_char.hpp>
+    #include <boost/spirit/iterator/multi_pass.hpp>
+    #include <boost/spirit/iterator/position_iterator.hpp>
+    #define spirit_namespace boost::spirit
+#endif
+
+namespace json_spirit
+{
+    const spirit_namespace::int_parser < boost::int64_t >  int64_p  = spirit_namespace::int_parser < boost::int64_t  >();
+    const spirit_namespace::uint_parser< boost::uint64_t > uint64_p = spirit_namespace::uint_parser< boost::uint64_t >();
+
+    template< class Iter_type >
+    bool is_eq( Iter_type first, Iter_type last, const char* c_str )
+    {
+        for( Iter_type i = first; i != last; ++i, ++c_str )
+        {
+            if( *c_str == 0 ) return false;
+
+            if( *i != *c_str ) return false;
+        }
+
+        return true;
+    }
+
+    template< class Char_type >
+    Char_type hex_to_num( const Char_type c )
+    {
+        if( ( c >= '0' ) && ( c <= '9' ) ) return c - '0';
+        if( ( c >= 'a' ) && ( c <= 'f' ) ) return c - 'a' + 10;
+        if( ( c >= 'A' ) && ( c <= 'F' ) ) return c - 'A' + 10;
+        return 0;
+    }
+
+    template< class Char_type, class Iter_type >
+    Char_type hex_str_to_char( Iter_type& begin )
+    {
+        const Char_type c1( *( ++begin ) );
+        const Char_type c2( *( ++begin ) );
+
+        return ( hex_to_num( c1 ) << 4 ) + hex_to_num( c2 );
+    }       
+
+    template< class Char_type, class Iter_type >
+    Char_type unicode_str_to_char( Iter_type& begin )
+    {
+        const Char_type c1( *( ++begin ) );
+        const Char_type c2( *( ++begin ) );
+        const Char_type c3( *( ++begin ) );
+        const Char_type c4( *( ++begin ) );
+
+        return ( hex_to_num( c1 ) << 12 ) + 
+               ( hex_to_num( c2 ) <<  8 ) + 
+               ( hex_to_num( c3 ) <<  4 ) + 
+               hex_to_num( c4 );
+    }
+
+    template< class String_type >
+    void append_esc_char_and_incr_iter( String_type& s, 
+                                        typename String_type::const_iterator& begin, 
+                                        typename String_type::const_iterator end )
+    {
+        typedef typename String_type::value_type Char_type;
+             
+        const Char_type c2( *begin );
+
+        switch( c2 )
+        {
+            case 't':  s += '\t'; break;
+            case 'b':  s += '\b'; break;
+            case 'f':  s += '\f'; break;
+            case 'n':  s += '\n'; break;
+            case 'r':  s += '\r'; break;
+            case '\\': s += '\\'; break;
+            case '/':  s += '/';  break;
+            case '"':  s += '"';  break;
+            case 'x':  
+            {
+                if( end - begin >= 3 )  //  expecting "xHH..."
+                {
+                    s += hex_str_to_char< Char_type >( begin );  
+                }
+                break;
+            }
+            case 'u':  
+            {
+                if( end - begin >= 5 )  //  expecting "uHHHH..."
+                {
+                    s += unicode_str_to_char< Char_type >( begin );  
+                }
+                break;
+            }
+        }
+    }
+
+    template< class String_type >
+    String_type substitute_esc_chars( typename String_type::const_iterator begin, 
+                                   typename String_type::const_iterator end )
+    {
+        typedef typename String_type::const_iterator Iter_type;
+
+        if( end - begin < 2 ) return String_type( begin, end );
+
+        String_type result;
+        
+        result.reserve( end - begin );
+
+        const Iter_type end_minus_1( end - 1 );
+
+        Iter_type substr_start = begin;
+        Iter_type i = begin;
+
+        for( ; i < end_minus_1; ++i )
+        {
+            if( *i == '\\' )
+            {
+                result.append( substr_start, i );
+
+                ++i;  // skip the '\'
+             
+                append_esc_char_and_incr_iter( result, i, end );
+
+                substr_start = i + 1;
+            }
+        }
+
+        result.append( substr_start, end );
+
+        return result;
+    }
+
+    template< class String_type >
+    String_type get_str_( typename String_type::const_iterator begin, 
+                       typename String_type::const_iterator end )
+    {
+        assert( end - begin >= 2 );
+
+        typedef typename String_type::const_iterator Iter_type;
+
+        Iter_type str_without_quotes( ++begin );
+        Iter_type end_without_quotes( --end );
+
+        return substitute_esc_chars< String_type >( str_without_quotes, end_without_quotes );
+    }
+
+    inline std::string get_str( std::string::const_iterator begin, std::string::const_iterator end )
+    {
+        return get_str_< std::string >( begin, end );
+    }
+
+    inline std::wstring get_str( std::wstring::const_iterator begin, std::wstring::const_iterator end )
+    {
+        return get_str_< std::wstring >( begin, end );
+    }
+    
+    template< class String_type, class Iter_type >
+    String_type get_str( Iter_type begin, Iter_type end )
+    {
+        const String_type tmp( begin, end );  // convert multipass iterators to string iterators
+
+        return get_str( tmp.begin(), tmp.end() );
+    }
+
+    template< class Value_type, class Iter_type >
+    class Semantic_actions 
+    {
+    public:
+
+        typedef typename Value_type::Config_type Config_type;
+        typedef typename Config_type::String_type String_type;
+        typedef typename Config_type::Object_type Object_type;
+        typedef typename Config_type::Array_type Array_type;
+        typedef typename String_type::value_type Char_type;
+
+        Semantic_actions( Value_type& value )
+        :   value_( value )
+        ,   current_p_( 0 )
+        {
+        }
+
+        void begin_obj( Char_type c )
+        {
+            assert( c == '{' );
+
+            begin_compound< Object_type >();
+        }
+
+        void end_obj( Char_type c )
+        {
+            assert( c == '}' );
+
+            end_compound();
+        }
+
+        void begin_array( Char_type c )
+        {
+            assert( c == '[' );
+     
+            begin_compound< Array_type >();
+        }
+
+        void end_array( Char_type c )
+        {
+            assert( c == ']' );
+
+            end_compound();
+        }
+
+        void new_name( Iter_type begin, Iter_type end )
+        {
+            assert( current_p_->type() == obj_type );
+
+            name_ = get_str< String_type >( begin, end );
+        }
+
+        void new_str( Iter_type begin, Iter_type end )
+        {
+            add_to_current( get_str< String_type >( begin, end ) );
+        }
+
+        void new_true( Iter_type begin, Iter_type end )
+        {
+            assert( is_eq( begin, end, "true" ) );
+
+            add_to_current( true );
+        }
+
+        void new_false( Iter_type begin, Iter_type end )
+        {
+            assert( is_eq( begin, end, "false" ) );
+
+            add_to_current( false );
+        }
+
+        void new_null( Iter_type begin, Iter_type end )
+        {
+            assert( is_eq( begin, end, "null" ) );
+
+            add_to_current( Value_type() );
+        }
+
+        void new_int( boost::int64_t i )
+        {
+            add_to_current( i );
+        }
+
+        void new_uint64( boost::uint64_t ui )
+        {
+            add_to_current( ui );
+        }
+
+        void new_real( double d )
+        {
+            add_to_current( d );
+        }
+
+    private:
+
+        Semantic_actions& operator=( const Semantic_actions& ); // to prevent "assignment operator could not be generated" warning
+
+        Value_type* add_first( const Value_type& value )
+        {
+            assert( current_p_ == 0 );
+
+            value_ = value;
+            current_p_ = &value_;
+            return current_p_;
+        }
+
+        template< class Array_or_obj >
+        void begin_compound()
+        {
+            if( current_p_ == 0 )
+            {
+                add_first( Array_or_obj() );
+            }
+            else
+            {
+                stack_.push_back( current_p_ );
+
+                Array_or_obj new_array_or_obj;   // avoid copy by building new array or object in place
+
+                current_p_ = add_to_current( new_array_or_obj );
+            }
+        }
+
+        void end_compound()
+        {
+            if( current_p_ != &value_ )
+            {
+                current_p_ = stack_.back();
+                
+                stack_.pop_back();
+            }    
+        }
+
+        Value_type* add_to_current( const Value_type& value )
+        {
+            if( current_p_ == 0 )
+            {
+                return add_first( value );
+            }
+            else if( current_p_->type() == array_type )
+            {
+                current_p_->get_array().push_back( value );
+
+                return &current_p_->get_array().back(); 
+            }
+            
+            assert( current_p_->type() == obj_type );
+
+            return &Config_type::add( current_p_->get_obj(), name_, value );
+        }
+
+        Value_type& value_;             // this is the object or array that is being created
+        Value_type* current_p_;         // the child object or array that is currently being constructed
+
+        std::vector< Value_type* > stack_;   // previous child objects and arrays
+
+        String_type name_;              // of current name/value pair
+    };
+
+    template< typename Iter_type >
+    void throw_error( spirit_namespace::position_iterator< Iter_type > i, const std::string& reason )
+    {
+        throw Error_position( i.get_position().line, i.get_position().column, reason );
+    }
+
+    template< typename Iter_type >
+    void throw_error( Iter_type i, const std::string& reason )
+    {
+       throw reason;
+    }
+
+    template< class Value_type, class Iter_type >
+    class Json_grammer : public spirit_namespace::grammar< Json_grammer< Value_type, Iter_type > >
+    {
+    public:
+
+        typedef Semantic_actions< Value_type, Iter_type > Semantic_actions_t;
+
+        Json_grammer( Semantic_actions_t& semantic_actions )
+        :   actions_( semantic_actions )
+        {
+        }
+
+        static void throw_not_value( Iter_type begin, Iter_type end )
+        {
+    	    throw_error( begin, "not a value" );
+        }
+
+        static void throw_not_array( Iter_type begin, Iter_type end )
+        {
+    	    throw_error( begin, "not an array" );
+        }
+
+        static void throw_not_object( Iter_type begin, Iter_type end )
+        {
+    	    throw_error( begin, "not an object" );
+        }
+
+        static void throw_not_pair( Iter_type begin, Iter_type end )
+        {
+    	    throw_error( begin, "not a pair" );
+        }
+
+        static void throw_not_colon( Iter_type begin, Iter_type end )
+        {
+    	    throw_error( begin, "no colon in pair" );
+        }
+
+        static void throw_not_string( Iter_type begin, Iter_type end )
+        {
+    	    throw_error( begin, "not a string" );
+        }
+
+        template< typename ScannerT >
+        class definition
+        {
+        public:
+
+            definition( const Json_grammer& self )
+            {
+                using namespace spirit_namespace;
+
+                typedef typename Value_type::String_type::value_type Char_type;
+
+                typedef boost::function< void( Char_type )            > Char_action;
+                typedef boost::function< void( Iter_type, Iter_type ) > Str_action;
+                typedef boost::function< void( double )               > Real_action;
+                typedef boost::function< void( boost::int64_t )       > Int_action;
+                typedef boost::function< void( boost::uint64_t )      > Uint64_action;
+
+                Char_action   begin_obj  ( boost::bind( &Semantic_actions_t::begin_obj,   &self.actions_, _1 ) );
+                Char_action   end_obj    ( boost::bind( &Semantic_actions_t::end_obj,     &self.actions_, _1 ) );
+                Char_action   begin_array( boost::bind( &Semantic_actions_t::begin_array, &self.actions_, _1 ) );
+                Char_action   end_array  ( boost::bind( &Semantic_actions_t::end_array,   &self.actions_, _1 ) );
+                Str_action    new_name   ( boost::bind( &Semantic_actions_t::new_name,    &self.actions_, _1, _2 ) );
+                Str_action    new_str    ( boost::bind( &Semantic_actions_t::new_str,     &self.actions_, _1, _2 ) );
+                Str_action    new_true   ( boost::bind( &Semantic_actions_t::new_true,    &self.actions_, _1, _2 ) );
+                Str_action    new_false  ( boost::bind( &Semantic_actions_t::new_false,   &self.actions_, _1, _2 ) );
+                Str_action    new_null   ( boost::bind( &Semantic_actions_t::new_null,    &self.actions_, _1, _2 ) );
+                Real_action   new_real   ( boost::bind( &Semantic_actions_t::new_real,    &self.actions_, _1 ) );
+                Int_action    new_int    ( boost::bind( &Semantic_actions_t::new_int,     &self.actions_, _1 ) );
+                Uint64_action new_uint64 ( boost::bind( &Semantic_actions_t::new_uint64,  &self.actions_, _1 ) );
+
+                json_
+                    = value_ | eps_p[ &throw_not_value ]
+                    ;
+
+                value_
+                    = string_[ new_str ] 
+                    | number_ 
+                    | object_ 
+                    | array_ 
+                    | str_p( "true" ) [ new_true  ] 
+                    | str_p( "false" )[ new_false ] 
+                    | str_p( "null" ) [ new_null  ]
+                    ;
+
+                object_ 
+                    = ch_p('{')[ begin_obj ]
+                    >> !members_
+                    >> ( ch_p('}')[ end_obj ] | eps_p[ &throw_not_object ] )
+                    ;
+
+                members_
+                    = pair_ >> *( ',' >> pair_ )
+                    ;
+
+                pair_
+                    = string_[ new_name ]
+                    >> ( ':' | eps_p[ &throw_not_colon ] )
+                    >> ( value_ | eps_p[ &throw_not_value ] )
+                    ;
+
+                array_
+                    = ch_p('[')[ begin_array ]
+                    >> !elements_
+                    >> ( ch_p(']')[ end_array ] | eps_p[ &throw_not_array ] )
+                    ;
+
+                elements_
+                    = value_ >> *( ',' >> value_ )
+                    ;
+
+                string_ 
+                    = lexeme_d // this causes white space inside a string to be retained
+                      [
+                          confix_p
+                          ( 
+                              '"', 
+                              *lex_escape_ch_p,
+                              '"'
+                          ) 
+                      ]
+                    ;
+
+                number_
+                    = strict_real_p[ new_real   ] 
+                    | int64_p      [ new_int    ]
+                    | uint64_p     [ new_uint64 ]
+                    ;
+            }
+
+            spirit_namespace::rule< ScannerT > json_, object_, members_, pair_, array_, elements_, value_, string_, number_;
+
+            const spirit_namespace::rule< ScannerT >& start() const { return json_; }
+        };
+
+    private:
+
+        Json_grammer& operator=( const Json_grammer& ); // to prevent "assignment operator could not be generated" warning
+
+        Semantic_actions_t& actions_;
+    };
+
+    template< class Iter_type, class Value_type >
+    Iter_type read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value )
+    {
+        Semantic_actions< Value_type, Iter_type > semantic_actions( value );
+     
+        const spirit_namespace::parse_info< Iter_type > info = 
+                            spirit_namespace::parse( begin, end, 
+                                                    Json_grammer< Value_type, Iter_type >( semantic_actions ), 
+                                                    spirit_namespace::space_p );
+
+        if( !info.hit )
+        {
+            assert( false ); // in theory exception should already have been thrown
+            throw_error( info.stop, "error" );
+        }
+
+        return info.stop;
+    }
+
+    template< class Iter_type, class Value_type >
+    void add_posn_iter_and_read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value )
+    {
+        typedef spirit_namespace::position_iterator< Iter_type > Posn_iter_t;
+
+        const Posn_iter_t posn_begin( begin, end );
+        const Posn_iter_t posn_end( end, end );
+     
+        read_range_or_throw( posn_begin, posn_end, value );
+    }
+
+    template< class Iter_type, class Value_type >
+    bool read_range( Iter_type& begin, Iter_type end, Value_type& value )
+    {
+        try
+        {
+            begin = read_range_or_throw( begin, end, value );
+
+            return true;
+        }
+        catch( ... )
+        {
+            return false;
+        }
+    }
+
+    template< class String_type, class Value_type >
+    void read_string_or_throw( const String_type& s, Value_type& value )
+    {
+        add_posn_iter_and_read_range_or_throw( s.begin(), s.end(), value );
+    }
+
+    template< class String_type, class Value_type >
+    bool read_string( const String_type& s, Value_type& value )
+    {
+        typename String_type::const_iterator begin = s.begin();
+
+        return read_range( begin, s.end(), value );
+    }
+
+    template< class Istream_type >
+    struct Multi_pass_iters
+    {
+        typedef typename Istream_type::char_type Char_type;
+        typedef std::istream_iterator< Char_type, Char_type > istream_iter;
+        typedef spirit_namespace::multi_pass< istream_iter > Mp_iter;
+
+        Multi_pass_iters( Istream_type& is )
+        {
+            is.unsetf( std::ios::skipws );
+
+            begin_ = spirit_namespace::make_multi_pass( istream_iter( is ) );
+            end_   = spirit_namespace::make_multi_pass( istream_iter() );
+        }
+
+        Mp_iter begin_;
+        Mp_iter end_;
+    };
+
+    template< class Istream_type, class Value_type >
+    bool read_stream( Istream_type& is, Value_type& value )
+    {
+        Multi_pass_iters< Istream_type > mp_iters( is );
+
+        return read_range( mp_iters.begin_, mp_iters.end_, value );
+    }
+
+    template< class Istream_type, class Value_type >
+    void read_stream_or_throw( Istream_type& is, Value_type& value )
+    {
+        const Multi_pass_iters< Istream_type > mp_iters( is );
+
+        add_posn_iter_and_read_range_or_throw( mp_iters.begin_, mp_iters.end_, value );
+    }
+}
+
+#endif

+ 62 - 0
ecoin/src/json/json_spirit_stream_reader.h

@@ -0,0 +1,62 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef JSON_SPIRIT_READ_STREAM
+#define JSON_SPIRIT_READ_STREAM
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+#include "json_spirit_reader_template.h"
+
+namespace json_spirit
+{
+    template< class Istream_type, class Value_type >
+    class Stream_reader
+    {
+    public:
+
+        Stream_reader( Istream_type& is )
+        :   iters_( is )
+        {
+        }
+
+        bool read_next( Value_type& value )
+        {
+            return read_range( iters_.begin_, iters_.end_, value );
+        }
+
+    private:
+
+        typedef Multi_pass_iters< Istream_type > Mp_iters;
+
+        Mp_iters iters_;
+    };
+
+    template< class Istream_type, class Value_type >
+    class Stream_reader_thrower
+    {
+    public:
+
+        Stream_reader_thrower( Istream_type& is )
+        :   iters_( is )
+        ,    posn_begin_( iters_.begin_, iters_.end_ )
+        ,    posn_end_( iters_.end_, iters_.end_ )
+        {
+        }
+
+        void read_next( Value_type& value )
+        {
+            posn_begin_ = read_range_or_throw( posn_begin_, posn_end_, value );
+        }
+
+    private:
+
+        typedef Multi_pass_iters< Istream_type > Mp_iters;
+        typedef spirit_namespace::position_iterator< typename Mp_iters::Mp_iter > Posn_iter_t;
+
+        Mp_iters iters_;
+        Posn_iter_t posn_begin_, posn_end_;
+    };
+}
+
+#endif

+ 57 - 0
ecoin/src/json/json_spirit_utils.h

@@ -0,0 +1,57 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef JSON_SPIRIT_UTILS
+#define JSON_SPIRIT_UTILS
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+#include "json_spirit_value.h"
+#include <map>
+
+namespace json_spirit
+{ 
+    template< class Obj_t, class Map_t >
+    void obj_to_map( const Obj_t& obj, Map_t& mp_obj )
+    {
+        mp_obj.clear();
+
+        for( typename Obj_t::const_iterator i = obj.begin(); i != obj.end(); ++i )
+        {
+            mp_obj[ i->name_ ] = i->value_;
+        }
+    }
+
+    template< class Obj_t, class Map_t >
+    void map_to_obj( const Map_t& mp_obj, Obj_t& obj )
+    {
+        obj.clear();
+
+        for( typename Map_t::const_iterator i = mp_obj.begin(); i != mp_obj.end(); ++i )
+        {
+            obj.push_back( typename Obj_t::value_type( i->first, i->second ) );
+        }
+    }
+
+    typedef std::map< std::string, Value > Mapped_obj;
+
+#ifndef BOOST_NO_STD_WSTRING
+    typedef std::map< std::wstring, wValue > wMapped_obj;
+#endif
+
+    template< class Object_type, class String_type >
+    const typename Object_type::value_type::Value_type& find_value( const Object_type& obj, const String_type& name )
+    {
+        for( typename Object_type::const_iterator i = obj.begin(); i != obj.end(); ++i )
+        {
+            if( i->name_ == name )
+            {
+                return i->value_;
+            }
+        }
+
+        return Object_type::value_type::Value_type::null;
+    }
+}
+
+#endif

+ 8 - 0
ecoin/src/json/json_spirit_value.cpp

@@ -0,0 +1,8 @@
+/* Copyright (c) 2007 John W Wilkinson
+
+   This source code can be used for any purpose as long as
+   this comment is retained. */
+
+// json spirit version 2.00
+
+#include "json_spirit_value.h"

+ 507 - 0
ecoin/src/json/json_spirit_value.h

@@ -0,0 +1,507 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef JSON_SPIRIT_VALUE
+#define JSON_SPIRIT_VALUE
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+#include <vector>
+#include <map>
+#include <string>
+#include <cassert>
+#include <sstream>
+#include <stdexcept>
+#include <boost/config.hpp> 
+#include <boost/cstdint.hpp> 
+#include <boost/shared_ptr.hpp> 
+#include <boost/variant.hpp> 
+
+namespace json_spirit
+{
+    enum Value_type{ obj_type, array_type, str_type, bool_type, int_type, real_type, null_type };
+    static const char* Value_type_name[]={"obj", "array", "str", "bool", "int", "real", "null"};
+
+    template< class Config >    // Config determines whether the value uses std::string or std::wstring and
+                                // whether JSON Objects are represented as vectors or maps
+    class Value_impl
+    {
+    public:
+
+        typedef Config Config_type;
+        typedef typename Config::String_type String_type;
+        typedef typename Config::Object_type Object;
+        typedef typename Config::Array_type Array;
+        typedef typename String_type::const_pointer Const_str_ptr;  // eg const char*
+
+        Value_impl();  // creates null value
+        Value_impl( Const_str_ptr      value ); 
+        Value_impl( const String_type& value );
+        Value_impl( const Object&      value );
+        Value_impl( const Array&       value );
+        Value_impl( bool               value );
+        Value_impl( int                value );
+        Value_impl( boost::int64_t     value );
+        Value_impl( boost::uint64_t    value );
+        Value_impl( double             value );
+
+        Value_impl( const Value_impl& other );
+
+        bool operator==( const Value_impl& lhs ) const;
+
+        Value_impl& operator=( const Value_impl& lhs );
+
+        Value_type type() const;
+
+        bool is_uint64() const;
+        bool is_null() const;
+
+        const String_type& get_str()    const;
+        const Object&      get_obj()    const;
+        const Array&       get_array()  const;
+        bool               get_bool()   const;
+        int                get_int()    const;
+        boost::int64_t     get_int64()  const;
+        boost::uint64_t    get_uint64() const;
+        double             get_real()   const;
+
+        Object& get_obj();
+        Array&  get_array();
+
+        template< typename T > T get_value() const;  // example usage: int    i = value.get_value< int >();
+                                                     // or             double d = value.get_value< double >();
+
+        static const Value_impl null;
+
+    private:
+
+        void check_type( const Value_type vtype ) const;
+
+        typedef boost::variant< String_type, 
+                                boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >, 
+                                bool, boost::int64_t, double > Variant;
+
+        Value_type type_;
+        Variant v_;
+        bool is_uint64_;
+    };
+
+    template< class Config >
+    struct Pair_impl
+    {
+        typedef typename Config::String_type String_type;
+        typedef typename Config::Value_type Value_type;
+
+        Pair_impl( const String_type& name, const Value_type& value );
+
+        bool operator==( const Pair_impl& lhs ) const;
+
+        String_type name_;
+        Value_type value_;
+    };
+
+    template< class String >
+    struct Config_vector
+    {
+        typedef String String_type;
+        typedef Value_impl< Config_vector > Value_type;
+        typedef Pair_impl < Config_vector > Pair_type;
+        typedef std::vector< Value_type > Array_type;
+        typedef std::vector< Pair_type > Object_type;
+
+        static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value )
+        {
+            obj.push_back( Pair_type( name , value ) );
+
+            return obj.back().value_;
+        }
+                
+        static String_type get_name( const Pair_type& pair )
+        {
+            return pair.name_;
+        }
+                
+        static Value_type get_value( const Pair_type& pair )
+        {
+            return pair.value_;
+        }
+    };
+
+    typedef Config_vector< std::string > Config;
+    typedef Config::Value_type  Value;
+    typedef Config::Pair_type   Pair;
+    typedef Config::Object_type Object;
+    typedef Config::Array_type  Array;
+
+#ifndef BOOST_NO_STD_WSTRING
+
+    typedef Config_vector< std::wstring > wConfig;
+
+    typedef wConfig::Value_type  wValue;
+    typedef wConfig::Pair_type   wPair;
+    typedef wConfig::Object_type wObject;
+    typedef wConfig::Array_type  wArray;
+#endif
+
+    template< class String >
+    struct Config_map
+    {
+        typedef String String_type;
+        typedef Value_impl< Config_map > Value_type;
+        typedef std::vector< Value_type > Array_type;
+        typedef std::map< String_type, Value_type > Object_type;
+        typedef typename Object_type::value_type Pair_type;
+
+        static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value )
+        {
+            return obj[ name ] = value;
+        }
+                
+        static String_type get_name( const Pair_type& pair )
+        {
+            return pair.first;
+        }
+                
+        static Value_type get_value( const Pair_type& pair )
+        {
+            return pair.second;
+        }
+    };
+
+    typedef Config_map< std::string > mConfig;
+    typedef mConfig::Value_type  mValue;
+    typedef mConfig::Object_type mObject;
+    typedef mConfig::Array_type  mArray;
+
+#ifndef BOOST_NO_STD_WSTRING
+
+    typedef Config_map< std::wstring > wmConfig;
+
+    typedef wmConfig::Value_type  wmValue;
+    typedef wmConfig::Object_type wmObject;
+    typedef wmConfig::Array_type  wmArray;
+
+#endif
+
+    template< class Config >
+    const Value_impl< Config > Value_impl< Config >::null;
+
+    template< class Config >
+    Value_impl< Config >::Value_impl()
+    :   type_( null_type )
+    ,   is_uint64_( false )
+    {
+    }
+
+    template< class Config >
+    Value_impl< Config >::Value_impl( const Const_str_ptr value )
+    :   type_( str_type )
+    ,   v_( String_type( value ) )
+    ,   is_uint64_( false )
+    {
+    }
+
+    template< class Config >
+    Value_impl< Config >::Value_impl( const String_type& value )
+    :   type_( str_type )
+    ,   v_( value )
+    ,   is_uint64_( false )
+    {
+    }
+
+    template< class Config >
+    Value_impl< Config >::Value_impl( const Object& value )
+    :   type_( obj_type )
+    ,   v_( value )
+    ,   is_uint64_( false )
+    {
+    }
+
+    template< class Config >
+    Value_impl< Config >::Value_impl( const Array& value )
+    :   type_( array_type )
+    ,   v_( value )
+    ,   is_uint64_( false )
+    {
+    }
+
+    template< class Config >
+    Value_impl< Config >::Value_impl( bool value )
+    :   type_( bool_type )
+    ,   v_( value )
+    ,   is_uint64_( false )
+    {
+    }
+
+    template< class Config >
+    Value_impl< Config >::Value_impl( int value )
+    :   type_( int_type )
+    ,   v_( static_cast< boost::int64_t >( value ) )
+    ,   is_uint64_( false )
+    {
+    }
+
+    template< class Config >
+    Value_impl< Config >::Value_impl( boost::int64_t value )
+    :   type_( int_type )
+    ,   v_( value )
+    ,   is_uint64_( false )
+    {
+    }
+
+    template< class Config >
+    Value_impl< Config >::Value_impl( boost::uint64_t value )
+    :   type_( int_type )
+    ,   v_( static_cast< boost::int64_t >( value ) )
+    ,   is_uint64_( true )
+    {
+    }
+
+    template< class Config >
+    Value_impl< Config >::Value_impl( double value )
+    :   type_( real_type )
+    ,   v_( value )
+    ,   is_uint64_( false )
+    {
+    }
+
+    template< class Config >
+    Value_impl< Config >::Value_impl( const Value_impl< Config >& other )
+    :   type_( other.type() )
+    ,   v_( other.v_ )
+    ,   is_uint64_( other.is_uint64_ )
+    {
+    }
+
+    template< class Config >
+    Value_impl< Config >& Value_impl< Config >::operator=( const Value_impl& lhs )
+    {
+        Value_impl tmp( lhs );
+
+        std::swap( type_, tmp.type_ );
+        std::swap( v_, tmp.v_ );
+        std::swap( is_uint64_, tmp.is_uint64_ );
+
+        return *this;
+    }
+
+    template< class Config >
+    bool Value_impl< Config >::operator==( const Value_impl& lhs ) const
+    {
+        if( this == &lhs ) return true;
+
+        if( type() != lhs.type() ) return false;
+
+        return v_ == lhs.v_; 
+    }
+
+    template< class Config >
+    Value_type Value_impl< Config >::type() const
+    {
+        return type_;
+    }
+
+    template< class Config >
+    bool Value_impl< Config >::is_uint64() const
+    {
+        return is_uint64_;
+    }
+
+    template< class Config >
+    bool Value_impl< Config >::is_null() const
+    {
+        return type() == null_type;
+    }
+
+    template< class Config >
+    void Value_impl< Config >::check_type( const Value_type vtype ) const
+    {
+        if( type() != vtype ) 
+        {
+            std::ostringstream os;
+
+            os << "value is type " << Value_type_name[type()] << ", expected " << Value_type_name[vtype];
+
+            throw std::runtime_error( os.str() );
+        }
+    }
+
+    template< class Config >
+    const typename Config::String_type& Value_impl< Config >::get_str() const
+    {
+        check_type(  str_type );
+
+        return *boost::get< String_type >( &v_ );
+    }
+
+    template< class Config >
+    const typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() const
+    {
+        check_type( obj_type );
+
+        return *boost::get< Object >( &v_ );
+    }
+     
+    template< class Config >
+    const typename Value_impl< Config >::Array& Value_impl< Config >::get_array() const
+    {
+        check_type(  array_type );
+
+        return *boost::get< Array >( &v_ );
+    }
+     
+    template< class Config >
+    bool Value_impl< Config >::get_bool() const
+    {
+        check_type(  bool_type );
+
+        return boost::get< bool >( v_ );
+    }
+     
+    template< class Config >
+    int Value_impl< Config >::get_int() const
+    {
+        check_type(  int_type );
+
+        return static_cast< int >( get_int64() );
+    }
+    
+    template< class Config >
+    boost::int64_t Value_impl< Config >::get_int64() const
+    {
+        check_type(  int_type );
+
+        return boost::get< boost::int64_t >( v_ );
+    }
+    
+    template< class Config >
+    boost::uint64_t Value_impl< Config >::get_uint64() const
+    {
+        check_type(  int_type );
+
+        return static_cast< boost::uint64_t >( get_int64() );
+    }
+
+    template< class Config >
+    double Value_impl< Config >::get_real() const
+    {
+        if( type() == int_type )
+        {
+            return is_uint64() ? static_cast< double >( get_uint64() )
+                               : static_cast< double >( get_int64() );
+        }
+
+        check_type(  real_type );
+
+        return boost::get< double >( v_ );
+    }
+
+    template< class Config >
+    typename Value_impl< Config >::Object& Value_impl< Config >::get_obj()
+    {
+        check_type(  obj_type );
+
+        return *boost::get< Object >( &v_ );
+    }
+
+    template< class Config >
+    typename Value_impl< Config >::Array& Value_impl< Config >::get_array()
+    {
+        check_type(  array_type );
+
+        return *boost::get< Array >( &v_ );
+    }
+
+    template< class Config >
+    Pair_impl< Config >::Pair_impl( const String_type& name, const Value_type& value )
+    :   name_( name )
+    ,   value_( value )
+    {
+    }
+
+    template< class Config >
+    bool Pair_impl< Config >::operator==( const Pair_impl< Config >& lhs ) const
+    {
+        if( this == &lhs ) return true;
+
+        return ( name_ == lhs.name_ ) && ( value_ == lhs.value_ );
+    }
+
+    template < class String_type >
+    String_type to_str( const char* c_str )
+    {
+        String_type result;
+
+        for( const char* p = c_str; *p != 0; ++p )
+        {
+            result += *p;
+        }
+
+        return result;
+    }
+
+    namespace internal_
+    {
+        template< typename T >
+        struct Type_to_type
+        {
+        };
+
+        template< class Value > 
+        int get_value( const Value& value, Type_to_type< int > )
+        {
+            return value.get_int();
+        }
+       
+        template< class Value > 
+        boost::int64_t get_value( const Value& value, Type_to_type< boost::int64_t > )
+        {
+            return value.get_int64();
+        }
+       
+        template< class Value > 
+        boost::uint64_t get_value( const Value& value, Type_to_type< boost::uint64_t > )
+        {
+            return value.get_uint64();
+        }
+       
+        template< class Value > 
+        double get_value( const Value& value, Type_to_type< double > )
+        {
+            return value.get_real();
+        }
+       
+        template< class Value > 
+        typename Value::String_type get_value( const Value& value, Type_to_type< typename Value::String_type > )
+        {
+            return value.get_str();
+        }
+       
+        template< class Value > 
+        typename Value::Array get_value( const Value& value, Type_to_type< typename Value::Array > )
+        {
+            return value.get_array();
+        }
+       
+        template< class Value > 
+        typename Value::Object get_value( const Value& value, Type_to_type< typename Value::Object > )
+        {
+            return value.get_obj();
+        }
+       
+        template< class Value > 
+        bool get_value( const Value& value, Type_to_type< bool > )
+        {
+            return value.get_bool();
+        }
+    }
+
+    template< class Config >
+    template< typename T > 
+    T Value_impl< Config >::get_value() const
+    {
+        return internal_::get_value( *this, internal_::Type_to_type< T >() );
+    }
+}
+
+#endif

+ 91 - 0
ecoin/src/json/json_spirit_writer.cpp

@@ -0,0 +1,91 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#include "json_spirit_writer.h"
+#include "json_spirit_writer_template.h"
+
+void json_spirit::write( const Value& value, std::ostream& os )
+{
+    write_stream( value, os, false );
+}
+
+void json_spirit::write_formatted( const Value& value, std::ostream& os )
+{
+    write_stream( value, os, true );
+}
+
+std::string json_spirit::write( const Value& value )
+{
+    return write_string( value, false );
+}
+
+std::string json_spirit::write_formatted( const Value& value )
+{
+    return write_string( value, true );
+}
+
+#ifndef BOOST_NO_STD_WSTRING
+
+void json_spirit::write( const wValue& value, std::wostream& os )
+{
+    write_stream( value, os, false );
+}
+
+void json_spirit::write_formatted( const wValue& value, std::wostream& os )
+{
+    write_stream( value, os, true );
+}
+
+std::wstring json_spirit::write( const wValue&  value )
+{
+    return write_string( value, false );
+}
+
+std::wstring json_spirit::write_formatted( const wValue&  value )
+{
+    return write_string( value, true );
+}
+
+#endif
+
+void json_spirit::write( const mValue& value, std::ostream& os )
+{
+    write_stream( value, os, false );
+}
+
+void json_spirit::write_formatted( const mValue& value, std::ostream& os )
+{
+    write_stream( value, os, true );
+}
+
+std::string json_spirit::write( const mValue& value )
+{
+    return write_string( value, false );
+}
+
+std::string json_spirit::write_formatted( const mValue& value )
+{
+    return write_string( value, true );
+}
+
+#ifndef BOOST_NO_STD_WSTRING
+
+void json_spirit::write( const wmValue& value, std::wostream& os )
+{
+    write_stream( value, os, false );
+}
+
+void json_spirit::write_formatted( const wmValue& value, std::wostream& os )
+{
+    write_stream( value, os, true );
+}
+
+std::wstring json_spirit::write( const wmValue&  value )
+{
+    return write_string( value, false );
+}
+
+std::wstring json_spirit::write_formatted( const wmValue&  value )
+{
+    return write_string( value, true );
+}
+
+#endif

+ 43 - 0
ecoin/src/json/json_spirit_writer.h

@@ -0,0 +1,43 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef JSON_SPIRIT_WRITER
+#define JSON_SPIRIT_WRITER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+#include "json_spirit_value.h"
+#include <iostream>
+
+namespace json_spirit
+{
+    void         write          ( const Value& value, std::ostream&  os );
+    void         write_formatted( const Value& value, std::ostream&  os );
+    std::string  write          ( const Value& value );
+    std::string  write_formatted( const Value& value );
+
+#ifndef BOOST_NO_STD_WSTRING
+
+    void         write          ( const wValue& value, std::wostream& os );
+    void         write_formatted( const wValue& value, std::wostream& os );
+    std::wstring write          ( const wValue& value );
+    std::wstring write_formatted( const wValue& value );
+
+#endif
+
+    void         write          ( const mValue& value, std::ostream&  os );
+    void         write_formatted( const mValue& value, std::ostream&  os );
+    std::string  write          ( const mValue& value );
+    std::string  write_formatted( const mValue& value );
+
+#ifndef BOOST_NO_STD_WSTRING
+
+    void         write          ( const wmValue& value, std::wostream& os );
+    void         write_formatted( const wmValue& value, std::wostream& os );
+    std::wstring write          ( const wmValue& value );
+    std::wstring write_formatted( const wmValue& value );
+
+#endif
+}
+
+#endif

+ 238 - 0
ecoin/src/json/json_spirit_writer_template.h

@@ -0,0 +1,238 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef JSON_SPIRIT_WRITER_TEMPLATE
+#define JSON_SPIRIT_WRITER_TEMPLATE
+
+#include "json_spirit_value.h"
+#include <cassert>
+#include <sstream>
+#include <iomanip>
+
+namespace json_spirit
+{
+    inline char to_hex_char( unsigned int c )
+    {
+        assert( c <= 0xF );
+
+        const char ch = static_cast< char >( c );
+
+        if( ch < 10 ) return '0' + ch;
+
+        return 'A' - 10 + ch;
+    }
+
+    template< class String_type >
+    String_type non_printable_to_string( unsigned int c )
+    {
+        typedef typename String_type::value_type Char_type;
+
+        String_type result( 6, '\\' );
+
+        result[1] = 'u';
+
+        result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4;
+        result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4;
+        result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4;
+        result[ 2 ] = to_hex_char( c & 0x000F );
+
+        return result;
+    }
+
+    template< typename Char_type, class String_type >
+    bool add_esc_char( Char_type c, String_type& s )
+    {
+        switch( c )
+        {
+            case '"':  s += to_str< String_type >( "\\\"" ); return true;
+            case '\\': s += to_str< String_type >( "\\\\" ); return true;
+            case '\b': s += to_str< String_type >( "\\b"  ); return true;
+            case '\f': s += to_str< String_type >( "\\f"  ); return true;
+            case '\n': s += to_str< String_type >( "\\n"  ); return true;
+            case '\r': s += to_str< String_type >( "\\r"  ); return true;
+            case '\t': s += to_str< String_type >( "\\t"  ); return true;
+        }
+
+        return false;
+    }
+
+    template< class String_type >
+    String_type add_esc_chars( const String_type& s )
+    {
+        typedef typename String_type::const_iterator Iter_type;
+        typedef typename String_type::value_type     Char_type;
+
+        String_type result;
+
+        const Iter_type end( s.end() );
+
+        for( Iter_type i = s.begin(); i != end; ++i )
+        {
+            const Char_type c( *i );
+
+            if( add_esc_char( c, result ) ) continue;
+
+            const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c );
+
+            if( iswprint( unsigned_c ) )
+            {
+                result += c;
+            }
+            else
+            {
+                result += non_printable_to_string< String_type >( unsigned_c );
+            }
+        }
+
+        return result;
+    }
+
+    template< class Value_type, class Ostream_type >
+    class Generator
+    {
+        typedef typename Value_type::Config_type Config_type;
+        typedef typename Config_type::String_type String_type;
+        typedef typename Config_type::Object_type Object_type;
+        typedef typename Config_type::Array_type Array_type;
+        typedef typename String_type::value_type Char_type;
+        typedef typename Object_type::value_type Obj_member_type;
+
+    public:
+
+        Generator( const Value_type& value, Ostream_type& os, bool pretty )
+        :   os_( os )
+        ,   indentation_level_( 0 )
+        ,   pretty_( pretty )
+        {
+            output( value );
+        }
+
+    private:
+
+        void output( const Value_type& value )
+        {
+            switch( value.type() )
+            {
+                case obj_type:   output( value.get_obj() );   break;
+                case array_type: output( value.get_array() ); break;
+                case str_type:   output( value.get_str() );   break;
+                case bool_type:  output( value.get_bool() );  break;
+                case int_type:   output_int( value );         break;
+                case real_type:  os_ << std::showpoint << std::fixed << std::setprecision(8)
+                                     << value.get_real();     break;
+
+                case null_type:  os_ << "null";               break;
+                default: assert( false );
+            }
+        }
+
+        void output( const Object_type& obj )
+        {
+            output_array_or_obj( obj, '{', '}' );
+        }
+
+        void output( const Array_type& arr )
+        {
+            output_array_or_obj( arr, '[', ']' );
+        }
+
+        void output( const Obj_member_type& member )
+        {
+            output( Config_type::get_name( member ) ); space(); 
+            os_ << ':'; space(); 
+            output( Config_type::get_value( member ) );
+        }
+
+        void output_int( const Value_type& value )
+        {
+            if( value.is_uint64() )
+            {
+                os_ << value.get_uint64();
+            }
+            else
+            {
+               os_ << value.get_int64();
+            }
+        }
+
+        void output( const String_type& s )
+        {
+            os_ << '"' << add_esc_chars( s ) << '"';
+        }
+
+        void output( bool b )
+        {
+            os_ << to_str< String_type >( b ? "true" : "false" );
+        }
+
+        template< class T >
+        void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char )
+        {
+            os_ << start_char; new_line();
+
+            ++indentation_level_;
+            
+            for( typename T::const_iterator i = t.begin(); i != t.end(); ++i )
+            {
+                indent(); output( *i );
+
+                typename T::const_iterator next = i;
+
+                if( ++next != t.end())
+                {
+                    os_ << ',';
+                }
+
+                new_line();
+            }
+
+            --indentation_level_;
+
+            indent(); os_ << end_char;
+        }
+        
+        void indent()
+        {
+            if( !pretty_ ) return;
+
+            for( int i = 0; i < indentation_level_; ++i )
+            { 
+                os_ << "    ";
+            }
+        }
+
+        void space()
+        {
+            if( pretty_ ) os_ << ' ';
+        }
+
+        void new_line()
+        {
+            if( pretty_ ) os_ << '\n';
+        }
+
+        Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning
+
+        Ostream_type& os_;
+        int indentation_level_;
+        bool pretty_;
+    };
+
+    template< class Value_type, class Ostream_type >
+    void write_stream( const Value_type& value, Ostream_type& os, bool pretty )
+    {
+        Generator< Value_type, Ostream_type >( value, os, pretty );
+    }
+
+    template< class Value_type >
+    typename Value_type::String_type write_string( const Value_type& value, bool pretty )
+    {
+        typedef typename Value_type::String_type::value_type Char_type;
+
+        std::basic_ostringstream< Char_type > os;
+
+        write_stream( value, os, pretty );
+
+        return os.str();
+    }
+}
+
+#endif

+ 308 - 0
ecoin/src/kernel.cpp

@@ -0,0 +1,308 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#include <boost/assign/list_of.hpp>
+#include "kernel.h"
+#include "txdb.h"
+
+using namespace std;
+
+extern int nStakeMaxAge;
+extern int nStakeTargetSpacing;
+
+typedef std::map<int, unsigned int> MapModifierCheckpoints;
+
+static std::map<int, unsigned int> mapStakeModifierCheckpoints =
+    boost::assign::map_list_of
+        ( 0, 0x0e00670b )
+    ;
+
+static std::map<int, unsigned int> mapStakeModifierCheckpointsTestNet =
+    boost::assign::map_list_of
+        ( 0, 0x0e00670b )
+    ;
+
+int64 GetWeight(int64 nIntervalBeginning, int64 nIntervalEnd)
+{
+    return min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, (int64)nStakeMaxAge);
+}
+
+static bool GetLastStakeModifier(const CBlockIndex* pindex, uint64& nStakeModifier, int64& nModifierTime)
+{
+    if (!pindex)
+        return error("GetLastStakeModifier: null pindex");
+    while (pindex && pindex->pprev && !pindex->GeneratedStakeModifier())
+        pindex = pindex->pprev;
+    if (!pindex->GeneratedStakeModifier())
+        return error("GetLastStakeModifier: no generation at genesis block");
+    nStakeModifier = pindex->nStakeModifier;
+    nModifierTime = pindex->GetBlockTime();
+    return true;
+}
+
+static int64 GetStakeModifierSelectionIntervalSection(int nSection)
+{
+    assert (nSection >= 0 && nSection < 64);
+    return (nModifierInterval * 63 / (63 + ((63 - nSection) * (MODIFIER_INTERVAL_RATIO - 1))));
+}
+
+static int64 GetStakeModifierSelectionInterval()
+{
+    int64 nSelectionInterval = 0;
+    for (int nSection=0; nSection<64; nSection++)
+        nSelectionInterval += GetStakeModifierSelectionIntervalSection(nSection);
+    return nSelectionInterval;
+}
+
+static bool SelectBlockFromCandidates(vector<pair<int64, uint256> >& vSortedByTimestamp, map<uint256, const CBlockIndex*>& mapSelectedBlocks,
+    int64 nSelectionIntervalStop, uint64 nStakeModifierPrev, const CBlockIndex** pindexSelected)
+{
+    bool fSelected = false;
+    uint256 hashBest = 0;
+    *pindexSelected = (const CBlockIndex*) 0;
+    BOOST_FOREACH(const PAIRTYPE(int64, uint256)& item, vSortedByTimestamp)
+    {
+        if (!mapBlockIndex.count(item.second))
+            return error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString().c_str());
+        const CBlockIndex* pindex = mapBlockIndex[item.second];
+        if (fSelected && pindex->GetBlockTime() > nSelectionIntervalStop)
+            break;
+        if (mapSelectedBlocks.count(pindex->GetBlockHash()) > 0)
+            continue;
+        uint256 hashProof = pindex->IsProofOfStake()? pindex->hashProofOfStake : pindex->GetBlockHash();
+        CDataStream ss(SER_GETHASH, 0);
+        ss << hashProof << nStakeModifierPrev;
+        uint256 hashSelection = Hash(ss.begin(), ss.end());
+        if (pindex->IsProofOfStake())
+            hashSelection >>= 32;
+        if (fSelected && hashSelection < hashBest)
+        {
+            hashBest = hashSelection;
+            *pindexSelected = (const CBlockIndex*) pindex;
+        }
+        else if (!fSelected)
+        {
+            fSelected = true;
+            hashBest = hashSelection;
+            *pindexSelected = (const CBlockIndex*) pindex;
+        }
+    }
+    if (fDebug && GetBoolArg("-printstakemodifier"))
+        printf("SelectBlockFromCandidates: selection hash=%s\n", hashBest.ToString().c_str());
+    return fSelected;
+}
+
+bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64& nStakeModifier, bool& fGeneratedStakeModifier)
+{
+    nStakeModifier = 0;
+    fGeneratedStakeModifier = false;
+    if (!pindexPrev)
+    {
+        fGeneratedStakeModifier = true;
+        return true;  // genesis block's modifier is 0
+    }
+    int64 nModifierTime = 0;
+    if (!GetLastStakeModifier(pindexPrev, nStakeModifier, nModifierTime))
+        return error("ComputeNextStakeModifier: unable to get last modifier");
+    if (fDebug)
+    {
+        printf("ComputeNextStakeModifier: prev modifier=0x%016" PRI64x" time=%s\n", nStakeModifier, DateTimeStrFormat(nModifierTime).c_str());
+    }
+    if (nModifierTime / nModifierInterval >= pindexPrev->GetBlockTime() / nModifierInterval)
+        return true;
+
+    vector<pair<int64, uint256> > vSortedByTimestamp;
+    vSortedByTimestamp.reserve(64 * nModifierInterval / nStakeTargetSpacing);
+    int64 nSelectionInterval = GetStakeModifierSelectionInterval();
+    int64 nSelectionIntervalStart = (pindexPrev->GetBlockTime() / nModifierInterval) * nModifierInterval - nSelectionInterval;
+    const CBlockIndex* pindex = pindexPrev;
+    while (pindex && pindex->GetBlockTime() >= nSelectionIntervalStart)
+    {
+        vSortedByTimestamp.push_back(make_pair(pindex->GetBlockTime(), pindex->GetBlockHash()));
+        pindex = pindex->pprev;
+    }
+    int nHeightFirstCandidate = pindex ? (pindex->nHeight + 1) : 0;
+    reverse(vSortedByTimestamp.begin(), vSortedByTimestamp.end());
+    sort(vSortedByTimestamp.begin(), vSortedByTimestamp.end());
+
+    uint64 nStakeModifierNew = 0;
+    int64 nSelectionIntervalStop = nSelectionIntervalStart;
+    map<uint256, const CBlockIndex*> mapSelectedBlocks;
+    for (int nRound=0; nRound<min(64, (int)vSortedByTimestamp.size()); nRound++)
+    {
+        nSelectionIntervalStop += GetStakeModifierSelectionIntervalSection(nRound);
+        if (!SelectBlockFromCandidates(vSortedByTimestamp, mapSelectedBlocks, nSelectionIntervalStop, nStakeModifier, &pindex))
+            return error("ComputeNextStakeModifier: unable to select block at round %d", nRound);
+        nStakeModifierNew |= (((uint64)pindex->GetStakeEntropyBit()) << nRound);
+        mapSelectedBlocks.insert(make_pair(pindex->GetBlockHash(), pindex));
+        if (fDebug && GetBoolArg("-printstakemodifier"))
+            printf("ComputeNextStakeModifier: selected round %d stop=%s height=%d bit=%d\n", nRound, DateTimeStrFormat(nSelectionIntervalStop).c_str(), pindex->nHeight, pindex->GetStakeEntropyBit());
+    }
+
+    if (fDebug && GetBoolArg("-printstakemodifier"))
+    {
+        string strSelectionMap = "";
+        strSelectionMap.insert(0, pindexPrev->nHeight - nHeightFirstCandidate + 1, '-');
+        pindex = pindexPrev;
+        while (pindex && pindex->nHeight >= nHeightFirstCandidate)
+        {
+            if (pindex->IsProofOfStake())
+                strSelectionMap.replace(pindex->nHeight - nHeightFirstCandidate, 1, "=");
+            pindex = pindex->pprev;
+        }
+        BOOST_FOREACH(const PAIRTYPE(uint256, const CBlockIndex*)& item, mapSelectedBlocks)
+        {
+            strSelectionMap.replace(item.second->nHeight - nHeightFirstCandidate, 1, item.second->IsProofOfStake()? "S" : "W");
+        }
+        printf("ComputeNextStakeModifier: selection height [%d, %d] map %s\n", nHeightFirstCandidate, pindexPrev->nHeight, strSelectionMap.c_str());
+    }
+    if (fDebug)
+    {
+        printf("ComputeNextStakeModifier: new modifier=0x%016" PRI64x" time=%s\n", nStakeModifierNew, DateTimeStrFormat(pindexPrev->GetBlockTime()).c_str());
+    }
+
+    nStakeModifier = nStakeModifierNew;
+    fGeneratedStakeModifier = true;
+    return true;
+}
+
+static bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64& nStakeModifier, int& nStakeModifierHeight, int64& nStakeModifierTime, bool fPrintProofOfStake)
+{
+    nStakeModifier = 0;
+    if (!mapBlockIndex.count(hashBlockFrom))
+        return error("GetKernelStakeModifier() : block not indexed");
+    const CBlockIndex* pindexFrom = mapBlockIndex[hashBlockFrom];
+    nStakeModifierHeight = pindexFrom->nHeight;
+    nStakeModifierTime = pindexFrom->GetBlockTime();
+    int64 nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval();
+    const CBlockIndex* pindex = pindexFrom;
+    while (nStakeModifierTime < pindexFrom->GetBlockTime() + nStakeModifierSelectionInterval)
+    {
+        if (!pindex->pnext)
+        {   // reached best block; may happen if node is behind on block chain
+            if (fPrintProofOfStake || (pindex->GetBlockTime() + nStakeMinAge - nStakeModifierSelectionInterval > GetAdjustedTime()))
+                return error("GetKernelStakeModifier() : reached best block %s at height %d from block %s",
+                    pindex->GetBlockHash().ToString().c_str(), pindex->nHeight, hashBlockFrom.ToString().c_str());
+            else
+                return false;
+        }
+        pindex = pindex->pnext;
+        if (pindex->GeneratedStakeModifier())
+        {
+            nStakeModifierHeight = pindex->nHeight;
+            nStakeModifierTime = pindex->GetBlockTime();
+        }
+    }
+    nStakeModifier = pindex->nStakeModifier;
+    return true;
+}
+
+bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake)
+{
+    if (nTimeTx < txPrev.nTime)  // Transaction timestamp violation
+        return error("CheckStakeKernelHash() : nTime violation");
+
+    unsigned int nTimeBlockFrom = blockFrom.GetBlockTime();
+    if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement
+        return error("CheckStakeKernelHash() : min age violation");
+
+    CBigNum bnTargetPerCoinDay;
+    bnTargetPerCoinDay.SetCompact(nBits);
+    int64 nValueIn = txPrev.vout[prevout.n].nValue;
+
+    uint256 hashBlockFrom = blockFrom.GetHash();
+
+    CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64)txPrev.nTime, (int64)nTimeTx) / COIN / (24 * 60 * 60);
+    targetProofOfStake = (bnCoinDayWeight * bnTargetPerCoinDay).getuint256();
+
+    // Calculate hash
+    CDataStream ss(SER_GETHASH, 0);
+    uint64 nStakeModifier = 0;
+    int nStakeModifierHeight = 0;
+    int64 nStakeModifierTime = 0;
+
+    if (!GetKernelStakeModifier(hashBlockFrom, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake))
+        return false;
+    ss << nStakeModifier;
+
+    ss << nTimeBlockFrom << nTxPrevOffset << txPrev.nTime << prevout.n << nTimeTx;
+    hashProofOfStake = Hash(ss.begin(), ss.end());
+    if (fPrintProofOfStake)
+    {
+        printf("CheckStakeKernelHash() : using modifier 0x%016" PRI64x" at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
+            nStakeModifier, nStakeModifierHeight,
+            DateTimeStrFormat(nStakeModifierTime).c_str(),
+            mapBlockIndex[hashBlockFrom]->nHeight,
+            DateTimeStrFormat(blockFrom.GetBlockTime()).c_str());
+        printf("CheckStakeKernelHash() : check modifier=0x%016" PRI64x" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
+            nStakeModifier,
+            nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx,
+            hashProofOfStake.ToString().c_str());
+    }
+
+    if (CBigNum(hashProofOfStake) > bnCoinDayWeight * bnTargetPerCoinDay)
+        return false;
+    if (fDebug && !fPrintProofOfStake)
+    {
+        printf("CheckStakeKernelHash() : using modifier 0x%016" PRI64x" at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
+            nStakeModifier, nStakeModifierHeight,
+            DateTimeStrFormat(nStakeModifierTime).c_str(),
+            mapBlockIndex[hashBlockFrom]->nHeight,
+            DateTimeStrFormat(blockFrom.GetBlockTime()).c_str());
+        printf("CheckStakeKernelHash() : pass modifier=0x%016" PRI64x" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
+            nStakeModifier,
+            nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx,
+            hashProofOfStake.ToString().c_str());
+    }
+    return true;
+}
+
+bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake, uint256& targetProofOfStake)
+{
+    if (!tx.IsCoinStake())
+        return error("CheckProofOfStake() : called on non-coinstake %s", tx.GetHash().ToString().c_str());
+
+    const CTxIn& txin = tx.vin[0];
+    CTxDB txdb("r");
+    CTransaction txPrev;
+    CTxIndex txindex;
+    if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
+        return tx.DoS(1, error("CheckProofOfStake() : INFO: read txPrev failed"));  // previous transaction not in main chain, may occur during initial download
+
+    if (!VerifySignature(txPrev, tx, 0, true, 0))
+        return tx.DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", tx.GetHash().ToString().c_str()));
+
+    CBlock block;
+    if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+        return fDebug? error("CheckProofOfStake() : read block failed") : false; // unable to read block of previous transaction
+
+    if (!CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, txPrev, txin.prevout, tx.nTime, hashProofOfStake, targetProofOfStake, fDebug))
+        return tx.DoS(1, error("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s", tx.GetHash().ToString().c_str(), hashProofOfStake.ToString().c_str())); // may occur during initial download or if behind on block chain sync
+
+    return true;
+}
+
+bool CheckCoinStakeTimestamp(int64 nTimeBlock, int64 nTimeTx)
+{
+    return (nTimeBlock == nTimeTx);
+}
+
+unsigned int GetStakeModifierChecksum(const CBlockIndex* pindex)
+{
+    assert (pindex->pprev || pindex->GetBlockHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet));
+    CDataStream ss(SER_GETHASH, 0);
+    if (pindex->pprev)
+        ss << pindex->pprev->nStakeModifierChecksum;
+    ss << pindex->nFlags << pindex->hashProofOfStake << pindex->nStakeModifier;
+    uint256 hashChecksum = Hash(ss.begin(), ss.end());
+    hashChecksum >>= (256 - 32);
+    return hashChecksum.Get64();
+}
+
+bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierChecksum)
+{
+    MapModifierCheckpoints& checkpoints = (fTestNet ? mapStakeModifierCheckpointsTestNet : mapStakeModifierCheckpoints);
+
+    if (checkpoints.count(nHeight)){
+        return nStakeModifierChecksum == checkpoints[nHeight];
+    }
+    return true;
+}

+ 37 - 0
ecoin/src/kernel.h

@@ -0,0 +1,37 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef ECOIN_KERNEL_H
+#define ECOIN_KERNEL_H
+
+#include "main.h"
+
+// MODIFIER_INTERVAL: time to elapse before new modifier is computed
+extern unsigned int nModifierInterval;
+
+// MODIFIER_INTERVAL_RATIO:
+// ratio of group interval length between the last group and the first group
+static const int MODIFIER_INTERVAL_RATIO = 3;
+
+// Compute the hash modifier for proof-of-stake
+bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64& nStakeModifier, bool& fGeneratedStakeModifier);
+
+// Check whether stake kernel meets hash target
+// Sets hashProofOfStake on success return
+bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake=false);
+
+// Check kernel hash target and coinstake signature
+// Sets hashProofOfStake on success return
+bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake, uint256& targetProofOfStake);
+
+// Check whether the coinstake timestamp meets protocol
+bool CheckCoinStakeTimestamp(int64 nTimeBlock, int64 nTimeTx);
+
+// Get stake modifier checksum
+unsigned int GetStakeModifierChecksum(const CBlockIndex* pindex);
+
+// Check stake modifier hard checkpoints
+bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierChecksum);
+
+// Get time weight using supplied timestamps
+int64 GetWeight(int64 nIntervalBeginning, int64 nIntervalEnd);
+
+#endif // ECOIN_KERNEL_H

+ 385 - 0
ecoin/src/key.cpp

@@ -0,0 +1,385 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#include <map>
+#include <openssl/ecdsa.h>
+#include <openssl/obj_mac.h>
+#include "key.h"
+
+int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
+{
+    int ok = 0;
+    BN_CTX *ctx = NULL;
+    EC_POINT *pub_key = NULL;
+
+    if (!eckey) return 0;
+
+    const EC_GROUP *group = EC_KEY_get0_group(eckey);
+
+    if ((ctx = BN_CTX_new()) == NULL)
+        goto err;
+
+    pub_key = EC_POINT_new(group);
+
+    if (pub_key == NULL)
+        goto err;
+
+    if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx))
+        goto err;
+
+    EC_KEY_set_private_key(eckey,priv_key);
+    EC_KEY_set_public_key(eckey,pub_key);
+
+    ok = 1;
+
+err:
+
+    if (pub_key)
+        EC_POINT_free(pub_key);
+    if (ctx != NULL)
+        BN_CTX_free(ctx);
+
+    return(ok);
+}
+
+int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check)
+{
+    if (!eckey) return 0;
+
+    int ret = 0;
+    BN_CTX *ctx = NULL;
+
+    BIGNUM *x = NULL;
+    BIGNUM *e = NULL;
+    BIGNUM *order = NULL;
+    BIGNUM *sor = NULL;
+    BIGNUM *eor = NULL;
+    BIGNUM *field = NULL;
+    EC_POINT *R = NULL;
+    EC_POINT *O = NULL;
+    EC_POINT *Q = NULL;
+    BIGNUM *rr = NULL;
+    BIGNUM *zero = NULL;
+    int n = 0;
+    int i = recid / 2;
+
+    const EC_GROUP *group = EC_KEY_get0_group(eckey);
+    if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; }
+    BN_CTX_start(ctx);
+    order = BN_CTX_get(ctx);
+    if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; }
+    x = BN_CTX_get(ctx);
+    if (!BN_copy(x, order)) { ret=-1; goto err; }
+    if (!BN_mul_word(x, i)) { ret=-1; goto err; }
+    if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; }
+    field = BN_CTX_get(ctx);
+    if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; }
+    if (BN_cmp(x, field) >= 0) { ret=0; goto err; }
+    if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
+    if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; }
+    if (check)
+    {
+        if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
+        if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; }
+        if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; }
+    }
+    if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
+    n = EC_GROUP_get_degree(group);
+    e = BN_CTX_get(ctx);
+    if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; }
+    if (8*msglen > n) BN_rshift(e, e, 8-(n & 7));
+    zero = BN_CTX_get(ctx);
+    if (!BN_zero(zero)) { ret=-1; goto err; }
+    if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; }
+    rr = BN_CTX_get(ctx);
+    if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; }
+    sor = BN_CTX_get(ctx);
+    if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; }
+    eor = BN_CTX_get(ctx);
+    if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; }
+    if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; }
+    if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; }
+
+    ret = 1;
+
+err:
+    if (ctx) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+    }
+    if (R != NULL) EC_POINT_free(R);
+    if (O != NULL) EC_POINT_free(O);
+    if (Q != NULL) EC_POINT_free(Q);
+    return ret;
+}
+
+void CKey::SetCompressedPubKey()
+{
+    EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
+    fCompressedPubKey = true;
+}
+
+void CKey::Reset()
+{
+    fCompressedPubKey = false;
+    if (pkey != NULL)
+        EC_KEY_free(pkey);
+    pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
+    if (pkey == NULL)
+        throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed");
+    fSet = false;
+}
+
+CKey::CKey()
+{
+    pkey = NULL;
+    Reset();
+}
+
+CKey::CKey(const CKey& b)
+{
+    pkey = EC_KEY_dup(b.pkey);
+    if (pkey == NULL)
+        throw key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed");
+    fSet = b.fSet;
+}
+
+CKey& CKey::operator=(const CKey& b)
+{
+    if (!EC_KEY_copy(pkey, b.pkey))
+        throw key_error("CKey::operator=(const CKey&) : EC_KEY_copy failed");
+    fSet = b.fSet;
+    return (*this);
+}
+
+CKey::~CKey()
+{
+    EC_KEY_free(pkey);
+}
+
+bool CKey::IsNull() const
+{
+    return !fSet;
+}
+
+bool CKey::IsCompressed() const
+{
+    return fCompressedPubKey;
+}
+
+void CKey::MakeNewKey(bool fCompressed)
+{
+    if (!EC_KEY_generate_key(pkey))
+        throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed");
+    if (fCompressed)
+        SetCompressedPubKey();
+    fSet = true;
+}
+
+bool CKey::SetPrivKey(const CPrivKey& vchPrivKey)
+{
+    const unsigned char* pbegin = &vchPrivKey[0];
+    if (d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size()))
+    {
+        if (EC_KEY_check_key(pkey))
+        {
+            fSet = true;
+            return true;
+        }
+    }
+    pkey = NULL;
+    Reset();
+    return false;
+}
+
+bool CKey::SetSecret(const CSecret& vchSecret, bool fCompressed)
+{
+    EC_KEY_free(pkey);
+    pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
+    if (pkey == NULL)
+        throw key_error("CKey::SetSecret() : EC_KEY_new_by_curve_name failed");
+    if (vchSecret.size() != 32)
+        throw key_error("CKey::SetSecret() : secret must be 32 bytes");
+    BIGNUM *bn = BN_bin2bn(&vchSecret[0],32,BN_new());
+    if (bn == NULL)
+        throw key_error("CKey::SetSecret() : BN_bin2bn failed");
+    if (!EC_KEY_regenerate_key(pkey,bn))
+    {
+        BN_clear_free(bn);
+        throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed");
+    }
+    BN_clear_free(bn);
+    fSet = true;
+    if (fCompressed || fCompressedPubKey)
+        SetCompressedPubKey();
+    return true;
+}
+
+CSecret CKey::GetSecret(bool &fCompressed) const
+{
+    CSecret vchRet;
+    vchRet.resize(32);
+    const BIGNUM *bn = EC_KEY_get0_private_key(pkey);
+    int nBytes = BN_num_bytes(bn);
+    if (bn == NULL)
+        throw key_error("CKey::GetSecret() : EC_KEY_get0_private_key failed");
+    int n=BN_bn2bin(bn,&vchRet[32 - nBytes]);
+    if (n != nBytes)
+        throw key_error("CKey::GetSecret(): BN_bn2bin failed");
+    fCompressed = fCompressedPubKey;
+    return vchRet;
+}
+
+CPrivKey CKey::GetPrivKey() const
+{
+    int nSize = i2d_ECPrivateKey(pkey, NULL);
+    if (!nSize)
+        throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed");
+    CPrivKey vchPrivKey(nSize, 0);
+    unsigned char* pbegin = &vchPrivKey[0];
+    if (i2d_ECPrivateKey(pkey, &pbegin) != nSize)
+        throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size");
+    return vchPrivKey;
+}
+
+bool CKey::SetPubKey(const CPubKey& vchPubKey)
+{
+    const unsigned char* pbegin = &vchPubKey.vchPubKey[0];
+    if (o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.vchPubKey.size()))
+    {
+        fSet = true;
+        if (vchPubKey.vchPubKey.size() == 33)
+            SetCompressedPubKey();
+        return true;
+    }
+    pkey = NULL;
+    Reset();
+    return false;
+}
+
+CPubKey CKey::GetPubKey() const
+{
+    int nSize = i2o_ECPublicKey(pkey, NULL);
+    if (!nSize)
+        throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed");
+    std::vector<unsigned char> vchPubKey(nSize, 0);
+    unsigned char* pbegin = &vchPubKey[0];
+    if (i2o_ECPublicKey(pkey, &pbegin) != nSize)
+        throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size");
+    return CPubKey(vchPubKey);
+}
+
+bool CKey::Sign(uint256 hash, std::vector<unsigned char>& vchSig)
+{
+    unsigned int nSize = ECDSA_size(pkey);
+    vchSig.resize(nSize); // Make sure it is big enough
+    if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], &nSize, pkey))
+    {
+        vchSig.clear();
+        return false;
+    }
+    vchSig.resize(nSize); // Shrink to fit actual size
+    return true;
+}
+
+bool CKey::SignCompact(uint256 hash, std::vector<unsigned char>& vchSig)
+{
+    bool fOk = false;
+    ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey);
+    if (sig==NULL)
+        return false;
+    vchSig.clear();
+    vchSig.resize(65,0);
+    int nBitsR = BN_num_bits(sig->r);
+    int nBitsS = BN_num_bits(sig->s);
+    if (nBitsR <= 256 && nBitsS <= 256)
+    {
+        int nRecId = -1;
+        for (int i=0; i<4; i++)
+        {
+            CKey keyRec;
+            keyRec.fSet = true;
+            if (fCompressedPubKey)
+                keyRec.SetCompressedPubKey();
+            if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1)
+                if (keyRec.GetPubKey() == this->GetPubKey())
+                {
+                    nRecId = i;
+                    break;
+                }
+        }
+
+        if (nRecId == -1)
+        {
+            ECDSA_SIG_free(sig);
+            throw key_error("CKey::SignCompact() : unable to construct recoverable key");
+        }
+
+        vchSig[0] = nRecId+27+(fCompressedPubKey ? 4 : 0);
+        BN_bn2bin(sig->r,&vchSig[33-(nBitsR+7)/8]);
+        BN_bn2bin(sig->s,&vchSig[65-(nBitsS+7)/8]);
+        fOk = true;
+    }
+    ECDSA_SIG_free(sig);
+    return fOk;
+}
+
+bool CKey::SetCompactSignature(uint256 hash, const std::vector<unsigned char>& vchSig)
+{
+    if (vchSig.size() != 65)
+        return false;
+    int nV = vchSig[0];
+    if (nV<27 || nV>=35)
+        return false;
+    ECDSA_SIG *sig = ECDSA_SIG_new();
+    BN_bin2bn(&vchSig[1],32,sig->r);
+    BN_bin2bn(&vchSig[33],32,sig->s);
+
+    EC_KEY_free(pkey);
+    pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
+    if (nV >= 31)
+    {
+        SetCompressedPubKey();
+        nV -= 4;
+    }
+    if (ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), nV - 27, 0) == 1)
+    {
+        fSet = true;
+        ECDSA_SIG_free(sig);
+        return true;
+    }
+    ECDSA_SIG_free(sig);
+    return false;
+}
+
+bool CKey::Verify(uint256 hash, const std::vector<unsigned char>& vchSig)
+{
+    if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1)
+        return false;
+
+    return true;
+}
+
+bool CKey::VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig)
+{
+    CKey key;
+    if (!key.SetCompactSignature(hash, vchSig))
+        return false;
+    if (GetPubKey() != key.GetPubKey())
+        return false;
+
+    return true;
+}
+
+bool CKey::IsValid()
+{
+    if (!fSet)
+        return false;
+
+    if (!EC_KEY_check_key(pkey))
+        return false;
+
+    bool fCompr;
+    CSecret secret = GetSecret(fCompr);
+    CKey key2;
+    key2.SetSecret(secret, fCompr);
+    return GetPubKey() == key2.GetPubKey();
+}

+ 119 - 0
ecoin/src/key.h

@@ -0,0 +1,119 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef ECOIN_KEY_H
+#define ECOIN_KEY_H
+
+#include <stdexcept>
+#include <vector>
+
+#include "allocators.h"
+#include "serialize.h"
+#include "uint256.h"
+#include "util.h"
+
+#include <openssl/ec.h> // for EC_KEY definition
+
+class key_error : public std::runtime_error
+{
+public:
+    explicit key_error(const std::string& str) : std::runtime_error(str) {}
+};
+
+class CKeyID : public uint160
+{
+public:
+    CKeyID() : uint160(0) { }
+    CKeyID(const uint160 &in) : uint160(in) { }
+};
+
+class CScriptID : public uint160
+{
+public:
+    CScriptID() : uint160(0) { }
+    CScriptID(const uint160 &in) : uint160(in) { }
+};
+
+class CPubKey {
+private:
+    std::vector<unsigned char> vchPubKey;
+    friend class CKey;
+
+public:
+    CPubKey() { }
+    CPubKey(const std::vector<unsigned char> &vchPubKeyIn) : vchPubKey(vchPubKeyIn) { }
+    friend bool operator==(const CPubKey &a, const CPubKey &b) { return a.vchPubKey == b.vchPubKey; }
+    friend bool operator!=(const CPubKey &a, const CPubKey &b) { return a.vchPubKey != b.vchPubKey; }
+    friend bool operator<(const CPubKey &a, const CPubKey &b) { return a.vchPubKey < b.vchPubKey; }
+
+    IMPLEMENT_SERIALIZE(
+        READWRITE(vchPubKey);
+    )
+
+    CKeyID GetID() const {
+        return CKeyID(Hash160(vchPubKey));
+    }
+
+    uint256 GetHash() const {
+        return Hash(vchPubKey.begin(), vchPubKey.end());
+    }
+
+    bool IsValid() const {
+        return vchPubKey.size() == 33 || vchPubKey.size() == 65;
+    }
+
+    bool IsCompressed() const {
+        return vchPubKey.size() == 33;
+    }
+
+    std::vector<unsigned char> Raw() const {
+        return vchPubKey;
+    }
+};
+
+
+typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
+typedef std::vector<unsigned char, secure_allocator<unsigned char> > CSecret;
+class CKey
+{
+protected:
+    EC_KEY* pkey;
+    bool fSet;
+    bool fCompressedPubKey;
+
+    void SetCompressedPubKey();
+
+public:
+
+    void Reset();
+
+    CKey();
+    CKey(const CKey& b);
+
+    CKey& operator=(const CKey& b);
+
+    ~CKey();
+
+    bool IsNull() const;
+    bool IsCompressed() const;
+
+    void MakeNewKey(bool fCompressed);
+    bool SetPrivKey(const CPrivKey& vchPrivKey);
+    bool SetSecret(const CSecret& vchSecret, bool fCompressed = false);
+    CSecret GetSecret(bool &fCompressed) const;
+    CPrivKey GetPrivKey() const;
+    bool SetPubKey(const CPubKey& vchPubKey);
+    CPubKey GetPubKey() const;
+
+    bool Sign(uint256 hash, std::vector<unsigned char>& vchSig);
+
+    bool SignCompact(uint256 hash, std::vector<unsigned char>& vchSig);
+
+    bool SetCompactSignature(uint256 hash, const std::vector<unsigned char>& vchSig);
+
+    bool Verify(uint256 hash, const std::vector<unsigned char>& vchSig);
+
+    bool VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig);
+
+    bool IsValid();
+};
+
+#endif

+ 217 - 0
ecoin/src/keystore.cpp

@@ -0,0 +1,217 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#include "keystore.h"
+#include "script.h"
+
+bool CKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
+{
+    CKey key;
+    if (!GetKey(address, key))
+        return false;
+    vchPubKeyOut = key.GetPubKey();
+    return true;
+}
+
+bool CBasicKeyStore::AddKey(const CKey& key)
+{
+    bool fCompressed = false;
+    CSecret secret = key.GetSecret(fCompressed);
+    {
+        LOCK(cs_KeyStore);
+        mapKeys[key.GetPubKey().GetID()] = make_pair(secret, fCompressed);
+    }
+    return true;
+}
+
+bool CBasicKeyStore::AddCScript(const CScript& redeemScript)
+{
+    {
+        LOCK(cs_KeyStore);
+        mapScripts[redeemScript.GetID()] = redeemScript;
+    }
+    return true;
+}
+
+bool CBasicKeyStore::HaveCScript(const CScriptID& hash) const
+{
+    bool result;
+    {
+        LOCK(cs_KeyStore);
+        result = (mapScripts.count(hash) > 0);
+    }
+    return result;
+}
+
+
+bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const
+{
+    {
+        LOCK(cs_KeyStore);
+        ScriptMap::const_iterator mi = mapScripts.find(hash);
+        if (mi != mapScripts.end())
+        {
+            redeemScriptOut = (*mi).second;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool CCryptoKeyStore::SetCrypted()
+{
+    {
+        LOCK(cs_KeyStore);
+        if (fUseCrypto)
+            return true;
+        if (!mapKeys.empty())
+            return false;
+        fUseCrypto = true;
+    }
+    return true;
+}
+
+bool CCryptoKeyStore::Lock()
+{
+    if (!SetCrypted())
+        return false;
+
+    {
+        LOCK(cs_KeyStore);
+        vMasterKey.clear();
+    }
+
+    NotifyStatusChanged(this);
+    return true;
+}
+
+bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
+{
+    {
+        LOCK(cs_KeyStore);
+        if (!SetCrypted())
+            return false;
+
+        CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
+        for (; mi != mapCryptedKeys.end(); ++mi)
+        {
+            const CPubKey &vchPubKey = (*mi).second.first;
+            const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
+            CSecret vchSecret;
+            if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret))
+                return false;
+            if (vchSecret.size() != 32)
+                return false;
+            CKey key;
+            key.SetPubKey(vchPubKey);
+            key.SetSecret(vchSecret);
+            if (key.GetPubKey() == vchPubKey)
+                break;
+            return false;
+        }
+        vMasterKey = vMasterKeyIn;
+    }
+    NotifyStatusChanged(this);
+    return true;
+}
+
+bool CCryptoKeyStore::AddKey(const CKey& key)
+{
+    {
+        LOCK(cs_KeyStore);
+        if (!IsCrypted())
+            return CBasicKeyStore::AddKey(key);
+
+        if (IsLocked())
+            return false;
+
+        std::vector<unsigned char> vchCryptedSecret;
+        CPubKey vchPubKey = key.GetPubKey();
+        bool fCompressed;
+        if (!EncryptSecret(vMasterKey, key.GetSecret(fCompressed), vchPubKey.GetHash(), vchCryptedSecret))
+            return false;
+
+        if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret))
+            return false;
+    }
+    return true;
+}
+
+
+bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
+{
+    {
+        LOCK(cs_KeyStore);
+        if (!SetCrypted())
+            return false;
+
+        mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
+    }
+    return true;
+}
+
+bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const
+{
+    {
+        LOCK(cs_KeyStore);
+        if (!IsCrypted())
+            return CBasicKeyStore::GetKey(address, keyOut);
+
+        CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
+        if (mi != mapCryptedKeys.end())
+        {
+            const CPubKey &vchPubKey = (*mi).second.first;
+            const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
+            CSecret vchSecret;
+            if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret))
+                return false;
+            if (vchSecret.size() != 32)
+                return false;
+            keyOut.SetPubKey(vchPubKey);
+            keyOut.SetSecret(vchSecret);
+            return true;
+        }
+    }
+    return false;
+}
+
+bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
+{
+    {
+        LOCK(cs_KeyStore);
+        if (!IsCrypted())
+            return CKeyStore::GetPubKey(address, vchPubKeyOut);
+
+        CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
+        if (mi != mapCryptedKeys.end())
+        {
+            vchPubKeyOut = (*mi).second.first;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
+{
+    {
+        LOCK(cs_KeyStore);
+        if (!mapCryptedKeys.empty() || IsCrypted())
+            return false;
+
+        fUseCrypto = true;
+        BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys)
+        {
+            CKey key;
+            if (!key.SetSecret(mKey.second.first, mKey.second.second))
+                return false;
+            const CPubKey vchPubKey = key.GetPubKey();
+            std::vector<unsigned char> vchCryptedSecret;
+            bool fCompressed;
+            if (!EncryptSecret(vMasterKeyIn, key.GetSecret(fCompressed), vchPubKey.GetHash(), vchCryptedSecret))
+                return false;
+            if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
+                return false;
+        }
+        mapKeys.clear();
+    }
+    return true;
+}

+ 160 - 0
ecoin/src/keystore.h

@@ -0,0 +1,160 @@
+// ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
+#ifndef ECOIN_KEYSTORE_H
+#define ECOIN_KEYSTORE_H
+
+#include "crypter.h"
+#include "sync.h"
+#include <boost/signals2/signal.hpp>
+
+class CScript;
+
+class CKeyStore
+{
+protected:
+    mutable CCriticalSection cs_KeyStore;
+
+public:
+    virtual ~CKeyStore() {}
+
+    virtual bool AddKey(const CKey& key) =0;
+    virtual bool HaveKey(const CKeyID &address) const =0;
+    virtual bool GetKey(const CKeyID &address, CKey& keyOut) const =0;
+    virtual void GetKeys(std::set<CKeyID> &setAddress) const =0;
+    virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const;
+    virtual bool AddCScript(const CScript& redeemScript) =0;
+    virtual bool HaveCScript(const CScriptID &hash) const =0;
+    virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const =0;
+    virtual bool GetSecret(const CKeyID &address, CSecret& vchSecret, bool &fCompressed) const
+    {
+        CKey key;
+        if (!GetKey(address, key))
+            return false;
+        vchSecret = key.GetSecret(fCompressed);
+        return true;
+    }
+};
+
+typedef std::map<CKeyID, std::pair<CSecret, bool> > KeyMap;
+typedef std::map<CScriptID, CScript > ScriptMap;
+
+class CBasicKeyStore : public CKeyStore
+{
+protected:
+    KeyMap mapKeys;
+    ScriptMap mapScripts;
+
+public:
+    bool AddKey(const CKey& key);
+    bool HaveKey(const CKeyID &address) const
+    {
+        bool result;
+        {
+            LOCK(cs_KeyStore);
+            result = (mapKeys.count(address) > 0);
+        }
+        return result;
+    }
+    void GetKeys(std::set<CKeyID> &setAddress) const
+    {
+        setAddress.clear();
+        {
+            LOCK(cs_KeyStore);
+            KeyMap::const_iterator mi = mapKeys.begin();
+            while (mi != mapKeys.end())
+            {
+                setAddress.insert((*mi).first);
+                mi++;
+            }
+        }
+    }
+    bool GetKey(const CKeyID &address, CKey &keyOut) const
+    {
+        {
+            LOCK(cs_KeyStore);
+            KeyMap::const_iterator mi = mapKeys.find(address);
+            if (mi != mapKeys.end())
+            {
+                keyOut.Reset();
+                keyOut.SetSecret((*mi).second.first, (*mi).second.second);
+                return true;
+            }
+        }
+        return false;
+    }
+    virtual bool AddCScript(const CScript& redeemScript);
+    virtual bool HaveCScript(const CScriptID &hash) const;
+    virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const;
+};
+
+typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap;
+
+class CCryptoKeyStore : public CBasicKeyStore
+{
+private:
+    CryptedKeyMap mapCryptedKeys;
+    CKeyingMaterial vMasterKey;
+    bool fUseCrypto;
+
+protected:
+    bool SetCrypted();
+    bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
+    bool Unlock(const CKeyingMaterial& vMasterKeyIn);
+
+public:
+    CCryptoKeyStore() : fUseCrypto(false)
+    {
+    }
+
+    bool IsCrypted() const
+    {
+        return fUseCrypto;
+    }
+
+    bool IsLocked() const
+    {
+        if (!IsCrypted())
+            return false;
+        bool result;
+        {
+            LOCK(cs_KeyStore);
+            result = vMasterKey.empty();
+        }
+        return result;
+    }
+
+    bool Lock();
+
+    virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
+    bool AddKey(const CKey& key);
+    bool HaveKey(const CKeyID &address) const
+    {
+        {
+            LOCK(cs_KeyStore);
+            if (!IsCrypted())
+                return CBasicKeyStore::HaveKey(address);
+            return mapCryptedKeys.count(address) > 0;
+        }
+        return false;
+    }
+    bool GetKey(const CKeyID &address, CKey& keyOut) const;
+    bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const;
+    void GetKeys(std::set<CKeyID> &setAddress) const
+    {
+        if (!IsCrypted())
+        {
+            CBasicKeyStore::GetKeys(setAddress);
+            return;
+        }
+        setAddress.clear();
+        CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
+        while (mi != mapCryptedKeys.end())
+        {
+            setAddress.insert((*mi).first);
+            mi++;
+        }
+    }
+
+    boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged;
+};
+
+#endif

+ 13 - 0
ecoin/src/leveldb/.gitignore

@@ -0,0 +1,13 @@
+build_config.mk
+*.a
+*.o
+*.dylib*
+*.so
+*.so.*
+*_test
+db_bench
+leveldbutil
+Release
+Debug
+Benchmark
+vs2010.*

+ 11 - 0
ecoin/src/leveldb/AUTHORS

@@ -0,0 +1,11 @@
+# Names should be added to this file like so:
+# Name or Organization <email address>
+
+Google Inc.
+
+# Initial version authors:
+Jeffrey Dean <jeff@google.com>
+Sanjay Ghemawat <sanjay@google.com>
+
+# Partial list of contributors:
+Kevin Regan <kevin.d.regan@gmail.com>

+ 27 - 0
ecoin/src/leveldb/LICENSE

@@ -0,0 +1,27 @@
+Copyright (c) 2011 The LevelDB Authors. 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 Google Inc. 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
+OWNER 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.

+ 206 - 0
ecoin/src/leveldb/Makefile

@@ -0,0 +1,206 @@
+# Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#-----------------------------------------------
+# Uncomment exactly one of the lines labelled (A), (B), and (C) below
+# to switch between compilation modes.
+
+OPT ?= -O2 -DNDEBUG       # (A) Production use (optimized mode)
+# OPT ?= -g2              # (B) Debug mode, w/ full line-level debugging symbols
+# OPT ?= -O2 -g2 -DNDEBUG # (C) Profiling mode: opt, but w/debugging symbols
+#-----------------------------------------------
+
+# detect what platform we're building on
+$(shell CC="$(CC)" CXX="$(CXX)" TARGET_OS="$(TARGET_OS)" \
+    sh ./build_detect_platform build_config.mk ./)
+# this file is generated by the previous line to set build flags and sources
+include build_config.mk
+
+CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT)
+CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT)
+
+LDFLAGS += $(PLATFORM_LDFLAGS)
+LIBS += $(PLATFORM_LIBS)
+
+LIBOBJECTS = $(SOURCES:.cc=.o)
+MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o)
+
+TESTUTIL = ./util/testutil.o
+TESTHARNESS = ./util/testharness.o $(TESTUTIL)
+
+TESTS = \
+	arena_test \
+	bloom_test \
+	c_test \
+	cache_test \
+	coding_test \
+	corruption_test \
+	crc32c_test \
+	db_test \
+	dbformat_test \
+	env_test \
+	filename_test \
+	filter_block_test \
+	issue178_test \
+	log_test \
+	memenv_test \
+	skiplist_test \
+	table_test \
+	version_edit_test \
+	version_set_test \
+	write_batch_test
+
+PROGRAMS = db_bench leveldbutil $(TESTS)
+BENCHMARKS = db_bench_sqlite3 db_bench_tree_db
+
+LIBRARY = libleveldb.a
+MEMENVLIBRARY = libmemenv.a
+
+default: all
+
+# Should we build shared libraries?
+ifneq ($(PLATFORM_SHARED_EXT),)
+
+ifneq ($(PLATFORM_SHARED_VERSIONED),true)
+SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT)
+SHARED2 = $(SHARED1)
+SHARED3 = $(SHARED1)
+SHARED = $(SHARED1)
+else
+# Update db.h if you change these.
+SHARED_MAJOR = 1
+SHARED_MINOR = 12
+SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT)
+SHARED2 = $(SHARED1).$(SHARED_MAJOR)
+SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR)
+SHARED = $(SHARED1) $(SHARED2) $(SHARED3)
+$(SHARED1): $(SHARED3)
+	ln -fs $(SHARED3) $(SHARED1)
+$(SHARED2): $(SHARED3)
+	ln -fs $(SHARED3) $(SHARED2)
+endif
+
+$(SHARED3):
+	$(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) -o $(SHARED3) $(LIBS)
+
+endif  # PLATFORM_SHARED_EXT
+
+all: $(SHARED) $(LIBRARY)
+
+check: all $(PROGRAMS) $(TESTS)
+	for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done
+
+clean:
+	-rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) */*.o */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk
+	-rm -rf ios-x86/* ios-arm/*
+
+$(LIBRARY): $(LIBOBJECTS)
+	rm -f $@
+	$(AR) -rs $@ $(LIBOBJECTS)
+
+db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL)
+	$(CXX) $(LDFLAGS) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LIBS)
+
+db_bench_sqlite3: doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL)
+	$(CXX) $(LDFLAGS) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lsqlite3 $(LIBS)
+
+db_bench_tree_db: doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL)
+	$(CXX) $(LDFLAGS) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lkyotocabinet $(LIBS)
+
+leveldbutil: db/leveldb_main.o $(LIBOBJECTS)
+	$(CXX) $(LDFLAGS) db/leveldb_main.o $(LIBOBJECTS) -o $@ $(LIBS)
+
+arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+issue178_test: issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(MEMENVLIBRARY) : $(MEMENVOBJECTS)
+	rm -f $@
+	$(AR) -rs $@ $(MEMENVOBJECTS)
+
+memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LIBS)
+
+ifeq ($(PLATFORM), IOS)
+# For iOS, create universal object files to be used on both the simulator and
+# a device.
+PLATFORMSROOT=/Applications/Xcode.app/Contents/Developer/Platforms
+SIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/Developer
+DEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/Developer
+IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString)
+
+.cc.o:
+	mkdir -p ios-x86/$(dir $@)
+	$(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@
+	mkdir -p ios-arm/$(dir $@)
+	$(DEVICEROOT)/usr/bin/$(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@
+	lipo ios-x86/$@ ios-arm/$@ -create -output $@
+
+.c.o:
+	mkdir -p ios-x86/$(dir $@)
+	$(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@
+	mkdir -p ios-arm/$(dir $@)
+	$(DEVICEROOT)/usr/bin/$(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@
+	lipo ios-x86/$@ ios-arm/$@ -create -output $@
+
+else
+.cc.o:
+	$(CXX) $(CXXFLAGS) -c $< -o $@
+
+.c.o:
+	$(CC) $(CFLAGS) -c $< -o $@
+endif

+ 17 - 0
ecoin/src/leveldb/NEWS

@@ -0,0 +1,17 @@
+Release 1.2 2011-05-16
+----------------------
+
+Fixes for larger databases (tested up to one billion 100-byte entries,
+i.e., ~100GB).
+
+(1) Place hard limit on number of level-0 files.  This fixes errors
+of the form "too many open files".
+
+(2) Fixed memtable management.  Before the fix, a heavy write burst
+could cause unbounded memory usage.
+
+A fix for a logging bug where the reader would incorrectly complain
+about corruption.
+
+Allow public access to WriteBatch contents so that users can easily
+wrap a DB.

+ 51 - 0
ecoin/src/leveldb/README

@@ -0,0 +1,51 @@
+leveldb: A key-value store
+Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com)
+
+The code under this directory implements a system for maintaining a
+persistent key/value store.
+
+See doc/index.html for more explanation.
+See doc/impl.html for a brief overview of the implementation.
+
+The public interface is in include/*.h.  Callers should not include or
+rely on the details of any other header files in this package.  Those
+internal APIs may be changed without warning.
+
+Guide to header files:
+
+include/db.h
+    Main interface to the DB: Start here
+
+include/options.h
+    Control over the behavior of an entire database, and also
+    control over the behavior of individual reads and writes.
+
+include/comparator.h
+    Abstraction for user-specified comparison function.  If you want
+    just bytewise comparison of keys, you can use the default comparator,
+    but clients can write their own comparator implementations if they
+    want custom ordering (e.g. to handle different character
+    encodings, etc.)
+
+include/iterator.h
+    Interface for iterating over data. You can get an iterator
+    from a DB object.
+
+include/write_batch.h
+    Interface for atomically applying multiple updates to a database.
+
+include/slice.h
+    A simple module for maintaining a pointer and a length into some
+    other byte array.
+
+include/status.h
+    Status is returned from many of the public interfaces and is used
+    to report success and various kinds of errors.
+
+include/env.h
+    Abstraction of the OS environment.  A posix implementation of
+    this interface is in util/env_posix.cc
+
+include/table.h
+include/table_builder.h
+    Lower-level modules that most clients probably won't use directly

+ 14 - 0
ecoin/src/leveldb/TODO

@@ -0,0 +1,14 @@
+ss
+- Stats
+
+db
+- Maybe implement DB::BulkDeleteForRange(start_key, end_key)
+  that would blow away files whose ranges are entirely contained
+  within [start_key..end_key]?  For Chrome, deletion of obsolete
+  object stores, etc. can be done in the background anyway, so
+  probably not that important.
+- There have been requests for MultiGet.
+
+After a range is completely deleted, what gets rid of the
+corresponding files if we do no future changes to that range.  Make
+the conditions for triggering compactions fire in more situations?

+ 39 - 0
ecoin/src/leveldb/WINDOWS.md

@@ -0,0 +1,39 @@
+# Building LevelDB On Windows
+
+## Prereqs 
+
+Install the [Windows Software Development Kit version 7.1](http://www.microsoft.com/downloads/dlx/en-us/listdetailsview.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b).
+
+Download and extract the [Snappy source distribution](http://snappy.googlecode.com/files/snappy-1.0.5.tar.gz)
+
+1. Open the "Windows SDK 7.1 Command Prompt" :
+   Start Menu -> "Microsoft Windows SDK v7.1" > "Windows SDK 7.1 Command Prompt"
+2. Change the directory to the leveldb project
+
+## Building the Static lib 
+
+* 32 bit Version 
+
+        setenv /x86
+        msbuild.exe /p:Configuration=Release /p:Platform=Win32 /p:Snappy=..\snappy-1.0.5
+
+* 64 bit Version 
+
+        setenv /x64
+        msbuild.exe /p:Configuration=Release /p:Platform=x64 /p:Snappy=..\snappy-1.0.5
+
+
+## Building and Running the Benchmark app
+
+* 32 bit Version 
+
+	    setenv /x86
+	    msbuild.exe /p:Configuration=Benchmark /p:Platform=Win32 /p:Snappy=..\snappy-1.0.5
+		Benchmark\leveldb.exe
+
+* 64 bit Version 
+
+	    setenv /x64
+	    msbuild.exe /p:Configuration=Benchmark /p:Platform=x64 /p:Snappy=..\snappy-1.0.5
+	    x64\Benchmark\leveldb.exe
+

+ 214 - 0
ecoin/src/leveldb/build_detect_platform

@@ -0,0 +1,214 @@
+#!/bin/sh
+#
+# Detects OS we're compiling on and outputs a file specified by the first
+# argument, which in turn gets read while processing Makefile.
+#
+# The output will set the following variables:
+#   CC                          C Compiler path
+#   CXX                         C++ Compiler path
+#   PLATFORM_LDFLAGS            Linker flags
+#   PLATFORM_LIBS               Libraries flags
+#   PLATFORM_SHARED_EXT         Extension for shared libraries
+#   PLATFORM_SHARED_LDFLAGS     Flags for building shared library
+#                               This flag is embedded just before the name
+#                               of the shared library without intervening spaces
+#   PLATFORM_SHARED_CFLAGS      Flags for compiling objects for shared library
+#   PLATFORM_CCFLAGS            C compiler flags
+#   PLATFORM_CXXFLAGS           C++ compiler flags.  Will contain:
+#   PLATFORM_SHARED_VERSIONED   Set to 'true' if platform supports versioned
+#                               shared libraries, empty otherwise.
+#
+# The PLATFORM_CCFLAGS and PLATFORM_CXXFLAGS might include the following:
+#
+#       -DLEVELDB_CSTDATOMIC_PRESENT if <cstdatomic> is present
+#       -DLEVELDB_PLATFORM_POSIX     for Posix-based platforms
+#       -DSNAPPY                     if the Snappy library is present
+#
+
+OUTPUT=$1
+PREFIX=$2
+if test -z "$OUTPUT" || test -z "$PREFIX"; then
+  echo "usage: $0 <output-filename> <directory_prefix>" >&2
+  exit 1
+fi
+
+# Delete existing output, if it exists
+rm -f $OUTPUT
+touch $OUTPUT
+
+if test -z "$CC"; then
+    CC=cc
+fi
+
+if test -z "$CXX"; then
+    CXX=g++
+fi
+
+if test -z "$TMPDIR"; then
+    TMPDIR=/tmp
+fi
+
+# Detect OS
+if test -z "$TARGET_OS"; then
+    TARGET_OS=`uname -s`
+fi
+
+COMMON_FLAGS=
+CROSS_COMPILE=
+PLATFORM_CCFLAGS=
+PLATFORM_CXXFLAGS=
+PLATFORM_LDFLAGS=
+PLATFORM_LIBS=
+PLATFORM_SHARED_EXT="so"
+PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl,"
+PLATFORM_SHARED_CFLAGS="-fPIC"
+PLATFORM_SHARED_VERSIONED=true
+
+MEMCMP_FLAG=
+if [ "$CXX" = "g++" ]; then
+    # Use libc's memcmp instead of GCC's memcmp.  This results in ~40%
+    # performance improvement on readrandom under gcc 4.4.3 on Linux/x86.
+    MEMCMP_FLAG="-fno-builtin-memcmp"
+fi
+
+case "$TARGET_OS" in
+    Darwin)
+        PLATFORM=OS_MACOSX
+        COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX"
+        PLATFORM_SHARED_EXT=dylib
+        [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd`
+        PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name $INSTALL_PATH/"
+        PORT_FILE=port/port_posix.cc
+        ;;
+    Linux)
+        PLATFORM=OS_LINUX
+        COMMON_FLAGS="$MEMCMP_FLAG -pthread -DOS_LINUX"
+        PLATFORM_LDFLAGS="-pthread"
+        PORT_FILE=port/port_posix.cc
+        ;;
+    SunOS)
+        PLATFORM=OS_SOLARIS
+        COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_SOLARIS"
+        PLATFORM_LIBS="-lpthread -lrt"
+        PORT_FILE=port/port_posix.cc
+        ;;
+    FreeBSD)
+        PLATFORM=OS_FREEBSD
+        COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_FREEBSD"
+        PLATFORM_LIBS="-lpthread"
+        PORT_FILE=port/port_posix.cc
+        ;;
+    GNU/kFreeBSD)
+        PLATFORM=OS_KFREEBSD
+        COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_KFREEBSD"
+        PLATFORM_LIBS="-lpthread"
+        PORT_FILE=port/port_posix.cc
+        ;;
+    NetBSD)
+        PLATFORM=OS_NETBSD
+        COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD"
+        PLATFORM_LIBS="-lpthread -lgcc_s"
+        PORT_FILE=port/port_posix.cc
+        ;;
+    OpenBSD)
+        PLATFORM=OS_OPENBSD
+        COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_OPENBSD"
+        PLATFORM_LDFLAGS="-pthread"
+        PORT_FILE=port/port_posix.cc
+        ;;
+    DragonFly)
+        PLATFORM=OS_DRAGONFLYBSD
+        COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_DRAGONFLYBSD"
+        PLATFORM_LIBS="-lpthread"
+        PORT_FILE=port/port_posix.cc
+        ;;
+    OS_ANDROID_CROSSCOMPILE)
+        PLATFORM=OS_ANDROID
+        COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX"
+        PLATFORM_LDFLAGS=""  # All pthread features are in the Android C library
+        PORT_FILE=port/port_posix.cc
+        CROSS_COMPILE=true
+        ;;
+    HP-UX)
+        PLATFORM=OS_HPUX
+        COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_HPUX"
+        PLATFORM_LDFLAGS="-pthread"
+        PORT_FILE=port/port_posix.cc
+        # man ld: +h internal_name
+        PLATFORM_SHARED_LDFLAGS="-shared -Wl,+h -Wl,"
+        ;;
+    OS_WINDOWS_CROSSCOMPILE | NATIVE_WINDOWS)
+        PLATFORM=OS_WINDOWS
+        COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_WINDOWS -DLEVELDB_PLATFORM_WINDOWS -DWINVER=0x0500 -D__USE_MINGW_ANSI_STDIO=1"
+        PLATFORM_SOURCES="util/env_win.cc"
+        PLATFORM_LIBS="-lshlwapi"
+        PORT_FILE=port/port_win.cc
+        CROSS_COMPILE=true
+        ;;
+    *)
+        echo "Unknown platform!" >&2
+        exit 1
+esac
+
+# We want to make a list of all cc files within util, db, table, and helpers
+# except for the test and benchmark files. By default, find will output a list
+# of all files matching either rule, so we need to append -print to make the
+# prune take effect.
+DIRS="$PREFIX/db $PREFIX/util $PREFIX/table"
+
+set -f # temporarily disable globbing so that our patterns aren't expanded
+PRUNE_TEST="-name *test*.cc -prune"
+PRUNE_BENCH="-name *_bench.cc -prune"
+PRUNE_TOOL="-name leveldb_main.cc -prune"
+PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_TOOL -o -name '*.cc' -print | sort | sed "s,^$PREFIX/,," | tr "\n" " "`
+
+set +f # re-enable globbing
+
+# The sources consist of the portable files, plus the platform-specific port
+# file.
+echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT
+echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT
+
+if [ "$CROSS_COMPILE" = "true" ]; then
+    # Cross-compiling; do not try any compilation tests.
+    true
+else
+    CXXOUTPUT="${TMPDIR}/leveldb_build_detect_platform-cxx.$$"
+
+    # If -std=c++0x works, use <cstdatomic>.  Otherwise use port_posix.h.
+    $CXX $CXXFLAGS -std=c++0x -x c++ - -o $CXXOUTPUT 2>/dev/null  <<EOF
+      #include <cstdatomic>
+      int main() {}
+EOF
+    if [ "$?" = 0 ]; then
+        COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX -DLEVELDB_CSTDATOMIC_PRESENT"
+        PLATFORM_CXXFLAGS="-std=c++0x"
+    else
+        COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX"
+    fi
+
+    # Test whether tcmalloc is available
+    $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT -ltcmalloc 2>/dev/null  <<EOF
+      int main() {}
+EOF
+    if [ "$?" = 0 ]; then
+        PLATFORM_LIBS="$PLATFORM_LIBS -ltcmalloc"
+    fi
+
+    rm -f $CXXOUTPUT 2>/dev/null
+fi
+
+PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
+PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
+
+echo "CC=$CC" >> $OUTPUT
+echo "CXX=$CXX" >> $OUTPUT
+echo "PLATFORM=$PLATFORM" >> $OUTPUT
+echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT
+echo "PLATFORM_LIBS=$PLATFORM_LIBS" >> $OUTPUT
+echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT
+echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT
+echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT
+echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT
+echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT
+echo "PLATFORM_SHARED_VERSIONED=$PLATFORM_SHARED_VERSIONED" >> $OUTPUT

+ 88 - 0
ecoin/src/leveldb/db/builder.cc

@@ -0,0 +1,88 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#include "db/builder.h"
+
+#include "db/filename.h"
+#include "db/dbformat.h"
+#include "db/table_cache.h"
+#include "db/version_edit.h"
+#include "leveldb/db.h"
+#include "leveldb/env.h"
+#include "leveldb/iterator.h"
+
+namespace leveldb {
+
+Status BuildTable(const std::string& dbname,
+                  Env* env,
+                  const Options& options,
+                  TableCache* table_cache,
+                  Iterator* iter,
+                  FileMetaData* meta) {
+  Status s;
+  meta->file_size = 0;
+  iter->SeekToFirst();
+
+  std::string fname = TableFileName(dbname, meta->number);
+  if (iter->Valid()) {
+    WritableFile* file;
+    s = env->NewWritableFile(fname, &file);
+    if (!s.ok()) {
+      return s;
+    }
+
+    TableBuilder* builder = new TableBuilder(options, file);
+    meta->smallest.DecodeFrom(iter->key());
+    for (; iter->Valid(); iter->Next()) {
+      Slice key = iter->key();
+      meta->largest.DecodeFrom(key);
+      builder->Add(key, iter->value());
+    }
+
+    // Finish and check for builder errors
+    if (s.ok()) {
+      s = builder->Finish();
+      if (s.ok()) {
+        meta->file_size = builder->FileSize();
+        assert(meta->file_size > 0);
+      }
+    } else {
+      builder->Abandon();
+    }
+    delete builder;
+
+    // Finish and check for file errors
+    if (s.ok()) {
+      s = file->Sync();
+    }
+    if (s.ok()) {
+      s = file->Close();
+    }
+    delete file;
+    file = NULL;
+
+    if (s.ok()) {
+      // Verify that the table is usable
+      Iterator* it = table_cache->NewIterator(ReadOptions(),
+                                              meta->number,
+                                              meta->file_size);
+      s = it->status();
+      delete it;
+    }
+  }
+
+  // Check for input iterator errors
+  if (!iter->status().ok()) {
+    s = iter->status();
+  }
+
+  if (s.ok() && meta->file_size > 0) {
+    // Keep it
+  } else {
+    env->DeleteFile(fname);
+  }
+  return s;
+}
+
+}  // namespace leveldb

+ 34 - 0
ecoin/src/leveldb/db/builder.h

@@ -0,0 +1,34 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#ifndef STORAGE_LEVELDB_DB_BUILDER_H_
+#define STORAGE_LEVELDB_DB_BUILDER_H_
+
+#include "leveldb/status.h"
+
+namespace leveldb {
+
+struct Options;
+struct FileMetaData;
+
+class Env;
+class Iterator;
+class TableCache;
+class VersionEdit;
+
+// Build a Table file from the contents of *iter.  The generated file
+// will be named according to meta->number.  On success, the rest of
+// *meta will be filled with metadata about the generated table.
+// If no data is present in *iter, meta->file_size will be set to
+// zero, and no Table file will be produced.
+extern Status BuildTable(const std::string& dbname,
+                         Env* env,
+                         const Options& options,
+                         TableCache* table_cache,
+                         Iterator* iter,
+                         FileMetaData* meta);
+
+}  // namespace leveldb
+
+#endif  // STORAGE_LEVELDB_DB_BUILDER_H_

+ 595 - 0
ecoin/src/leveldb/db/c.cc

@@ -0,0 +1,595 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#include "leveldb/c.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "leveldb/cache.h"
+#include "leveldb/comparator.h"
+#include "leveldb/db.h"
+#include "leveldb/env.h"
+#include "leveldb/filter_policy.h"
+#include "leveldb/iterator.h"
+#include "leveldb/options.h"
+#include "leveldb/status.h"
+#include "leveldb/write_batch.h"
+
+using leveldb::Cache;
+using leveldb::Comparator;
+using leveldb::CompressionType;
+using leveldb::DB;
+using leveldb::Env;
+using leveldb::FileLock;
+using leveldb::FilterPolicy;
+using leveldb::Iterator;
+using leveldb::kMajorVersion;
+using leveldb::kMinorVersion;
+using leveldb::Logger;
+using leveldb::NewBloomFilterPolicy;
+using leveldb::NewLRUCache;
+using leveldb::Options;
+using leveldb::RandomAccessFile;
+using leveldb::Range;
+using leveldb::ReadOptions;
+using leveldb::SequentialFile;
+using leveldb::Slice;
+using leveldb::Snapshot;
+using leveldb::Status;
+using leveldb::WritableFile;
+using leveldb::WriteBatch;
+using leveldb::WriteOptions;
+
+extern "C" {
+
+struct leveldb_t              { DB*               rep; };
+struct leveldb_iterator_t     { Iterator*         rep; };
+struct leveldb_writebatch_t   { WriteBatch        rep; };
+struct leveldb_snapshot_t     { const Snapshot*   rep; };
+struct leveldb_readoptions_t  { ReadOptions       rep; };
+struct leveldb_writeoptions_t { WriteOptions      rep; };
+struct leveldb_options_t      { Options           rep; };
+struct leveldb_cache_t        { Cache*            rep; };
+struct leveldb_seqfile_t      { SequentialFile*   rep; };
+struct leveldb_randomfile_t   { RandomAccessFile* rep; };
+struct leveldb_writablefile_t { WritableFile*     rep; };
+struct leveldb_logger_t       { Logger*           rep; };
+struct leveldb_filelock_t     { FileLock*         rep; };
+
+struct leveldb_comparator_t : public Comparator {
+  void* state_;
+  void (*destructor_)(void*);
+  int (*compare_)(
+      void*,
+      const char* a, size_t alen,
+      const char* b, size_t blen);
+  const char* (*name_)(void*);
+
+  virtual ~leveldb_comparator_t() {
+    (*destructor_)(state_);
+  }
+
+  virtual int Compare(const Slice& a, const Slice& b) const {
+    return (*compare_)(state_, a.data(), a.size(), b.data(), b.size());
+  }
+
+  virtual const char* Name() const {
+    return (*name_)(state_);
+  }
+
+  // No-ops since the C binding does not support key shortening methods.
+  virtual void FindShortestSeparator(std::string*, const Slice&) const { }
+  virtual void FindShortSuccessor(std::string* key) const { }
+};
+
+struct leveldb_filterpolicy_t : public FilterPolicy {
+  void* state_;
+  void (*destructor_)(void*);
+  const char* (*name_)(void*);
+  char* (*create_)(
+      void*,
+      const char* const* key_array, const size_t* key_length_array,
+      int num_keys,
+      size_t* filter_length);
+  unsigned char (*key_match_)(
+      void*,
+      const char* key, size_t length,
+      const char* filter, size_t filter_length);
+
+  virtual ~leveldb_filterpolicy_t() {
+    (*destructor_)(state_);
+  }
+
+  virtual const char* Name() const {
+    return (*name_)(state_);
+  }
+
+  virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const {
+    std::vector<const char*> key_pointers(n);
+    std::vector<size_t> key_sizes(n);
+    for (int i = 0; i < n; i++) {
+      key_pointers[i] = keys[i].data();
+      key_sizes[i] = keys[i].size();
+    }
+    size_t len;
+    char* filter = (*create_)(state_, &key_pointers[0], &key_sizes[0], n, &len);
+    dst->append(filter, len);
+    free(filter);
+  }
+
+  virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const {
+    return (*key_match_)(state_, key.data(), key.size(),
+                         filter.data(), filter.size());
+  }
+};
+
+struct leveldb_env_t {
+  Env* rep;
+  bool is_default;
+};
+
+static bool SaveError(char** errptr, const Status& s) {
+  assert(errptr != NULL);
+  if (s.ok()) {
+    return false;
+  } else if (*errptr == NULL) {
+    *errptr = strdup(s.ToString().c_str());
+  } else {
+    // TODO(sanjay): Merge with existing error?
+    free(*errptr);
+    *errptr = strdup(s.ToString().c_str());
+  }
+  return true;
+}
+
+static char* CopyString(const std::string& str) {
+  char* result = reinterpret_cast<char*>(malloc(sizeof(char) * str.size()));
+  memcpy(result, str.data(), sizeof(char) * str.size());
+  return result;
+}
+
+leveldb_t* leveldb_open(
+    const leveldb_options_t* options,
+    const char* name,
+    char** errptr) {
+  DB* db;
+  if (SaveError(errptr, DB::Open(options->rep, std::string(name), &db))) {
+    return NULL;
+  }
+  leveldb_t* result = new leveldb_t;
+  result->rep = db;
+  return result;
+}
+
+void leveldb_close(leveldb_t* db) {
+  delete db->rep;
+  delete db;
+}
+
+void leveldb_put(
+    leveldb_t* db,
+    const leveldb_writeoptions_t* options,
+    const char* key, size_t keylen,
+    const char* val, size_t vallen,
+    char** errptr) {
+  SaveError(errptr,
+            db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen)));
+}
+
+void leveldb_delete(
+    leveldb_t* db,
+    const leveldb_writeoptions_t* options,
+    const char* key, size_t keylen,
+    char** errptr) {
+  SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen)));
+}
+
+
+void leveldb_write(
+    leveldb_t* db,
+    const leveldb_writeoptions_t* options,
+    leveldb_writebatch_t* batch,
+    char** errptr) {
+  SaveError(errptr, db->rep->Write(options->rep, &batch->rep));
+}
+
+char* leveldb_get(
+    leveldb_t* db,
+    const leveldb_readoptions_t* options,
+    const char* key, size_t keylen,
+    size_t* vallen,
+    char** errptr) {
+  char* result = NULL;
+  std::string tmp;
+  Status s = db->rep->Get(options->rep, Slice(key, keylen), &tmp);
+  if (s.ok()) {
+    *vallen = tmp.size();
+    result = CopyString(tmp);
+  } else {
+    *vallen = 0;
+    if (!s.IsNotFound()) {
+      SaveError(errptr, s);
+    }
+  }
+  return result;
+}
+
+leveldb_iterator_t* leveldb_create_iterator(
+    leveldb_t* db,
+    const leveldb_readoptions_t* options) {
+  leveldb_iterator_t* result = new leveldb_iterator_t;
+  result->rep = db->rep->NewIterator(options->rep);
+  return result;
+}
+
+const leveldb_snapshot_t* leveldb_create_snapshot(
+    leveldb_t* db) {
+  leveldb_snapshot_t* result = new leveldb_snapshot_t;
+  result->rep = db->rep->GetSnapshot();
+  return result;
+}
+
+void leveldb_release_snapshot(
+    leveldb_t* db,
+    const leveldb_snapshot_t* snapshot) {
+  db->rep->ReleaseSnapshot(snapshot->rep);
+  delete snapshot;
+}
+
+char* leveldb_property_value(
+    leveldb_t* db,
+    const char* propname) {
+  std::string tmp;
+  if (db->rep->GetProperty(Slice(propname), &tmp)) {
+    // We use strdup() since we expect human readable output.
+    return strdup(tmp.c_str());
+  } else {
+    return NULL;
+  }
+}
+
+void leveldb_approximate_sizes(
+    leveldb_t* db,
+    int num_ranges,
+    const char* const* range_start_key, const size_t* range_start_key_len,
+    const char* const* range_limit_key, const size_t* range_limit_key_len,
+    uint64_t* sizes) {
+  Range* ranges = new Range[num_ranges];
+  for (int i = 0; i < num_ranges; i++) {
+    ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]);
+    ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]);
+  }
+  db->rep->GetApproximateSizes(ranges, num_ranges, sizes);
+  delete[] ranges;
+}
+
+void leveldb_compact_range(
+    leveldb_t* db,
+    const char* start_key, size_t start_key_len,
+    const char* limit_key, size_t limit_key_len) {
+  Slice a, b;
+  db->rep->CompactRange(
+      // Pass NULL Slice if corresponding "const char*" is NULL
+      (start_key ? (a = Slice(start_key, start_key_len), &a) : NULL),
+      (limit_key ? (b = Slice(limit_key, limit_key_len), &b) : NULL));
+}
+
+void leveldb_destroy_db(
+    const leveldb_options_t* options,
+    const char* name,
+    char** errptr) {
+  SaveError(errptr, DestroyDB(name, options->rep));
+}
+
+void leveldb_repair_db(
+    const leveldb_options_t* options,
+    const char* name,
+    char** errptr) {
+  SaveError(errptr, RepairDB(name, options->rep));
+}
+
+void leveldb_iter_destroy(leveldb_iterator_t* iter) {
+  delete iter->rep;
+  delete iter;
+}
+
+unsigned char leveldb_iter_valid(const leveldb_iterator_t* iter) {
+  return iter->rep->Valid();
+}
+
+void leveldb_iter_seek_to_first(leveldb_iterator_t* iter) {
+  iter->rep->SeekToFirst();
+}
+
+void leveldb_iter_seek_to_last(leveldb_iterator_t* iter) {
+  iter->rep->SeekToLast();
+}
+
+void leveldb_iter_seek(leveldb_iterator_t* iter, const char* k, size_t klen) {
+  iter->rep->Seek(Slice(k, klen));
+}
+
+void leveldb_iter_next(leveldb_iterator_t* iter) {
+  iter->rep->Next();
+}
+
+void leveldb_iter_prev(leveldb_iterator_t* iter) {
+  iter->rep->Prev();
+}
+
+const char* leveldb_iter_key(const leveldb_iterator_t* iter, size_t* klen) {
+  Slice s = iter->rep->key();
+  *klen = s.size();
+  return s.data();
+}
+
+const char* leveldb_iter_value(const leveldb_iterator_t* iter, size_t* vlen) {
+  Slice s = iter->rep->value();
+  *vlen = s.size();
+  return s.data();
+}
+
+void leveldb_iter_get_error(const leveldb_iterator_t* iter, char** errptr) {
+  SaveError(errptr, iter->rep->status());
+}
+
+leveldb_writebatch_t* leveldb_writebatch_create() {
+  return new leveldb_writebatch_t;
+}
+
+void leveldb_writebatch_destroy(leveldb_writebatch_t* b) {
+  delete b;
+}
+
+void leveldb_writebatch_clear(leveldb_writebatch_t* b) {
+  b->rep.Clear();
+}
+
+void leveldb_writebatch_put(
+    leveldb_writebatch_t* b,
+    const char* key, size_t klen,
+    const char* val, size_t vlen) {
+  b->rep.Put(Slice(key, klen), Slice(val, vlen));
+}
+
+void leveldb_writebatch_delete(
+    leveldb_writebatch_t* b,
+    const char* key, size_t klen) {
+  b->rep.Delete(Slice(key, klen));
+}
+
+void leveldb_writebatch_iterate(
+    leveldb_writebatch_t* b,
+    void* state,
+    void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen),
+    void (*deleted)(void*, const char* k, size_t klen)) {
+  class H : public WriteBatch::Handler {
+   public:
+    void* state_;
+    void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen);
+    void (*deleted_)(void*, const char* k, size_t klen);
+    virtual void Put(const Slice& key, const Slice& value) {
+      (*put_)(state_, key.data(), key.size(), value.data(), value.size());
+    }
+    virtual void Delete(const Slice& key) {
+      (*deleted_)(state_, key.data(), key.size());
+    }
+  };
+  H handler;
+  handler.state_ = state;
+  handler.put_ = put;
+  handler.deleted_ = deleted;
+  b->rep.Iterate(&handler);
+}
+
+leveldb_options_t* leveldb_options_create() {
+  return new leveldb_options_t;
+}
+
+void leveldb_options_destroy(leveldb_options_t* options) {
+  delete options;
+}
+
+void leveldb_options_set_comparator(
+    leveldb_options_t* opt,
+    leveldb_comparator_t* cmp) {
+  opt->rep.comparator = cmp;
+}
+
+void leveldb_options_set_filter_policy(
+    leveldb_options_t* opt,
+    leveldb_filterpolicy_t* policy) {
+  opt->rep.filter_policy = policy;
+}
+
+void leveldb_options_set_create_if_missing(
+    leveldb_options_t* opt, unsigned char v) {
+  opt->rep.create_if_missing = v;
+}
+
+void leveldb_options_set_error_if_exists(
+    leveldb_options_t* opt, unsigned char v) {
+  opt->rep.error_if_exists = v;
+}
+
+void leveldb_options_set_paranoid_checks(
+    leveldb_options_t* opt, unsigned char v) {
+  opt->rep.paranoid_checks = v;
+}
+
+void leveldb_options_set_env(leveldb_options_t* opt, leveldb_env_t* env) {
+  opt->rep.env = (env ? env->rep : NULL);
+}
+
+void leveldb_options_set_info_log(leveldb_options_t* opt, leveldb_logger_t* l) {
+  opt->rep.info_log = (l ? l->rep : NULL);
+}
+
+void leveldb_options_set_write_buffer_size(leveldb_options_t* opt, size_t s) {
+  opt->rep.write_buffer_size = s;
+}
+
+void leveldb_options_set_max_open_files(leveldb_options_t* opt, int n) {
+  opt->rep.max_open_files = n;
+}
+
+void leveldb_options_set_cache(leveldb_options_t* opt, leveldb_cache_t* c) {
+  opt->rep.block_cache = c->rep;
+}
+
+void leveldb_options_set_block_size(leveldb_options_t* opt, size_t s) {
+  opt->rep.block_size = s;
+}
+
+void leveldb_options_set_block_restart_interval(leveldb_options_t* opt, int n) {
+  opt->rep.block_restart_interval = n;
+}
+
+void leveldb_options_set_compression(leveldb_options_t* opt, int t) {
+  opt->rep.compression = static_cast<CompressionType>(t);
+}
+
+leveldb_comparator_t* leveldb_comparator_create(
+    void* state,
+    void (*destructor)(void*),
+    int (*compare)(
+        void*,
+        const char* a, size_t alen,
+        const char* b, size_t blen),
+    const char* (*name)(void*)) {
+  leveldb_comparator_t* result = new leveldb_comparator_t;
+  result->state_ = state;
+  result->destructor_ = destructor;
+  result->compare_ = compare;
+  result->name_ = name;
+  return result;
+}
+
+void leveldb_comparator_destroy(leveldb_comparator_t* cmp) {
+  delete cmp;
+}
+
+leveldb_filterpolicy_t* leveldb_filterpolicy_create(
+    void* state,
+    void (*destructor)(void*),
+    char* (*create_filter)(
+        void*,
+        const char* const* key_array, const size_t* key_length_array,
+        int num_keys,
+        size_t* filter_length),
+    unsigned char (*key_may_match)(
+        void*,
+        const char* key, size_t length,
+        const char* filter, size_t filter_length),
+    const char* (*name)(void*)) {
+  leveldb_filterpolicy_t* result = new leveldb_filterpolicy_t;
+  result->state_ = state;
+  result->destructor_ = destructor;
+  result->create_ = create_filter;
+  result->key_match_ = key_may_match;
+  result->name_ = name;
+  return result;
+}
+
+void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t* filter) {
+  delete filter;
+}
+
+leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) {
+  // Make a leveldb_filterpolicy_t, but override all of its methods so
+  // they delegate to a NewBloomFilterPolicy() instead of user
+  // supplied C functions.
+  struct Wrapper : public leveldb_filterpolicy_t {
+    const FilterPolicy* rep_;
+    ~Wrapper() { delete rep_; }
+    const char* Name() const { return rep_->Name(); }
+    void CreateFilter(const Slice* keys, int n, std::string* dst) const {
+      return rep_->CreateFilter(keys, n, dst);
+    }
+    bool KeyMayMatch(const Slice& key, const Slice& filter) const {
+      return rep_->KeyMayMatch(key, filter);
+    }
+    static void DoNothing(void*) { }
+  };
+  Wrapper* wrapper = new Wrapper;
+  wrapper->rep_ = NewBloomFilterPolicy(bits_per_key);
+  wrapper->state_ = NULL;
+  wrapper->destructor_ = &Wrapper::DoNothing;
+  return wrapper;
+}
+
+leveldb_readoptions_t* leveldb_readoptions_create() {
+  return new leveldb_readoptions_t;
+}
+
+void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) {
+  delete opt;
+}
+
+void leveldb_readoptions_set_verify_checksums(
+    leveldb_readoptions_t* opt,
+    unsigned char v) {
+  opt->rep.verify_checksums = v;
+}
+
+void leveldb_readoptions_set_fill_cache(
+    leveldb_readoptions_t* opt, unsigned char v) {
+  opt->rep.fill_cache = v;
+}
+
+void leveldb_readoptions_set_snapshot(
+    leveldb_readoptions_t* opt,
+    const leveldb_snapshot_t* snap) {
+  opt->rep.snapshot = (snap ? snap->rep : NULL);
+}
+
+leveldb_writeoptions_t* leveldb_writeoptions_create() {
+  return new leveldb_writeoptions_t;
+}
+
+void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) {
+  delete opt;
+}
+
+void leveldb_writeoptions_set_sync(
+    leveldb_writeoptions_t* opt, unsigned char v) {
+  opt->rep.sync = v;
+}
+
+leveldb_cache_t* leveldb_cache_create_lru(size_t capacity) {
+  leveldb_cache_t* c = new leveldb_cache_t;
+  c->rep = NewLRUCache(capacity);
+  return c;
+}
+
+void leveldb_cache_destroy(leveldb_cache_t* cache) {
+  delete cache->rep;
+  delete cache;
+}
+
+leveldb_env_t* leveldb_create_default_env() {
+  leveldb_env_t* result = new leveldb_env_t;
+  result->rep = Env::Default();
+  result->is_default = true;
+  return result;
+}
+
+void leveldb_env_destroy(leveldb_env_t* env) {
+  if (!env->is_default) delete env->rep;
+  delete env;
+}
+
+void leveldb_free(void* ptr) {
+  free(ptr);
+}
+
+int leveldb_major_version() {
+  return kMajorVersion;
+}
+
+int leveldb_minor_version() {
+  return kMinorVersion;
+}
+
+}  // end extern "C"

+ 390 - 0
ecoin/src/leveldb/db/c_test.c

@@ -0,0 +1,390 @@
+/* Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+   Use of this source code is governed by a BSD-style license that can be
+   found in the LICENSE file. See the AUTHORS file for names of contributors. */
+
+#include "leveldb/c.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+const char* phase = "";
+static char dbname[200];
+
+static void StartPhase(const char* name) {
+  fprintf(stderr, "=== Test %s\n", name);
+  phase = name;
+}
+
+static const char* GetTempDir(void) {
+    const char* ret = getenv("TEST_TMPDIR");
+    if (ret == NULL || ret[0] == '\0')
+        ret = "/tmp";
+    return ret;
+}
+
+#define CheckNoError(err)                                               \
+  if ((err) != NULL) {                                                  \
+    fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \
+    abort();                                                            \
+  }
+
+#define CheckCondition(cond)                                            \
+  if (!(cond)) {                                                        \
+    fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, #cond); \
+    abort();                                                            \
+  }
+
+static void CheckEqual(const char* expected, const char* v, size_t n) {
+  if (expected == NULL && v == NULL) {
+    // ok
+  } else if (expected != NULL && v != NULL && n == strlen(expected) &&
+             memcmp(expected, v, n) == 0) {
+    // ok
+    return;
+  } else {
+    fprintf(stderr, "%s: expected '%s', got '%s'\n",
+            phase,
+            (expected ? expected : "(null)"),
+            (v ? v : "(null"));
+    abort();
+  }
+}
+
+static void Free(char** ptr) {
+  if (*ptr) {
+    free(*ptr);
+    *ptr = NULL;
+  }
+}
+
+static void CheckGet(
+    leveldb_t* db,
+    const leveldb_readoptions_t* options,
+    const char* key,
+    const char* expected) {
+  char* err = NULL;
+  size_t val_len;
+  char* val;
+  val = leveldb_get(db, options, key, strlen(key), &val_len, &err);
+  CheckNoError(err);
+  CheckEqual(expected, val, val_len);
+  Free(&val);
+}
+
+static void CheckIter(leveldb_iterator_t* iter,
+                      const char* key, const char* val) {
+  size_t len;
+  const char* str;
+  str = leveldb_iter_key(iter, &len);
+  CheckEqual(key, str, len);
+  str = leveldb_iter_value(iter, &len);
+  CheckEqual(val, str, len);
+}
+
+// Callback from leveldb_writebatch_iterate()
+static void CheckPut(void* ptr,
+                     const char* k, size_t klen,
+                     const char* v, size_t vlen) {
+  int* state = (int*) ptr;
+  CheckCondition(*state < 2);
+  switch (*state) {
+    case 0:
+      CheckEqual("bar", k, klen);
+      CheckEqual("b", v, vlen);
+      break;
+    case 1:
+      CheckEqual("box", k, klen);
+      CheckEqual("c", v, vlen);
+      break;
+  }
+  (*state)++;
+}
+
+// Callback from leveldb_writebatch_iterate()
+static void CheckDel(void* ptr, const char* k, size_t klen) {
+  int* state = (int*) ptr;
+  CheckCondition(*state == 2);
+  CheckEqual("bar", k, klen);
+  (*state)++;
+}
+
+static void CmpDestroy(void* arg) { }
+
+static int CmpCompare(void* arg, const char* a, size_t alen,
+                      const char* b, size_t blen) {
+  int n = (alen < blen) ? alen : blen;
+  int r = memcmp(a, b, n);
+  if (r == 0) {
+    if (alen < blen) r = -1;
+    else if (alen > blen) r = +1;
+  }
+  return r;
+}
+
+static const char* CmpName(void* arg) {
+  return "foo";
+}
+
+// Custom filter policy
+static unsigned char fake_filter_result = 1;
+static void FilterDestroy(void* arg) { }
+static const char* FilterName(void* arg) {
+  return "TestFilter";
+}
+static char* FilterCreate(
+    void* arg,
+    const char* const* key_array, const size_t* key_length_array,
+    int num_keys,
+    size_t* filter_length) {
+  *filter_length = 4;
+  char* result = malloc(4);
+  memcpy(result, "fake", 4);
+  return result;
+}
+unsigned char FilterKeyMatch(
+    void* arg,
+    const char* key, size_t length,
+    const char* filter, size_t filter_length) {
+  CheckCondition(filter_length == 4);
+  CheckCondition(memcmp(filter, "fake", 4) == 0);
+  return fake_filter_result;
+}
+
+int main(int argc, char** argv) {
+  leveldb_t* db;
+  leveldb_comparator_t* cmp;
+  leveldb_cache_t* cache;
+  leveldb_env_t* env;
+  leveldb_options_t* options;
+  leveldb_readoptions_t* roptions;
+  leveldb_writeoptions_t* woptions;
+  char* err = NULL;
+  int run = -1;
+
+  CheckCondition(leveldb_major_version() >= 1);
+  CheckCondition(leveldb_minor_version() >= 1);
+
+  snprintf(dbname, sizeof(dbname),
+           "%s/leveldb_c_test-%d",
+           GetTempDir(),
+           ((int) geteuid()));
+
+  StartPhase("create_objects");
+  cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName);
+  env = leveldb_create_default_env();
+  cache = leveldb_cache_create_lru(100000);
+
+  options = leveldb_options_create();
+  leveldb_options_set_comparator(options, cmp);
+  leveldb_options_set_error_if_exists(options, 1);
+  leveldb_options_set_cache(options, cache);
+  leveldb_options_set_env(options, env);
+  leveldb_options_set_info_log(options, NULL);
+  leveldb_options_set_write_buffer_size(options, 100000);
+  leveldb_options_set_paranoid_checks(options, 1);
+  leveldb_options_set_max_open_files(options, 10);
+  leveldb_options_set_block_size(options, 1024);
+  leveldb_options_set_block_restart_interval(options, 8);
+  leveldb_options_set_compression(options, leveldb_no_compression);
+
+  roptions = leveldb_readoptions_create();
+  leveldb_readoptions_set_verify_checksums(roptions, 1);
+  leveldb_readoptions_set_fill_cache(roptions, 0);
+
+  woptions = leveldb_writeoptions_create();
+  leveldb_writeoptions_set_sync(woptions, 1);
+
+  StartPhase("destroy");
+  leveldb_destroy_db(options, dbname, &err);
+  Free(&err);
+
+  StartPhase("open_error");
+  db = leveldb_open(options, dbname, &err);
+  CheckCondition(err != NULL);
+  Free(&err);
+
+  StartPhase("leveldb_free");
+  db = leveldb_open(options, dbname, &err);
+  CheckCondition(err != NULL);
+  leveldb_free(err);
+  err = NULL;
+
+  StartPhase("open");
+  leveldb_options_set_create_if_missing(options, 1);
+  db = leveldb_open(options, dbname, &err);
+  CheckNoError(err);
+  CheckGet(db, roptions, "foo", NULL);
+
+  StartPhase("put");
+  leveldb_put(db, woptions, "foo", 3, "hello", 5, &err);
+  CheckNoError(err);
+  CheckGet(db, roptions, "foo", "hello");
+
+  StartPhase("compactall");
+  leveldb_compact_range(db, NULL, 0, NULL, 0);
+  CheckGet(db, roptions, "foo", "hello");
+
+  StartPhase("compactrange");
+  leveldb_compact_range(db, "a", 1, "z", 1);
+  CheckGet(db, roptions, "foo", "hello");
+
+  StartPhase("writebatch");
+  {
+    leveldb_writebatch_t* wb = leveldb_writebatch_create();
+    leveldb_writebatch_put(wb, "foo", 3, "a", 1);
+    leveldb_writebatch_clear(wb);
+    leveldb_writebatch_put(wb, "bar", 3, "b", 1);
+    leveldb_writebatch_put(wb, "box", 3, "c", 1);
+    leveldb_writebatch_delete(wb, "bar", 3);
+    leveldb_write(db, woptions, wb, &err);
+    CheckNoError(err);
+    CheckGet(db, roptions, "foo", "hello");
+    CheckGet(db, roptions, "bar", NULL);
+    CheckGet(db, roptions, "box", "c");
+    int pos = 0;
+    leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel);
+    CheckCondition(pos == 3);
+    leveldb_writebatch_destroy(wb);
+  }
+
+  StartPhase("iter");
+  {
+    leveldb_iterator_t* iter = leveldb_create_iterator(db, roptions);
+    CheckCondition(!leveldb_iter_valid(iter));
+    leveldb_iter_seek_to_first(iter);
+    CheckCondition(leveldb_iter_valid(iter));
+    CheckIter(iter, "box", "c");
+    leveldb_iter_next(iter);
+    CheckIter(iter, "foo", "hello");
+    leveldb_iter_prev(iter);
+    CheckIter(iter, "box", "c");
+    leveldb_iter_prev(iter);
+    CheckCondition(!leveldb_iter_valid(iter));
+    leveldb_iter_seek_to_last(iter);
+    CheckIter(iter, "foo", "hello");
+    leveldb_iter_seek(iter, "b", 1);
+    CheckIter(iter, "box", "c");
+    leveldb_iter_get_error(iter, &err);
+    CheckNoError(err);
+    leveldb_iter_destroy(iter);
+  }
+
+  StartPhase("approximate_sizes");
+  {
+    int i;
+    int n = 20000;
+    char keybuf[100];
+    char valbuf[100];
+    uint64_t sizes[2];
+    const char* start[2] = { "a", "k00000000000000010000" };
+    size_t start_len[2] = { 1, 21 };
+    const char* limit[2] = { "k00000000000000010000", "z" };
+    size_t limit_len[2] = { 21, 1 };
+    leveldb_writeoptions_set_sync(woptions, 0);
+    for (i = 0; i < n; i++) {
+      snprintf(keybuf, sizeof(keybuf), "k%020d", i);
+      snprintf(valbuf, sizeof(valbuf), "v%020d", i);
+      leveldb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf),
+                  &err);
+      CheckNoError(err);
+    }
+    leveldb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes);
+    CheckCondition(sizes[0] > 0);
+    CheckCondition(sizes[1] > 0);
+  }
+
+  StartPhase("property");
+  {
+    char* prop = leveldb_property_value(db, "nosuchprop");
+    CheckCondition(prop == NULL);
+    prop = leveldb_property_value(db, "leveldb.stats");
+    CheckCondition(prop != NULL);
+    Free(&prop);
+  }
+
+  StartPhase("snapshot");
+  {
+    const leveldb_snapshot_t* snap;
+    snap = leveldb_create_snapshot(db);
+    leveldb_delete(db, woptions, "foo", 3, &err);
+    CheckNoError(err);
+    leveldb_readoptions_set_snapshot(roptions, snap);
+    CheckGet(db, roptions, "foo", "hello");
+    leveldb_readoptions_set_snapshot(roptions, NULL);
+    CheckGet(db, roptions, "foo", NULL);
+    leveldb_release_snapshot(db, snap);
+  }
+
+  StartPhase("repair");
+  {
+    leveldb_close(db);
+    leveldb_options_set_create_if_missing(options, 0);
+    leveldb_options_set_error_if_exists(options, 0);
+    leveldb_repair_db(options, dbname, &err);
+    CheckNoError(err);
+    db = leveldb_open(options, dbname, &err);
+    CheckNoError(err);
+    CheckGet(db, roptions, "foo", NULL);
+    CheckGet(db, roptions, "bar", NULL);
+    CheckGet(db, roptions, "box", "c");
+    leveldb_options_set_create_if_missing(options, 1);
+    leveldb_options_set_error_if_exists(options, 1);
+  }
+
+  StartPhase("filter");
+  for (run = 0; run < 2; run++) {
+    // First run uses custom filter, second run uses bloom filter
+    CheckNoError(err);
+    leveldb_filterpolicy_t* policy;
+    if (run == 0) {
+      policy = leveldb_filterpolicy_create(
+          NULL, FilterDestroy, FilterCreate, FilterKeyMatch, FilterName);
+    } else {
+      policy = leveldb_filterpolicy_create_bloom(10);
+    }
+
+    // Create new database
+    leveldb_close(db);
+    leveldb_destroy_db(options, dbname, &err);
+    leveldb_options_set_filter_policy(options, policy);
+    db = leveldb_open(options, dbname, &err);
+    CheckNoError(err);
+    leveldb_put(db, woptions, "foo", 3, "foovalue", 8, &err);
+    CheckNoError(err);
+    leveldb_put(db, woptions, "bar", 3, "barvalue", 8, &err);
+    CheckNoError(err);
+    leveldb_compact_range(db, NULL, 0, NULL, 0);
+
+    fake_filter_result = 1;
+    CheckGet(db, roptions, "foo", "foovalue");
+    CheckGet(db, roptions, "bar", "barvalue");
+    if (phase == 0) {
+      // Must not find value when custom filter returns false
+      fake_filter_result = 0;
+      CheckGet(db, roptions, "foo", NULL);
+      CheckGet(db, roptions, "bar", NULL);
+      fake_filter_result = 1;
+
+      CheckGet(db, roptions, "foo", "foovalue");
+      CheckGet(db, roptions, "bar", "barvalue");
+    }
+    leveldb_options_set_filter_policy(options, NULL);
+    leveldb_filterpolicy_destroy(policy);
+  }
+
+  StartPhase("cleanup");
+  leveldb_close(db);
+  leveldb_options_destroy(options);
+  leveldb_readoptions_destroy(roptions);
+  leveldb_writeoptions_destroy(woptions);
+  leveldb_cache_destroy(cache);
+  leveldb_comparator_destroy(cmp);
+  leveldb_env_destroy(env);
+
+  fprintf(stderr, "PASS\n");
+  return 0;
+}

+ 359 - 0
ecoin/src/leveldb/db/corruption_test.cc

@@ -0,0 +1,359 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#include "leveldb/db.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "leveldb/cache.h"
+#include "leveldb/env.h"
+#include "leveldb/table.h"
+#include "leveldb/write_batch.h"
+#include "db/db_impl.h"
+#include "db/filename.h"
+#include "db/log_format.h"
+#include "db/version_set.h"
+#include "util/logging.h"
+#include "util/testharness.h"
+#include "util/testutil.h"
+
+namespace leveldb {
+
+static const int kValueSize = 1000;
+
+class CorruptionTest {
+ public:
+  test::ErrorEnv env_;
+  std::string dbname_;
+  Cache* tiny_cache_;
+  Options options_;
+  DB* db_;
+
+  CorruptionTest() {
+    tiny_cache_ = NewLRUCache(100);
+    options_.env = &env_;
+    dbname_ = test::TmpDir() + "/db_test";
+    DestroyDB(dbname_, options_);
+
+    db_ = NULL;
+    options_.create_if_missing = true;
+    Reopen();
+    options_.create_if_missing = false;
+  }
+
+  ~CorruptionTest() {
+     delete db_;
+     DestroyDB(dbname_, Options());
+     delete tiny_cache_;
+  }
+
+  Status TryReopen(Options* options = NULL) {
+    delete db_;
+    db_ = NULL;
+    Options opt = (options ? *options : options_);
+    opt.env = &env_;
+    opt.block_cache = tiny_cache_;
+    return DB::Open(opt, dbname_, &db_);
+  }
+
+  void Reopen(Options* options = NULL) {
+    ASSERT_OK(TryReopen(options));
+  }
+
+  void RepairDB() {
+    delete db_;
+    db_ = NULL;
+    ASSERT_OK(::leveldb::RepairDB(dbname_, options_));
+  }
+
+  void Build(int n) {
+    std::string key_space, value_space;
+    WriteBatch batch;
+    for (int i = 0; i < n; i++) {
+      //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);
+      Slice key = Key(i, &key_space);
+      batch.Clear();
+      batch.Put(key, Value(i, &value_space));
+      ASSERT_OK(db_->Write(WriteOptions(), &batch));
+    }
+  }
+
+  void Check(int min_expected, int max_expected) {
+    int next_expected = 0;
+    int missed = 0;
+    int bad_keys = 0;
+    int bad_values = 0;
+    int correct = 0;
+    std::string value_space;
+    Iterator* iter = db_->NewIterator(ReadOptions());
+    for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
+      uint64_t key;
+      Slice in(iter->key());
+      if (!ConsumeDecimalNumber(&in, &key) ||
+          !in.empty() ||
+          key < next_expected) {
+        bad_keys++;
+        continue;
+      }
+      missed += (key - next_expected);
+      next_expected = key + 1;
+      if (iter->value() != Value(key, &value_space)) {
+        bad_values++;
+      } else {
+        correct++;
+      }
+    }
+    delete iter;
+
+    fprintf(stderr,
+            "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n",
+            min_expected, max_expected, correct, bad_keys, bad_values, missed);
+    ASSERT_LE(min_expected, correct);
+    ASSERT_GE(max_expected, correct);
+  }
+
+  void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
+    // Pick file to corrupt
+    std::vector<std::string> filenames;
+    ASSERT_OK(env_.GetChildren(dbname_, &filenames));
+    uint64_t number;
+    FileType type;
+    std::string fname;
+    int picked_number = -1;
+    for (int i = 0; i < filenames.size(); i++) {
+      if (ParseFileName(filenames[i], &number, &type) &&
+          type == filetype &&
+          int(number) > picked_number) {  // Pick latest file
+        fname = dbname_ + "/" + filenames[i];
+        picked_number = number;
+      }
+    }
+    ASSERT_TRUE(!fname.empty()) << filetype;
+
+    struct stat sbuf;
+    if (stat(fname.c_str(), &sbuf) != 0) {
+      const char* msg = strerror(errno);
+      ASSERT_TRUE(false) << fname << ": " << msg;
+    }
+
+    if (offset < 0) {
+      // Relative to end of file; make it absolute
+      if (-offset > sbuf.st_size) {
+        offset = 0;
+      } else {
+        offset = sbuf.st_size + offset;
+      }
+    }
+    if (offset > sbuf.st_size) {
+      offset = sbuf.st_size;
+    }
+    if (offset + bytes_to_corrupt > sbuf.st_size) {
+      bytes_to_corrupt = sbuf.st_size - offset;
+    }
+
+    // Do it
+    std::string contents;
+    Status s = ReadFileToString(Env::Default(), fname, &contents);
+    ASSERT_TRUE(s.ok()) << s.ToString();
+    for (int i = 0; i < bytes_to_corrupt; i++) {
+      contents[i + offset] ^= 0x80;
+    }
+    s = WriteStringToFile(Env::Default(), contents, fname);
+    ASSERT_TRUE(s.ok()) << s.ToString();
+  }
+
+  int Property(const std::string& name) {
+    std::string property;
+    int result;
+    if (db_->GetProperty(name, &property) &&
+        sscanf(property.c_str(), "%d", &result) == 1) {
+      return result;
+    } else {
+      return -1;
+    }
+  }
+
+  // Return the ith key
+  Slice Key(int i, std::string* storage) {
+    char buf[100];
+    snprintf(buf, sizeof(buf), "%016d", i);
+    storage->assign(buf, strlen(buf));
+    return Slice(*storage);
+  }
+
+  // Return the value to associate with the specified key
+  Slice Value(int k, std::string* storage) {
+    Random r(k);
+    return test::RandomString(&r, kValueSize, storage);
+  }
+};
+
+TEST(CorruptionTest, Recovery) {
+  Build(100);
+  Check(100, 100);
+  Corrupt(kLogFile, 19, 1);      // WriteBatch tag for first record
+  Corrupt(kLogFile, log::kBlockSize + 1000, 1);  // Somewhere in second block
+  Reopen();
+
+  // The 64 records in the first two log blocks are completely lost.
+  Check(36, 36);
+}
+
+TEST(CorruptionTest, RecoverWriteError) {
+  env_.writable_file_error_ = true;
+  Status s = TryReopen();
+  ASSERT_TRUE(!s.ok());
+}
+
+TEST(CorruptionTest, NewFileErrorDuringWrite) {
+  // Do enough writing to force minor compaction
+  env_.writable_file_error_ = true;
+  const int num = 3 + (Options().write_buffer_size / kValueSize);
+  std::string value_storage;
+  Status s;
+  for (int i = 0; s.ok() && i < num; i++) {
+    WriteBatch batch;
+    batch.Put("a", Value(100, &value_storage));
+    s = db_->Write(WriteOptions(), &batch);
+  }
+  ASSERT_TRUE(!s.ok());
+  ASSERT_GE(env_.num_writable_file_errors_, 1);
+  env_.writable_file_error_ = false;
+  Reopen();
+}
+
+TEST(CorruptionTest, TableFile) {
+  Build(100);
+  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
+  dbi->TEST_CompactMemTable();
+  dbi->TEST_CompactRange(0, NULL, NULL);
+  dbi->TEST_CompactRange(1, NULL, NULL);
+
+  Corrupt(kTableFile, 100, 1);
+  Check(99, 99);
+}
+
+TEST(CorruptionTest, TableFileIndexData) {
+  Build(10000);  // Enough to build multiple Tables
+  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
+  dbi->TEST_CompactMemTable();
+
+  Corrupt(kTableFile, -2000, 500);
+  Reopen();
+  Check(5000, 9999);
+}
+
+TEST(CorruptionTest, MissingDescriptor) {
+  Build(1000);
+  RepairDB();
+  Reopen();
+  Check(1000, 1000);
+}
+
+TEST(CorruptionTest, SequenceNumberRecovery) {
+  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
+  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
+  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3"));
+  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4"));
+  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5"));
+  RepairDB();
+  Reopen();
+  std::string v;
+  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
+  ASSERT_EQ("v5", v);
+  // Write something.  If sequence number was not recovered properly,
+  // it will be hidden by an earlier write.
+  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6"));
+  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
+  ASSERT_EQ("v6", v);
+  Reopen();
+  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
+  ASSERT_EQ("v6", v);
+}
+
+TEST(CorruptionTest, CorruptedDescriptor) {
+  ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
+  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
+  dbi->TEST_CompactMemTable();
+  dbi->TEST_CompactRange(0, NULL, NULL);
+
+  Corrupt(kDescriptorFile, 0, 1000);
+  Status s = TryReopen();
+  ASSERT_TRUE(!s.ok());
+
+  RepairDB();
+  Reopen();
+  std::string v;
+  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
+  ASSERT_EQ("hello", v);
+}
+
+TEST(CorruptionTest, CompactionInputError) {
+  Build(10);
+  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
+  dbi->TEST_CompactMemTable();
+  const int last = config::kMaxMemCompactLevel;
+  ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last)));
+
+  Corrupt(kTableFile, 100, 1);
+  Check(9, 9);
+
+  // Force compactions by writing lots of values
+  Build(10000);
+  Check(10000, 10000);
+}
+
+TEST(CorruptionTest, CompactionInputErrorParanoid) {
+  Options options;
+  options.paranoid_checks = true;
+  options.write_buffer_size = 1048576;
+  Reopen(&options);
+  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
+
+  // Fill levels >= 1 so memtable compaction outputs to level 1
+  for (int level = 1; level < config::kNumLevels; level++) {
+    dbi->Put(WriteOptions(), "", "begin");
+    dbi->Put(WriteOptions(), "~", "end");
+    dbi->TEST_CompactMemTable();
+  }
+
+  Build(10);
+  dbi->TEST_CompactMemTable();
+  ASSERT_EQ(1, Property("leveldb.num-files-at-level0"));
+
+  Corrupt(kTableFile, 100, 1);
+  Check(9, 9);
+
+  // Write must eventually fail because of corrupted table
+  Status s;
+  std::string tmp1, tmp2;
+  for (int i = 0; i < 10000 && s.ok(); i++) {
+    s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2));
+  }
+  ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db";
+}
+
+TEST(CorruptionTest, UnrelatedKeys) {
+  Build(10);
+  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
+  dbi->TEST_CompactMemTable();
+  Corrupt(kTableFile, 100, 1);
+
+  std::string tmp1, tmp2;
+  ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2)));
+  std::string v;
+  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
+  ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
+  dbi->TEST_CompactMemTable();
+  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
+  ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
+}
+
+}  // namespace leveldb
+
+int main(int argc, char** argv) {
+  return leveldb::test::RunAllTests();
+}

+ 979 - 0
ecoin/src/leveldb/db/db_bench.cc

@@ -0,0 +1,979 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "db/db_impl.h"
+#include "db/version_set.h"
+#include "leveldb/cache.h"
+#include "leveldb/db.h"
+#include "leveldb/env.h"
+#include "leveldb/write_batch.h"
+#include "port/port.h"
+#include "util/crc32c.h"
+#include "util/histogram.h"
+#include "util/mutexlock.h"
+#include "util/random.h"
+#include "util/testutil.h"
+
+// Comma-separated list of operations to run in the specified order
+//   Actual benchmarks:
+//      fillseq       -- write N values in sequential key order in async mode
+//      fillrandom    -- write N values in random key order in async mode
+//      overwrite     -- overwrite N values in random key order in async mode
+//      fillsync      -- write N/100 values in random key order in sync mode
+//      fill100K      -- write N/1000 100K values in random order in async mode
+//      deleteseq     -- delete N keys in sequential order
+//      deleterandom  -- delete N keys in random order
+//      readseq       -- read N times sequentially
+//      readreverse   -- read N times in reverse order
+//      readrandom    -- read N times in random order
+//      readmissing   -- read N missing keys in random order
+//      readhot       -- read N times in random order from 1% section of DB
+//      seekrandom    -- N random seeks
+//      crc32c        -- repeated crc32c of 4K of data
+//      acquireload   -- load N*1000 times
+//   Meta operations:
+//      compact     -- Compact the entire DB
+//      stats       -- Print DB stats
+//      sstables    -- Print sstable info
+//      heapprofile -- Dump a heap profile (if supported by this port)
+static const char* FLAGS_benchmarks =
+    "fillseq,"
+    "fillsync,"
+    "fillrandom,"
+    "overwrite,"
+    "readrandom,"
+    "readrandom,"  // Extra run to allow previous compactions to quiesce
+    "readseq,"
+    "readreverse,"
+    "compact,"
+    "readrandom,"
+    "readseq,"
+    "readreverse,"
+    "fill100K,"
+    "crc32c,"
+    "snappycomp,"
+    "snappyuncomp,"
+    "acquireload,"
+    ;
+
+// Number of key/values to place in database
+static int FLAGS_num = 1000000;
+
+// Number of read operations to do.  If negative, do FLAGS_num reads.
+static int FLAGS_reads = -1;
+
+// Number of concurrent threads to run.
+static int FLAGS_threads = 1;
+
+// Size of each value
+static int FLAGS_value_size = 100;
+
+// Arrange to generate values that shrink to this fraction of
+// their original size after compression
+static double FLAGS_compression_ratio = 0.5;
+
+// Print histogram of operation timings
+static bool FLAGS_histogram = false;
+
+// Number of bytes to buffer in memtable before compacting
+// (initialized to default value by "main")
+static int FLAGS_write_buffer_size = 0;
+
+// Number of bytes to use as a cache of uncompressed data.
+// Negative means use default settings.
+static int FLAGS_cache_size = -1;
+
+// Maximum number of files to keep open at the same time (use default if == 0)
+static int FLAGS_open_files = 0;
+
+// Bloom filter bits per key.
+// Negative means use default settings.
+static int FLAGS_bloom_bits = -1;
+
+// If true, do not destroy the existing database.  If you set this
+// flag and also specify a benchmark that wants a fresh database, that
+// benchmark will fail.
+static bool FLAGS_use_existing_db = false;
+
+// Use the db with the following name.
+static const char* FLAGS_db = NULL;
+
+namespace leveldb {
+
+namespace {
+
+// Helper for quickly generating random data.
+class RandomGenerator {
+ private:
+  std::string data_;
+  int pos_;
+
+ public:
+  RandomGenerator() {
+    // We use a limited amount of data over and over again and ensure
+    // that it is larger than the compression window (32KB), and also
+    // large enough to serve all typical value sizes we want to write.
+    Random rnd(301);
+    std::string piece;
+    while (data_.size() < 1048576) {
+      // Add a short fragment that is as compressible as specified
+      // by FLAGS_compression_ratio.
+      test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece);
+      data_.append(piece);
+    }
+    pos_ = 0;
+  }
+
+  Slice Generate(int len) {
+    if (pos_ + len > data_.size()) {
+      pos_ = 0;
+      assert(len < data_.size());
+    }
+    pos_ += len;
+    return Slice(data_.data() + pos_ - len, len);
+  }
+};
+
+static Slice TrimSpace(Slice s) {
+  int start = 0;
+  while (start < s.size() && isspace(s[start])) {
+    start++;
+  }
+  int limit = s.size();
+  while (limit > start && isspace(s[limit-1])) {
+    limit--;
+  }
+  return Slice(s.data() + start, limit - start);
+}
+
+static void AppendWithSpace(std::string* str, Slice msg) {
+  if (msg.empty()) return;
+  if (!str->empty()) {
+    str->push_back(' ');
+  }
+  str->append(msg.data(), msg.size());
+}
+
+class Stats {
+ private:
+  double start_;
+  double finish_;
+  double seconds_;
+  int done_;
+  int next_report_;
+  int64_t bytes_;
+  double last_op_finish_;
+  Histogram hist_;
+  std::string message_;
+
+ public:
+  Stats() { Start(); }
+
+  void Start() {
+    next_report_ = 100;
+    last_op_finish_ = start_;
+    hist_.Clear();
+    done_ = 0;
+    bytes_ = 0;
+    seconds_ = 0;
+    start_ = Env::Default()->NowMicros();
+    finish_ = start_;
+    message_.clear();
+  }
+
+  void Merge(const Stats& other) {
+    hist_.Merge(other.hist_);
+    done_ += other.done_;
+    bytes_ += other.bytes_;
+    seconds_ += other.seconds_;
+    if (other.start_ < start_) start_ = other.start_;
+    if (other.finish_ > finish_) finish_ = other.finish_;
+
+    // Just keep the messages from one thread
+    if (message_.empty()) message_ = other.message_;
+  }
+
+  void Stop() {
+    finish_ = Env::Default()->NowMicros();
+    seconds_ = (finish_ - start_) * 1e-6;
+  }
+
+  void AddMessage(Slice msg) {
+    AppendWithSpace(&message_, msg);
+  }
+
+  void FinishedSingleOp() {
+    if (FLAGS_histogram) {
+      double now = Env::Default()->NowMicros();
+      double micros = now - last_op_finish_;
+      hist_.Add(micros);
+      if (micros > 20000) {
+        fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
+        fflush(stderr);
+      }
+      last_op_finish_ = now;
+    }
+
+    done_++;
+    if (done_ >= next_report_) {
+      if      (next_report_ < 1000)   next_report_ += 100;
+      else if (next_report_ < 5000)   next_report_ += 500;
+      else if (next_report_ < 10000)  next_report_ += 1000;
+      else if (next_report_ < 50000)  next_report_ += 5000;
+      else if (next_report_ < 100000) next_report_ += 10000;
+      else if (next_report_ < 500000) next_report_ += 50000;
+      else                            next_report_ += 100000;
+      fprintf(stderr, "... finished %d ops%30s\r", done_, "");
+      fflush(stderr);
+    }
+  }
+
+  void AddBytes(int64_t n) {
+    bytes_ += n;
+  }
+
+  void Report(const Slice& name) {
+    // Pretend at least one op was done in case we are running a benchmark
+    // that does not call FinishedSingleOp().
+    if (done_ < 1) done_ = 1;
+
+    std::string extra;
+    if (bytes_ > 0) {
+      // Rate is computed on actual elapsed time, not the sum of per-thread
+      // elapsed times.
+      double elapsed = (finish_ - start_) * 1e-6;
+      char rate[100];
+      snprintf(rate, sizeof(rate), "%6.1f MB/s",
+               (bytes_ / 1048576.0) / elapsed);
+      extra = rate;
+    }
+    AppendWithSpace(&extra, message_);
+
+    fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
+            name.ToString().c_str(),
+            seconds_ * 1e6 / done_,
+            (extra.empty() ? "" : " "),
+            extra.c_str());
+    if (FLAGS_histogram) {
+      fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
+    }
+    fflush(stdout);
+  }
+};
+
+// State shared by all concurrent executions of the same benchmark.
+struct SharedState {
+  port::Mutex mu;
+  port::CondVar cv;
+  int total;
+
+  // Each thread goes through the following states:
+  //    (1) initializing
+  //    (2) waiting for others to be initialized
+  //    (3) running
+  //    (4) done
+
+  int num_initialized;
+  int num_done;
+  bool start;
+
+  SharedState() : cv(&mu) { }
+};
+
+// Per-thread state for concurrent executions of the same benchmark.
+struct ThreadState {
+  int tid;             // 0..n-1 when running in n threads
+  Random rand;         // Has different seeds for different threads
+  Stats stats;
+  SharedState* shared;
+
+  ThreadState(int index)
+      : tid(index),
+        rand(1000 + index) {
+  }
+};
+
+}  // namespace
+
+class Benchmark {
+ private:
+  Cache* cache_;
+  const FilterPolicy* filter_policy_;
+  DB* db_;
+  int num_;
+  int value_size_;
+  int entries_per_batch_;
+  WriteOptions write_options_;
+  int reads_;
+  int heap_counter_;
+
+  void PrintHeader() {
+    const int kKeySize = 16;
+    PrintEnvironment();
+    fprintf(stdout, "Keys:       %d bytes each\n", kKeySize);
+    fprintf(stdout, "Values:     %d bytes each (%d bytes after compression)\n",
+            FLAGS_value_size,
+            static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5));
+    fprintf(stdout, "Entries:    %d\n", num_);
+    fprintf(stdout, "RawSize:    %.1f MB (estimated)\n",
+            ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_)
+             / 1048576.0));
+    fprintf(stdout, "FileSize:   %.1f MB (estimated)\n",
+            (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_)
+             / 1048576.0));
+    PrintWarnings();
+    fprintf(stdout, "------------------------------------------------\n");
+  }
+
+  void PrintWarnings() {
+#if defined(__GNUC__) && !defined(__OPTIMIZE__)
+    fprintf(stdout,
+            "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"
+            );
+#endif
+#ifndef NDEBUG
+    fprintf(stdout,
+            "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
+#endif
+
+    // See if snappy is working by attempting to compress a compressible string
+    const char text[] = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
+    std::string compressed;
+    if (!port::Snappy_Compress(text, sizeof(text), &compressed)) {
+      fprintf(stdout, "WARNING: Snappy compression is not enabled\n");
+    } else if (compressed.size() >= sizeof(text)) {
+      fprintf(stdout, "WARNING: Snappy compression is not effective\n");
+    }
+  }
+
+  void PrintEnvironment() {
+    fprintf(stderr, "LevelDB:    version %d.%d\n",
+            kMajorVersion, kMinorVersion);
+
+#if defined(__linux)
+    time_t now = time(NULL);
+    fprintf(stderr, "Date:       %s", ctime(&now));  // ctime() adds newline
+
+    FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
+    if (cpuinfo != NULL) {
+      char line[1000];
+      int num_cpus = 0;
+      std::string cpu_type;
+      std::string cache_size;
+      while (fgets(line, sizeof(line), cpuinfo) != NULL) {
+        const char* sep = strchr(line, ':');
+        if (sep == NULL) {
+          continue;
+        }
+        Slice key = TrimSpace(Slice(line, sep - 1 - line));
+        Slice val = TrimSpace(Slice(sep + 1));
+        if (key == "model name") {
+          ++num_cpus;
+          cpu_type = val.ToString();
+        } else if (key == "cache size") {
+          cache_size = val.ToString();
+        }
+      }
+      fclose(cpuinfo);
+      fprintf(stderr, "CPU:        %d * %s\n", num_cpus, cpu_type.c_str());
+      fprintf(stderr, "CPUCache:   %s\n", cache_size.c_str());
+    }
+#endif
+  }
+
+ public:
+  Benchmark()
+  : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : NULL),
+    filter_policy_(FLAGS_bloom_bits >= 0
+                   ? NewBloomFilterPolicy(FLAGS_bloom_bits)
+                   : NULL),
+    db_(NULL),
+    num_(FLAGS_num),
+    value_size_(FLAGS_value_size),
+    entries_per_batch_(1),
+    reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
+    heap_counter_(0) {
+    std::vector<std::string> files;
+    Env::Default()->GetChildren(FLAGS_db, &files);
+    for (int i = 0; i < files.size(); i++) {
+      if (Slice(files[i]).starts_with("heap-")) {
+        Env::Default()->DeleteFile(std::string(FLAGS_db) + "/" + files[i]);
+      }
+    }
+    if (!FLAGS_use_existing_db) {
+      DestroyDB(FLAGS_db, Options());
+    }
+  }
+
+  ~Benchmark() {
+    delete db_;
+    delete cache_;
+    delete filter_policy_;
+  }
+
+  void Run() {
+    PrintHeader();
+    Open();
+
+    const char* benchmarks = FLAGS_benchmarks;
+    while (benchmarks != NULL) {
+      const char* sep = strchr(benchmarks, ',');
+      Slice name;
+      if (sep == NULL) {
+        name = benchmarks;
+        benchmarks = NULL;
+      } else {
+        name = Slice(benchmarks, sep - benchmarks);
+        benchmarks = sep + 1;
+      }
+
+      // Reset parameters that may be overriddden bwlow
+      num_ = FLAGS_num;
+      reads_ = (FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads);
+      value_size_ = FLAGS_value_size;
+      entries_per_batch_ = 1;
+      write_options_ = WriteOptions();
+
+      void (Benchmark::*method)(ThreadState*) = NULL;
+      bool fresh_db = false;
+      int num_threads = FLAGS_threads;
+
+      if (name == Slice("fillseq")) {
+        fresh_db = true;
+        method = &Benchmark::WriteSeq;
+      } else if (name == Slice("fillbatch")) {
+        fresh_db = true;
+        entries_per_batch_ = 1000;
+        method = &Benchmark::WriteSeq;
+      } else if (name == Slice("fillrandom")) {
+        fresh_db = true;
+        method = &Benchmark::WriteRandom;
+      } else if (name == Slice("overwrite")) {
+        fresh_db = false;
+        method = &Benchmark::WriteRandom;
+      } else if (name == Slice("fillsync")) {
+        fresh_db = true;
+        num_ /= 1000;
+        write_options_.sync = true;
+        method = &Benchmark::WriteRandom;
+      } else if (name == Slice("fill100K")) {
+        fresh_db = true;
+        num_ /= 1000;
+        value_size_ = 100 * 1000;
+        method = &Benchmark::WriteRandom;
+      } else if (name == Slice("readseq")) {
+        method = &Benchmark::ReadSequential;
+      } else if (name == Slice("readreverse")) {
+        method = &Benchmark::ReadReverse;
+      } else if (name == Slice("readrandom")) {
+        method = &Benchmark::ReadRandom;
+      } else if (name == Slice("readmissing")) {
+        method = &Benchmark::ReadMissing;
+      } else if (name == Slice("seekrandom")) {
+        method = &Benchmark::SeekRandom;
+      } else if (name == Slice("readhot")) {
+        method = &Benchmark::ReadHot;
+      } else if (name == Slice("readrandomsmall")) {
+        reads_ /= 1000;
+        method = &Benchmark::ReadRandom;
+      } else if (name == Slice("deleteseq")) {
+        method = &Benchmark::DeleteSeq;
+      } else if (name == Slice("deleterandom")) {
+        method = &Benchmark::DeleteRandom;
+      } else if (name == Slice("readwhilewriting")) {
+        num_threads++;  // Add extra thread for writing
+        method = &Benchmark::ReadWhileWriting;
+      } else if (name == Slice("compact")) {
+        method = &Benchmark::Compact;
+      } else if (name == Slice("crc32c")) {
+        method = &Benchmark::Crc32c;
+      } else if (name == Slice("acquireload")) {
+        method = &Benchmark::AcquireLoad;
+      } else if (name == Slice("snappycomp")) {
+        method = &Benchmark::SnappyCompress;
+      } else if (name == Slice("snappyuncomp")) {
+        method = &Benchmark::SnappyUncompress;
+      } else if (name == Slice("heapprofile")) {
+        HeapProfile();
+      } else if (name == Slice("stats")) {
+        PrintStats("leveldb.stats");
+      } else if (name == Slice("sstables")) {
+        PrintStats("leveldb.sstables");
+      } else {
+        if (name != Slice()) {  // No error message for empty name
+          fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
+        }
+      }
+
+      if (fresh_db) {
+        if (FLAGS_use_existing_db) {
+          fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n",
+                  name.ToString().c_str());
+          method = NULL;
+        } else {
+          delete db_;
+          db_ = NULL;
+          DestroyDB(FLAGS_db, Options());
+          Open();
+        }
+      }
+
+      if (method != NULL) {
+        RunBenchmark(num_threads, name, method);
+      }
+    }
+  }
+
+ private:
+  struct ThreadArg {
+    Benchmark* bm;
+    SharedState* shared;
+    ThreadState* thread;
+    void (Benchmark::*method)(ThreadState*);
+  };
+
+  static void ThreadBody(void* v) {
+    ThreadArg* arg = reinterpret_cast<ThreadArg*>(v);
+    SharedState* shared = arg->shared;
+    ThreadState* thread = arg->thread;
+    {
+      MutexLock l(&shared->mu);
+      shared->num_initialized++;
+      if (shared->num_initialized >= shared->total) {
+        shared->cv.SignalAll();
+      }
+      while (!shared->start) {
+        shared->cv.Wait();
+      }
+    }
+
+    thread->stats.Start();
+    (arg->bm->*(arg->method))(thread);
+    thread->stats.Stop();
+
+    {
+      MutexLock l(&shared->mu);
+      shared->num_done++;
+      if (shared->num_done >= shared->total) {
+        shared->cv.SignalAll();
+      }
+    }
+  }
+
+  void RunBenchmark(int n, Slice name,
+                    void (Benchmark::*method)(ThreadState*)) {
+    SharedState shared;
+    shared.total = n;
+    shared.num_initialized = 0;
+    shared.num_done = 0;
+    shared.start = false;
+
+    ThreadArg* arg = new ThreadArg[n];
+    for (int i = 0; i < n; i++) {
+      arg[i].bm = this;
+      arg[i].method = method;
+      arg[i].shared = &shared;
+      arg[i].thread = new ThreadState(i);
+      arg[i].thread->shared = &shared;
+      Env::Default()->StartThread(ThreadBody, &arg[i]);
+    }
+
+    shared.mu.Lock();
+    while (shared.num_initialized < n) {
+      shared.cv.Wait();
+    }
+
+    shared.start = true;
+    shared.cv.SignalAll();
+    while (shared.num_done < n) {
+      shared.cv.Wait();
+    }
+    shared.mu.Unlock();
+
+    for (int i = 1; i < n; i++) {
+      arg[0].thread->stats.Merge(arg[i].thread->stats);
+    }
+    arg[0].thread->stats.Report(name);
+
+    for (int i = 0; i < n; i++) {
+      delete arg[i].thread;
+    }
+    delete[] arg;
+  }
+
+  void Crc32c(ThreadState* thread) {
+    // Checksum about 500MB of data total
+    const int size = 4096;
+    const char* label = "(4K per op)";
+    std::string data(size, 'x');
+    int64_t bytes = 0;
+    uint32_t crc = 0;
+    while (bytes < 500 * 1048576) {
+      crc = crc32c::Value(data.data(), size);
+      thread->stats.FinishedSingleOp();
+      bytes += size;
+    }
+    // Print so result is not dead
+    fprintf(stderr, "... crc=0x%x\r", static_cast<unsigned int>(crc));
+
+    thread->stats.AddBytes(bytes);
+    thread->stats.AddMessage(label);
+  }
+
+  void AcquireLoad(ThreadState* thread) {
+    int dummy;
+    port::AtomicPointer ap(&dummy);
+    int count = 0;
+    void *ptr = NULL;
+    thread->stats.AddMessage("(each op is 1000 loads)");
+    while (count < 100000) {
+      for (int i = 0; i < 1000; i++) {
+        ptr = ap.Acquire_Load();
+      }
+      count++;
+      thread->stats.FinishedSingleOp();
+    }
+    if (ptr == NULL) exit(1); // Disable unused variable warning.
+  }
+
+  void SnappyCompress(ThreadState* thread) {
+    RandomGenerator gen;
+    Slice input = gen.Generate(Options().block_size);
+    int64_t bytes = 0;
+    int64_t produced = 0;
+    bool ok = true;
+    std::string compressed;
+    while (ok && bytes < 1024 * 1048576) {  // Compress 1G
+      ok = port::Snappy_Compress(input.data(), input.size(), &compressed);
+      produced += compressed.size();
+      bytes += input.size();
+      thread->stats.FinishedSingleOp();
+    }
+
+    if (!ok) {
+      thread->stats.AddMessage("(snappy failure)");
+    } else {
+      char buf[100];
+      snprintf(buf, sizeof(buf), "(output: %.1f%%)",
+               (produced * 100.0) / bytes);
+      thread->stats.AddMessage(buf);
+      thread->stats.AddBytes(bytes);
+    }
+  }
+
+  void SnappyUncompress(ThreadState* thread) {
+    RandomGenerator gen;
+    Slice input = gen.Generate(Options().block_size);
+    std::string compressed;
+    bool ok = port::Snappy_Compress(input.data(), input.size(), &compressed);
+    int64_t bytes = 0;
+    char* uncompressed = new char[input.size()];
+    while (ok && bytes < 1024 * 1048576) {  // Compress 1G
+      ok =  port::Snappy_Uncompress(compressed.data(), compressed.size(),
+                                    uncompressed);
+      bytes += input.size();
+      thread->stats.FinishedSingleOp();
+    }
+    delete[] uncompressed;
+
+    if (!ok) {
+      thread->stats.AddMessage("(snappy failure)");
+    } else {
+      thread->stats.AddBytes(bytes);
+    }
+  }
+
+  void Open() {
+    assert(db_ == NULL);
+    Options options;
+    options.create_if_missing = !FLAGS_use_existing_db;
+    options.block_cache = cache_;
+    options.write_buffer_size = FLAGS_write_buffer_size;
+    options.max_open_files = FLAGS_open_files;
+    options.filter_policy = filter_policy_;
+    Status s = DB::Open(options, FLAGS_db, &db_);
+    if (!s.ok()) {
+      fprintf(stderr, "open error: %s\n", s.ToString().c_str());
+      exit(1);
+    }
+  }
+
+  void WriteSeq(ThreadState* thread) {
+    DoWrite(thread, true);
+  }
+
+  void WriteRandom(ThreadState* thread) {
+    DoWrite(thread, false);
+  }
+
+  void DoWrite(ThreadState* thread, bool seq) {
+    if (num_ != FLAGS_num) {
+      char msg[100];
+      snprintf(msg, sizeof(msg), "(%d ops)", num_);
+      thread->stats.AddMessage(msg);
+    }
+
+    RandomGenerator gen;
+    WriteBatch batch;
+    Status s;
+    int64_t bytes = 0;
+    for (int i = 0; i < num_; i += entries_per_batch_) {
+      batch.Clear();
+      for (int j = 0; j < entries_per_batch_; j++) {
+        const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num);
+        char key[100];
+        snprintf(key, sizeof(key), "%016d", k);
+        batch.Put(key, gen.Generate(value_size_));
+        bytes += value_size_ + strlen(key);
+        thread->stats.FinishedSingleOp();
+      }
+      s = db_->Write(write_options_, &batch);
+      if (!s.ok()) {
+        fprintf(stderr, "put error: %s\n", s.ToString().c_str());
+        exit(1);
+      }
+    }
+    thread->stats.AddBytes(bytes);
+  }
+
+  void ReadSequential(ThreadState* thread) {
+    Iterator* iter = db_->NewIterator(ReadOptions());
+    int i = 0;
+    int64_t bytes = 0;
+    for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) {
+      bytes += iter->key().size() + iter->value().size();
+      thread->stats.FinishedSingleOp();
+      ++i;
+    }
+    delete iter;
+    thread->stats.AddBytes(bytes);
+  }
+
+  void ReadReverse(ThreadState* thread) {
+    Iterator* iter = db_->NewIterator(ReadOptions());
+    int i = 0;
+    int64_t bytes = 0;
+    for (iter->SeekToLast(); i < reads_ && iter->Valid(); iter->Prev()) {
+      bytes += iter->key().size() + iter->value().size();
+      thread->stats.FinishedSingleOp();
+      ++i;
+    }
+    delete iter;
+    thread->stats.AddBytes(bytes);
+  }
+
+  void ReadRandom(ThreadState* thread) {
+    ReadOptions options;
+    std::string value;
+    int found = 0;
+    for (int i = 0; i < reads_; i++) {
+      char key[100];
+      const int k = thread->rand.Next() % FLAGS_num;
+      snprintf(key, sizeof(key), "%016d", k);
+      if (db_->Get(options, key, &value).ok()) {
+        found++;
+      }
+      thread->stats.FinishedSingleOp();
+    }
+    char msg[100];
+    snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_);
+    thread->stats.AddMessage(msg);
+  }
+
+  void ReadMissing(ThreadState* thread) {
+    ReadOptions options;
+    std::string value;
+    for (int i = 0; i < reads_; i++) {
+      char key[100];
+      const int k = thread->rand.Next() % FLAGS_num;
+      snprintf(key, sizeof(key), "%016d.", k);
+      db_->Get(options, key, &value);
+      thread->stats.FinishedSingleOp();
+    }
+  }
+
+  void ReadHot(ThreadState* thread) {
+    ReadOptions options;
+    std::string value;
+    const int range = (FLAGS_num + 99) / 100;
+    for (int i = 0; i < reads_; i++) {
+      char key[100];
+      const int k = thread->rand.Next() % range;
+      snprintf(key, sizeof(key), "%016d", k);
+      db_->Get(options, key, &value);
+      thread->stats.FinishedSingleOp();
+    }
+  }
+
+  void SeekRandom(ThreadState* thread) {
+    ReadOptions options;
+    std::string value;
+    int found = 0;
+    for (int i = 0; i < reads_; i++) {
+      Iterator* iter = db_->NewIterator(options);
+      char key[100];
+      const int k = thread->rand.Next() % FLAGS_num;
+      snprintf(key, sizeof(key), "%016d", k);
+      iter->Seek(key);
+      if (iter->Valid() && iter->key() == key) found++;
+      delete iter;
+      thread->stats.FinishedSingleOp();
+    }
+    char msg[100];
+    snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_);
+    thread->stats.AddMessage(msg);
+  }
+
+  void DoDelete(ThreadState* thread, bool seq) {
+    RandomGenerator gen;
+    WriteBatch batch;
+    Status s;
+    for (int i = 0; i < num_; i += entries_per_batch_) {
+      batch.Clear();
+      for (int j = 0; j < entries_per_batch_; j++) {
+        const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num);
+        char key[100];
+        snprintf(key, sizeof(key), "%016d", k);
+        batch.Delete(key);
+        thread->stats.FinishedSingleOp();
+      }
+      s = db_->Write(write_options_, &batch);
+      if (!s.ok()) {
+        fprintf(stderr, "del error: %s\n", s.ToString().c_str());
+        exit(1);
+      }
+    }
+  }
+
+  void DeleteSeq(ThreadState* thread) {
+    DoDelete(thread, true);
+  }
+
+  void DeleteRandom(ThreadState* thread) {
+    DoDelete(thread, false);
+  }
+
+  void ReadWhileWriting(ThreadState* thread) {
+    if (thread->tid > 0) {
+      ReadRandom(thread);
+    } else {
+      // Special thread that keeps writing until other threads are done.
+      RandomGenerator gen;
+      while (true) {
+        {
+          MutexLock l(&thread->shared->mu);
+          if (thread->shared->num_done + 1 >= thread->shared->num_initialized) {
+            // Other threads have finished
+            break;
+          }
+        }
+
+        const int k = thread->rand.Next() % FLAGS_num;
+        char key[100];
+        snprintf(key, sizeof(key), "%016d", k);
+        Status s = db_->Put(write_options_, key, gen.Generate(value_size_));
+        if (!s.ok()) {
+          fprintf(stderr, "put error: %s\n", s.ToString().c_str());
+          exit(1);
+        }
+      }
+
+      // Do not count any of the preceding work/delay in stats.
+      thread->stats.Start();
+    }
+  }
+
+  void Compact(ThreadState* thread) {
+    db_->CompactRange(NULL, NULL);
+  }
+
+  void PrintStats(const char* key) {
+    std::string stats;
+    if (!db_->GetProperty(key, &stats)) {
+      stats = "(failed)";
+    }
+    fprintf(stdout, "\n%s\n", stats.c_str());
+  }
+
+  static void WriteToFile(void* arg, const char* buf, int n) {
+    reinterpret_cast<WritableFile*>(arg)->Append(Slice(buf, n));
+  }
+
+  void HeapProfile() {
+    char fname[100];
+    snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_);
+    WritableFile* file;
+    Status s = Env::Default()->NewWritableFile(fname, &file);
+    if (!s.ok()) {
+      fprintf(stderr, "%s\n", s.ToString().c_str());
+      return;
+    }
+    bool ok = port::GetHeapProfile(WriteToFile, file);
+    delete file;
+    if (!ok) {
+      fprintf(stderr, "heap profiling not supported\n");
+      Env::Default()->DeleteFile(fname);
+    }
+  }
+};
+
+}  // namespace leveldb
+
+int main(int argc, char** argv) {
+  FLAGS_write_buffer_size = leveldb::Options().write_buffer_size;
+  FLAGS_open_files = leveldb::Options().max_open_files;
+  std::string default_db_path;
+
+  for (int i = 1; i < argc; i++) {
+    double d;
+    int n;
+    char junk;
+    if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) {
+      FLAGS_benchmarks = argv[i] + strlen("--benchmarks=");
+    } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) {
+      FLAGS_compression_ratio = d;
+    } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&
+               (n == 0 || n == 1)) {
+      FLAGS_histogram = n;
+    } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 &&
+               (n == 0 || n == 1)) {
+      FLAGS_use_existing_db = n;
+    } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {
+      FLAGS_num = n;
+    } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) {
+      FLAGS_reads = n;
+    } else if (sscanf(argv[i], "--threads=%d%c", &n, &junk) == 1) {
+      FLAGS_threads = n;
+    } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {
+      FLAGS_value_size = n;
+    } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) {
+      FLAGS_write_buffer_size = n;
+    } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) {
+      FLAGS_cache_size = n;
+    } else if (sscanf(argv[i], "--bloom_bits=%d%c", &n, &junk) == 1) {
+      FLAGS_bloom_bits = n;
+    } else if (sscanf(argv[i], "--open_files=%d%c", &n, &junk) == 1) {
+      FLAGS_open_files = n;
+    } else if (strncmp(argv[i], "--db=", 5) == 0) {
+      FLAGS_db = argv[i] + 5;
+    } else {
+      fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
+      exit(1);
+    }
+  }
+
+  // Choose a location for the test database if none given with --db=<path>
+  if (FLAGS_db == NULL) {
+      leveldb::Env::Default()->GetTestDirectory(&default_db_path);
+      default_db_path += "/dbbench";
+      FLAGS_db = default_db_path.c_str();
+  }
+
+  leveldb::Benchmark benchmark;
+  benchmark.Run();
+  return 0;
+}

File diff suppressed because it is too large
+ 1485 - 0
ecoin/src/leveldb/db/db_impl.cc


+ 203 - 0
ecoin/src/leveldb/db/db_impl.h

@@ -0,0 +1,203 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#ifndef STORAGE_LEVELDB_DB_DB_IMPL_H_
+#define STORAGE_LEVELDB_DB_DB_IMPL_H_
+
+#include <deque>
+#include <set>
+#include "db/dbformat.h"
+#include "db/log_writer.h"
+#include "db/snapshot.h"
+#include "leveldb/db.h"
+#include "leveldb/env.h"
+#include "port/port.h"
+#include "port/thread_annotations.h"
+
+namespace leveldb {
+
+class MemTable;
+class TableCache;
+class Version;
+class VersionEdit;
+class VersionSet;
+
+class DBImpl : public DB {
+ public:
+  DBImpl(const Options& options, const std::string& dbname);
+  virtual ~DBImpl();
+
+  // Implementations of the DB interface
+  virtual Status Put(const WriteOptions&, const Slice& key, const Slice& value);
+  virtual Status Delete(const WriteOptions&, const Slice& key);
+  virtual Status Write(const WriteOptions& options, WriteBatch* updates);
+  virtual Status Get(const ReadOptions& options,
+                     const Slice& key,
+                     std::string* value);
+  virtual Iterator* NewIterator(const ReadOptions&);
+  virtual const Snapshot* GetSnapshot();
+  virtual void ReleaseSnapshot(const Snapshot* snapshot);
+  virtual bool GetProperty(const Slice& property, std::string* value);
+  virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes);
+  virtual void CompactRange(const Slice* begin, const Slice* end);
+
+  // Extra methods (for testing) that are not in the public DB interface
+
+  // Compact any files in the named level that overlap [*begin,*end]
+  void TEST_CompactRange(int level, const Slice* begin, const Slice* end);
+
+  // Force current memtable contents to be compacted.
+  Status TEST_CompactMemTable();
+
+  // Return an internal iterator over the current state of the database.
+  // The keys of this iterator are internal keys (see format.h).
+  // The returned iterator should be deleted when no longer needed.
+  Iterator* TEST_NewInternalIterator();
+
+  // Return the maximum overlapping data (in bytes) at next level for any
+  // file at a level >= 1.
+  int64_t TEST_MaxNextLevelOverlappingBytes();
+
+ private:
+  friend class DB;
+  struct CompactionState;
+  struct Writer;
+
+  Iterator* NewInternalIterator(const ReadOptions&,
+                                SequenceNumber* latest_snapshot);
+
+  Status NewDB();
+
+  // Recover the descriptor from persistent storage.  May do a significant
+  // amount of work to recover recently logged updates.  Any changes to
+  // be made to the descriptor are added to *edit.
+  Status Recover(VersionEdit* edit) EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+  void MaybeIgnoreError(Status* s) const;
+
+  // Delete any unneeded files and stale in-memory entries.
+  void DeleteObsoleteFiles();
+
+  // Compact the in-memory write buffer to disk.  Switches to a new
+  // log-file/memtable and writes a new descriptor iff successful.
+  Status CompactMemTable()
+      EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+  Status RecoverLogFile(uint64_t log_number,
+                        VersionEdit* edit,
+                        SequenceNumber* max_sequence)
+      EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+  Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base)
+      EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+  Status MakeRoomForWrite(bool force /* compact even if there is room? */)
+      EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+  WriteBatch* BuildBatchGroup(Writer** last_writer);
+
+  void MaybeScheduleCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+  static void BGWork(void* db);
+  void BackgroundCall();
+  Status BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+  void CleanupCompaction(CompactionState* compact)
+      EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+  Status DoCompactionWork(CompactionState* compact)
+      EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+  Status OpenCompactionOutputFile(CompactionState* compact);
+  Status FinishCompactionOutputFile(CompactionState* compact, Iterator* input);
+  Status InstallCompactionResults(CompactionState* compact)
+      EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+  // Constant after construction
+  Env* const env_;
+  const InternalKeyComparator internal_comparator_;
+  const InternalFilterPolicy internal_filter_policy_;
+  const Options options_;  // options_.comparator == &internal_comparator_
+  bool owns_info_log_;
+  bool owns_cache_;
+  const std::string dbname_;
+
+  // table_cache_ provides its own synchronization
+  TableCache* table_cache_;
+
+  // Lock over the persistent DB state.  Non-NULL iff successfully acquired.
+  FileLock* db_lock_;
+
+  // State below is protected by mutex_
+  port::Mutex mutex_;
+  port::AtomicPointer shutting_down_;
+  port::CondVar bg_cv_;          // Signalled when background work finishes
+  MemTable* mem_;
+  MemTable* imm_;                // Memtable being compacted
+  port::AtomicPointer has_imm_;  // So bg thread can detect non-NULL imm_
+  WritableFile* logfile_;
+  uint64_t logfile_number_;
+  log::Writer* log_;
+
+  // Queue of writers.
+  std::deque<Writer*> writers_;
+  WriteBatch* tmp_batch_;
+
+  SnapshotList snapshots_;
+
+  // Set of table files to protect from deletion because they are
+  // part of ongoing compactions.
+  std::set<uint64_t> pending_outputs_;
+
+  // Has a background compaction been scheduled or is running?
+  bool bg_compaction_scheduled_;
+
+  // Information for a manual compaction
+  struct ManualCompaction {
+    int level;
+    bool done;
+    const InternalKey* begin;   // NULL means beginning of key range
+    const InternalKey* end;     // NULL means end of key range
+    InternalKey tmp_storage;    // Used to keep track of compaction progress
+  };
+  ManualCompaction* manual_compaction_;
+
+  VersionSet* versions_;
+
+  // Have we encountered a background error in paranoid mode?
+  Status bg_error_;
+  int consecutive_compaction_errors_;
+
+  // Per level compaction stats.  stats_[level] stores the stats for
+  // compactions that produced data for the specified "level".
+  struct CompactionStats {
+    int64_t micros;
+    int64_t bytes_read;
+    int64_t bytes_written;
+
+    CompactionStats() : micros(0), bytes_read(0), bytes_written(0) { }
+
+    void Add(const CompactionStats& c) {
+      this->micros += c.micros;
+      this->bytes_read += c.bytes_read;
+      this->bytes_written += c.bytes_written;
+    }
+  };
+  CompactionStats stats_[config::kNumLevels];
+
+  // No copying allowed
+  DBImpl(const DBImpl&);
+  void operator=(const DBImpl&);
+
+  const Comparator* user_comparator() const {
+    return internal_comparator_.user_comparator();
+  }
+};
+
+// Sanitize db options.  The caller should delete result.info_log if
+// it is not equal to src.info_log.
+extern Options SanitizeOptions(const std::string& db,
+                               const InternalKeyComparator* icmp,
+                               const InternalFilterPolicy* ipolicy,
+                               const Options& src);
+
+}  // namespace leveldb
+
+#endif  // STORAGE_LEVELDB_DB_DB_IMPL_H_

+ 299 - 0
ecoin/src/leveldb/db/db_iter.cc

@@ -0,0 +1,299 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#include "db/db_iter.h"
+
+#include "db/filename.h"
+#include "db/dbformat.h"
+#include "leveldb/env.h"
+#include "leveldb/iterator.h"
+#include "port/port.h"
+#include "util/logging.h"
+#include "util/mutexlock.h"
+
+namespace leveldb {
+
+#if 0
+static void DumpInternalIter(Iterator* iter) {
+  for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
+    ParsedInternalKey k;
+    if (!ParseInternalKey(iter->key(), &k)) {
+      fprintf(stderr, "Corrupt '%s'\n", EscapeString(iter->key()).c_str());
+    } else {
+      fprintf(stderr, "@ '%s'\n", k.DebugString().c_str());
+    }
+  }
+}
+#endif
+
+namespace {
+
+// Memtables and sstables that make the DB representation contain
+// (userkey,seq,type) => uservalue entries.  DBIter
+// combines multiple entries for the same userkey found in the DB
+// representation into a single entry while accounting for sequence
+// numbers, deletion markers, overwrites, etc.
+class DBIter: public Iterator {
+ public:
+  // Which direction is the iterator currently moving?
+  // (1) When moving forward, the internal iterator is positioned at
+  //     the exact entry that yields this->key(), this->value()
+  // (2) When moving backwards, the internal iterator is positioned
+  //     just before all entries whose user key == this->key().
+  enum Direction {
+    kForward,
+    kReverse
+  };
+
+  DBIter(const std::string* dbname, Env* env,
+         const Comparator* cmp, Iterator* iter, SequenceNumber s)
+      : dbname_(dbname),
+        env_(env),
+        user_comparator_(cmp),
+        iter_(iter),
+        sequence_(s),
+        direction_(kForward),
+        valid_(false) {
+  }
+  virtual ~DBIter() {
+    delete iter_;
+  }
+  virtual bool Valid() const { return valid_; }
+  virtual Slice key() const {
+    assert(valid_);
+    return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_;
+  }
+  virtual Slice value() const {
+    assert(valid_);
+    return (direction_ == kForward) ? iter_->value() : saved_value_;
+  }
+  virtual Status status() const {
+    if (status_.ok()) {
+      return iter_->status();
+    } else {
+      return status_;
+    }
+  }
+
+  virtual void Next();
+  virtual void Prev();
+  virtual void Seek(const Slice& target);
+  virtual void SeekToFirst();
+  virtual void SeekToLast();
+
+ private:
+  void FindNextUserEntry(bool skipping, std::string* skip);
+  void FindPrevUserEntry();
+  bool ParseKey(ParsedInternalKey* key);
+
+  inline void SaveKey(const Slice& k, std::string* dst) {
+    dst->assign(k.data(), k.size());
+  }
+
+  inline void ClearSavedValue() {
+    if (saved_value_.capacity() > 1048576) {
+      std::string empty;
+      swap(empty, saved_value_);
+    } else {
+      saved_value_.clear();
+    }
+  }
+
+  const std::string* const dbname_;
+  Env* const env_;
+  const Comparator* const user_comparator_;
+  Iterator* const iter_;
+  SequenceNumber const sequence_;
+
+  Status status_;
+  std::string saved_key_;     // == current key when direction_==kReverse
+  std::string saved_value_;   // == current raw value when direction_==kReverse
+  Direction direction_;
+  bool valid_;
+
+  // No copying allowed
+  DBIter(const DBIter&);
+  void operator=(const DBIter&);
+};
+
+inline bool DBIter::ParseKey(ParsedInternalKey* ikey) {
+  if (!ParseInternalKey(iter_->key(), ikey)) {
+    status_ = Status::Corruption("corrupted internal key in DBIter");
+    return false;
+  } else {
+    return true;
+  }
+}
+
+void DBIter::Next() {
+  assert(valid_);
+
+  if (direction_ == kReverse) {  // Switch directions?
+    direction_ = kForward;
+    // iter_ is pointing just before the entries for this->key(),
+    // so advance into the range of entries for this->key() and then
+    // use the normal skipping code below.
+    if (!iter_->Valid()) {
+      iter_->SeekToFirst();
+    } else {
+      iter_->Next();
+    }
+    if (!iter_->Valid()) {
+      valid_ = false;
+      saved_key_.clear();
+      return;
+    }
+  }
+
+  // Temporarily use saved_key_ as storage for key to skip.
+  std::string* skip = &saved_key_;
+  SaveKey(ExtractUserKey(iter_->key()), skip);
+  FindNextUserEntry(true, skip);
+}
+
+void DBIter::FindNextUserEntry(bool skipping, std::string* skip) {
+  // Loop until we hit an acceptable entry to yield
+  assert(iter_->Valid());
+  assert(direction_ == kForward);
+  do {
+    ParsedInternalKey ikey;
+    if (ParseKey(&ikey) && ikey.sequence <= sequence_) {
+      switch (ikey.type) {
+        case kTypeDeletion:
+          // Arrange to skip all upcoming entries for this key since
+          // they are hidden by this deletion.
+          SaveKey(ikey.user_key, skip);
+          skipping = true;
+          break;
+        case kTypeValue:
+          if (skipping &&
+              user_comparator_->Compare(ikey.user_key, *skip) <= 0) {
+            // Entry hidden
+          } else {
+            valid_ = true;
+            saved_key_.clear();
+            return;
+          }
+          break;
+      }
+    }
+    iter_->Next();
+  } while (iter_->Valid());
+  saved_key_.clear();
+  valid_ = false;
+}
+
+void DBIter::Prev() {
+  assert(valid_);
+
+  if (direction_ == kForward) {  // Switch directions?
+    // iter_ is pointing at the current entry.  Scan backwards until
+    // the key changes so we can use the normal reverse scanning code.
+    assert(iter_->Valid());  // Otherwise valid_ would have been false
+    SaveKey(ExtractUserKey(iter_->key()), &saved_key_);
+    while (true) {
+      iter_->Prev();
+      if (!iter_->Valid()) {
+        valid_ = false;
+        saved_key_.clear();
+        ClearSavedValue();
+        return;
+      }
+      if (user_comparator_->Compare(ExtractUserKey(iter_->key()),
+                                    saved_key_) < 0) {
+        break;
+      }
+    }
+    direction_ = kReverse;
+  }
+
+  FindPrevUserEntry();
+}
+
+void DBIter::FindPrevUserEntry() {
+  assert(direction_ == kReverse);
+
+  ValueType value_type = kTypeDeletion;
+  if (iter_->Valid()) {
+    do {
+      ParsedInternalKey ikey;
+      if (ParseKey(&ikey) && ikey.sequence <= sequence_) {
+        if ((value_type != kTypeDeletion) &&
+            user_comparator_->Compare(ikey.user_key, saved_key_) < 0) {
+          // We encountered a non-deleted value in entries for previous keys,
+          break;
+        }
+        value_type = ikey.type;
+        if (value_type == kTypeDeletion) {
+          saved_key_.clear();
+          ClearSavedValue();
+        } else {
+          Slice raw_value = iter_->value();
+          if (saved_value_.capacity() > raw_value.size() + 1048576) {
+            std::string empty;
+            swap(empty, saved_value_);
+          }
+          SaveKey(ExtractUserKey(iter_->key()), &saved_key_);
+          saved_value_.assign(raw_value.data(), raw_value.size());
+        }
+      }
+      iter_->Prev();
+    } while (iter_->Valid());
+  }
+
+  if (value_type == kTypeDeletion) {
+    // End
+    valid_ = false;
+    saved_key_.clear();
+    ClearSavedValue();
+    direction_ = kForward;
+  } else {
+    valid_ = true;
+  }
+}
+
+void DBIter::Seek(const Slice& target) {
+  direction_ = kForward;
+  ClearSavedValue();
+  saved_key_.clear();
+  AppendInternalKey(
+      &saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek));
+  iter_->Seek(saved_key_);
+  if (iter_->Valid()) {
+    FindNextUserEntry(false, &saved_key_ /* temporary storage */);
+  } else {
+    valid_ = false;
+  }
+}
+
+void DBIter::SeekToFirst() {
+  direction_ = kForward;
+  ClearSavedValue();
+  iter_->SeekToFirst();
+  if (iter_->Valid()) {
+    FindNextUserEntry(false, &saved_key_ /* temporary storage */);
+  } else {
+    valid_ = false;
+  }
+}
+
+void DBIter::SeekToLast() {
+  direction_ = kReverse;
+  ClearSavedValue();
+  iter_->SeekToLast();
+  FindPrevUserEntry();
+}
+
+}  // anonymous namespace
+
+Iterator* NewDBIterator(
+    const std::string* dbname,
+    Env* env,
+    const Comparator* user_key_comparator,
+    Iterator* internal_iter,
+    const SequenceNumber& sequence) {
+  return new DBIter(dbname, env, user_key_comparator, internal_iter, sequence);
+}
+
+}  // namespace leveldb

+ 26 - 0
ecoin/src/leveldb/db/db_iter.h

@@ -0,0 +1,26 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#ifndef STORAGE_LEVELDB_DB_DB_ITER_H_
+#define STORAGE_LEVELDB_DB_DB_ITER_H_
+
+#include <stdint.h>
+#include "leveldb/db.h"
+#include "db/dbformat.h"
+
+namespace leveldb {
+
+// Return a new iterator that converts internal keys (yielded by
+// "*internal_iter") that were live at the specified "sequence" number
+// into appropriate user keys.
+extern Iterator* NewDBIterator(
+    const std::string* dbname,
+    Env* env,
+    const Comparator* user_key_comparator,
+    Iterator* internal_iter,
+    const SequenceNumber& sequence);
+
+}  // namespace leveldb
+
+#endif  // STORAGE_LEVELDB_DB_DB_ITER_H_

File diff suppressed because it is too large
+ 2092 - 0
ecoin/src/leveldb/db/db_test.cc


+ 140 - 0
ecoin/src/leveldb/db/dbformat.cc

@@ -0,0 +1,140 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#include <stdio.h>
+#include "db/dbformat.h"
+#include "port/port.h"
+#include "util/coding.h"
+
+namespace leveldb {
+
+static uint64_t PackSequenceAndType(uint64_t seq, ValueType t) {
+  assert(seq <= kMaxSequenceNumber);
+  assert(t <= kValueTypeForSeek);
+  return (seq << 8) | t;
+}
+
+void AppendInternalKey(std::string* result, const ParsedInternalKey& key) {
+  result->append(key.user_key.data(), key.user_key.size());
+  PutFixed64(result, PackSequenceAndType(key.sequence, key.type));
+}
+
+std::string ParsedInternalKey::DebugString() const {
+  char buf[50];
+  snprintf(buf, sizeof(buf), "' @ %llu : %d",
+           (unsigned long long) sequence,
+           int(type));
+  std::string result = "'";
+  result += EscapeString(user_key.ToString());
+  result += buf;
+  return result;
+}
+
+std::string InternalKey::DebugString() const {
+  std::string result;
+  ParsedInternalKey parsed;
+  if (ParseInternalKey(rep_, &parsed)) {
+    result = parsed.DebugString();
+  } else {
+    result = "(bad)";
+    result.append(EscapeString(rep_));
+  }
+  return result;
+}
+
+const char* InternalKeyComparator::Name() const {
+  return "leveldb.InternalKeyComparator";
+}
+
+int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const {
+  // Order by:
+  //    increasing user key (according to user-supplied comparator)
+  //    decreasing sequence number
+  //    decreasing type (though sequence# should be enough to disambiguate)
+  int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey));
+  if (r == 0) {
+    const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8);
+    const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8);
+    if (anum > bnum) {
+      r = -1;
+    } else if (anum < bnum) {
+      r = +1;
+    }
+  }
+  return r;
+}
+
+void InternalKeyComparator::FindShortestSeparator(
+      std::string* start,
+      const Slice& limit) const {
+  // Attempt to shorten the user portion of the key
+  Slice user_start = ExtractUserKey(*start);
+  Slice user_limit = ExtractUserKey(limit);
+  std::string tmp(user_start.data(), user_start.size());
+  user_comparator_->FindShortestSeparator(&tmp, user_limit);
+  if (tmp.size() < user_start.size() &&
+      user_comparator_->Compare(user_start, tmp) < 0) {
+    // User key has become shorter physically, but larger logically.
+    // Tack on the earliest possible number to the shortened user key.
+    PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek));
+    assert(this->Compare(*start, tmp) < 0);
+    assert(this->Compare(tmp, limit) < 0);
+    start->swap(tmp);
+  }
+}
+
+void InternalKeyComparator::FindShortSuccessor(std::string* key) const {
+  Slice user_key = ExtractUserKey(*key);
+  std::string tmp(user_key.data(), user_key.size());
+  user_comparator_->FindShortSuccessor(&tmp);
+  if (tmp.size() < user_key.size() &&
+      user_comparator_->Compare(user_key, tmp) < 0) {
+    // User key has become shorter physically, but larger logically.
+    // Tack on the earliest possible number to the shortened user key.
+    PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek));
+    assert(this->Compare(*key, tmp) < 0);
+    key->swap(tmp);
+  }
+}
+
+const char* InternalFilterPolicy::Name() const {
+  return user_policy_->Name();
+}
+
+void InternalFilterPolicy::CreateFilter(const Slice* keys, int n,
+                                        std::string* dst) const {
+  // We rely on the fact that the code in table.cc does not mind us
+  // adjusting keys[].
+  Slice* mkey = const_cast<Slice*>(keys);
+  for (int i = 0; i < n; i++) {
+    mkey[i] = ExtractUserKey(keys[i]);
+    // TODO(sanjay): Suppress dups?
+  }
+  user_policy_->CreateFilter(keys, n, dst);
+}
+
+bool InternalFilterPolicy::KeyMayMatch(const Slice& key, const Slice& f) const {
+  return user_policy_->KeyMayMatch(ExtractUserKey(key), f);
+}
+
+LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) {
+  size_t usize = user_key.size();
+  size_t needed = usize + 13;  // A conservative estimate
+  char* dst;
+  if (needed <= sizeof(space_)) {
+    dst = space_;
+  } else {
+    dst = new char[needed];
+  }
+  start_ = dst;
+  dst = EncodeVarint32(dst, usize + 8);
+  kstart_ = dst;
+  memcpy(dst, user_key.data(), usize);
+  dst += usize;
+  EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek));
+  dst += 8;
+  end_ = dst;
+}
+
+}  // namespace leveldb

+ 227 - 0
ecoin/src/leveldb/db/dbformat.h

@@ -0,0 +1,227 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#ifndef STORAGE_LEVELDB_DB_FORMAT_H_
+#define STORAGE_LEVELDB_DB_FORMAT_H_
+
+#include <stdio.h>
+#include "leveldb/comparator.h"
+#include "leveldb/db.h"
+#include "leveldb/filter_policy.h"
+#include "leveldb/slice.h"
+#include "leveldb/table_builder.h"
+#include "util/coding.h"
+#include "util/logging.h"
+
+namespace leveldb {
+
+// Grouping of constants.  We may want to make some of these
+// parameters set via options.
+namespace config {
+static const int kNumLevels = 7;
+
+// Level-0 compaction is started when we hit this many files.
+static const int kL0_CompactionTrigger = 4;
+
+// Soft limit on number of level-0 files.  We slow down writes at this point.
+static const int kL0_SlowdownWritesTrigger = 8;
+
+// Maximum number of level-0 files.  We stop writes at this point.
+static const int kL0_StopWritesTrigger = 12;
+
+// Maximum level to which a new compacted memtable is pushed if it
+// does not create overlap.  We try to push to level 2 to avoid the
+// relatively expensive level 0=>1 compactions and to avoid some
+// expensive manifest file operations.  We do not push all the way to
+// the largest level since that can generate a lot of wasted disk
+// space if the same key space is being repeatedly overwritten.
+static const int kMaxMemCompactLevel = 2;
+
+}  // namespace config
+
+class InternalKey;
+
+// Value types encoded as the last component of internal keys.
+// DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk
+// data structures.
+enum ValueType {
+  kTypeDeletion = 0x0,
+  kTypeValue = 0x1
+};
+// kValueTypeForSeek defines the ValueType that should be passed when
+// constructing a ParsedInternalKey object for seeking to a particular
+// sequence number (since we sort sequence numbers in decreasing order
+// and the value type is embedded as the low 8 bits in the sequence
+// number in internal keys, we need to use the highest-numbered
+// ValueType, not the lowest).
+static const ValueType kValueTypeForSeek = kTypeValue;
+
+typedef uint64_t SequenceNumber;
+
+// We leave eight bits empty at the bottom so a type and sequence#
+// can be packed together into 64-bits.
+static const SequenceNumber kMaxSequenceNumber =
+    ((0x1ull << 56) - 1);
+
+struct ParsedInternalKey {
+  Slice user_key;
+  SequenceNumber sequence;
+  ValueType type;
+
+  ParsedInternalKey() { }  // Intentionally left uninitialized (for speed)
+  ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t)
+      : user_key(u), sequence(seq), type(t) { }
+  std::string DebugString() const;
+};
+
+// Return the length of the encoding of "key".
+inline size_t InternalKeyEncodingLength(const ParsedInternalKey& key) {
+  return key.user_key.size() + 8;
+}
+
+// Append the serialization of "key" to *result.
+extern void AppendInternalKey(std::string* result,
+                              const ParsedInternalKey& key);
+
+// Attempt to parse an internal key from "internal_key".  On success,
+// stores the parsed data in "*result", and returns true.
+//
+// On error, returns false, leaves "*result" in an undefined state.
+extern bool ParseInternalKey(const Slice& internal_key,
+                             ParsedInternalKey* result);
+
+// Returns the user key portion of an internal key.
+inline Slice ExtractUserKey(const Slice& internal_key) {
+  assert(internal_key.size() >= 8);
+  return Slice(internal_key.data(), internal_key.size() - 8);
+}
+
+inline ValueType ExtractValueType(const Slice& internal_key) {
+  assert(internal_key.size() >= 8);
+  const size_t n = internal_key.size();
+  uint64_t num = DecodeFixed64(internal_key.data() + n - 8);
+  unsigned char c = num & 0xff;
+  return static_cast<ValueType>(c);
+}
+
+// A comparator for internal keys that uses a specified comparator for
+// the user key portion and breaks ties by decreasing sequence number.
+class InternalKeyComparator : public Comparator {
+ private:
+  const Comparator* user_comparator_;
+ public:
+  explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) { }
+  virtual const char* Name() const;
+  virtual int Compare(const Slice& a, const Slice& b) const;
+  virtual void FindShortestSeparator(
+      std::string* start,
+      const Slice& limit) const;
+  virtual void FindShortSuccessor(std::string* key) const;
+
+  const Comparator* user_comparator() const { return user_comparator_; }
+
+  int Compare(const InternalKey& a, const InternalKey& b) const;
+};
+
+// Filter policy wrapper that converts from internal keys to user keys
+class InternalFilterPolicy : public FilterPolicy {
+ private:
+  const FilterPolicy* const user_policy_;
+ public:
+  explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) { }
+  virtual const char* Name() const;
+  virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const;
+  virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const;
+};
+
+// Modules in this directory should keep internal keys wrapped inside
+// the following class instead of plain strings so that we do not
+// incorrectly use string comparisons instead of an InternalKeyComparator.
+class InternalKey {
+ private:
+  std::string rep_;
+ public:
+  InternalKey() { }   // Leave rep_ as empty to indicate it is invalid
+  InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) {
+    AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t));
+  }
+
+  void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); }
+  Slice Encode() const {
+    assert(!rep_.empty());
+    return rep_;
+  }
+
+  Slice user_key() const { return ExtractUserKey(rep_); }
+
+  void SetFrom(const ParsedInternalKey& p) {
+    rep_.clear();
+    AppendInternalKey(&rep_, p);
+  }
+
+  void Clear() { rep_.clear(); }
+
+  std::string DebugString() const;
+};
+
+inline int InternalKeyComparator::Compare(
+    const InternalKey& a, const InternalKey& b) const {
+  return Compare(a.Encode(), b.Encode());
+}
+
+inline bool ParseInternalKey(const Slice& internal_key,
+                             ParsedInternalKey* result) {
+  const size_t n = internal_key.size();
+  if (n < 8) return false;
+  uint64_t num = DecodeFixed64(internal_key.data() + n - 8);
+  unsigned char c = num & 0xff;
+  result->sequence = num >> 8;
+  result->type = static_cast<ValueType>(c);
+  result->user_key = Slice(internal_key.data(), n - 8);
+  return (c <= static_cast<unsigned char>(kTypeValue));
+}
+
+// A helper class useful for DBImpl::Get()
+class LookupKey {
+ public:
+  // Initialize *this for looking up user_key at a snapshot with
+  // the specified sequence number.
+  LookupKey(const Slice& user_key, SequenceNumber sequence);
+
+  ~LookupKey();
+
+  // Return a key suitable for lookup in a MemTable.
+  Slice memtable_key() const { return Slice(start_, end_ - start_); }
+
+  // Return an internal key (suitable for passing to an internal iterator)
+  Slice internal_key() const { return Slice(kstart_, end_ - kstart_); }
+
+  // Return the user key
+  Slice user_key() const { return Slice(kstart_, end_ - kstart_ - 8); }
+
+ private:
+  // We construct a char array of the form:
+  //    klength  varint32               <-- start_
+  //    userkey  char[klength]          <-- kstart_
+  //    tag      uint64
+  //                                    <-- end_
+  // The array is a suitable MemTable key.
+  // The suffix starting with "userkey" can be used as an InternalKey.
+  const char* start_;
+  const char* kstart_;
+  const char* end_;
+  char space_[200];      // Avoid allocation for short keys
+
+  // No copying allowed
+  LookupKey(const LookupKey&);
+  void operator=(const LookupKey&);
+};
+
+inline LookupKey::~LookupKey() {
+  if (start_ != space_) delete[] start_;
+}
+
+}  // namespace leveldb
+
+#endif  // STORAGE_LEVELDB_DB_FORMAT_H_

+ 112 - 0
ecoin/src/leveldb/db/dbformat_test.cc

@@ -0,0 +1,112 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#include "db/dbformat.h"
+#include "util/logging.h"
+#include "util/testharness.h"
+
+namespace leveldb {
+
+static std::string IKey(const std::string& user_key,
+                        uint64_t seq,
+                        ValueType vt) {
+  std::string encoded;
+  AppendInternalKey(&encoded, ParsedInternalKey(user_key, seq, vt));
+  return encoded;
+}
+
+static std::string Shorten(const std::string& s, const std::string& l) {
+  std::string result = s;
+  InternalKeyComparator(BytewiseComparator()).FindShortestSeparator(&result, l);
+  return result;
+}
+
+static std::string ShortSuccessor(const std::string& s) {
+  std::string result = s;
+  InternalKeyComparator(BytewiseComparator()).FindShortSuccessor(&result);
+  return result;
+}
+
+static void TestKey(const std::string& key,
+                    uint64_t seq,
+                    ValueType vt) {
+  std::string encoded = IKey(key, seq, vt);
+
+  Slice in(encoded);
+  ParsedInternalKey decoded("", 0, kTypeValue);
+
+  ASSERT_TRUE(ParseInternalKey(in, &decoded));
+  ASSERT_EQ(key, decoded.user_key.ToString());
+  ASSERT_EQ(seq, decoded.sequence);
+  ASSERT_EQ(vt, decoded.type);
+
+  ASSERT_TRUE(!ParseInternalKey(Slice("bar"), &decoded));
+}
+
+class FormatTest { };
+
+TEST(FormatTest, InternalKey_EncodeDecode) {
+  const char* keys[] = { "", "k", "hello", "longggggggggggggggggggggg" };
+  const uint64_t seq[] = {
+    1, 2, 3,
+    (1ull << 8) - 1, 1ull << 8, (1ull << 8) + 1,
+    (1ull << 16) - 1, 1ull << 16, (1ull << 16) + 1,
+    (1ull << 32) - 1, 1ull << 32, (1ull << 32) + 1
+  };
+  for (int k = 0; k < sizeof(keys) / sizeof(keys[0]); k++) {
+    for (int s = 0; s < sizeof(seq) / sizeof(seq[0]); s++) {
+      TestKey(keys[k], seq[s], kTypeValue);
+      TestKey("hello", 1, kTypeDeletion);
+    }
+  }
+}
+
+TEST(FormatTest, InternalKeyShortSeparator) {
+  // When user keys are same
+  ASSERT_EQ(IKey("foo", 100, kTypeValue),
+            Shorten(IKey("foo", 100, kTypeValue),
+                    IKey("foo", 99, kTypeValue)));
+  ASSERT_EQ(IKey("foo", 100, kTypeValue),
+            Shorten(IKey("foo", 100, kTypeValue),
+                    IKey("foo", 101, kTypeValue)));
+  ASSERT_EQ(IKey("foo", 100, kTypeValue),
+            Shorten(IKey("foo", 100, kTypeValue),
+                    IKey("foo", 100, kTypeValue)));
+  ASSERT_EQ(IKey("foo", 100, kTypeValue),
+            Shorten(IKey("foo", 100, kTypeValue),
+                    IKey("foo", 100, kTypeDeletion)));
+
+  // When user keys are misordered
+  ASSERT_EQ(IKey("foo", 100, kTypeValue),
+            Shorten(IKey("foo", 100, kTypeValue),
+                    IKey("bar", 99, kTypeValue)));
+
+  // When user keys are different, but correctly ordered
+  ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek),
+            Shorten(IKey("foo", 100, kTypeValue),
+                    IKey("hello", 200, kTypeValue)));
+
+  // When start user key is prefix of limit user key
+  ASSERT_EQ(IKey("foo", 100, kTypeValue),
+            Shorten(IKey("foo", 100, kTypeValue),
+                    IKey("foobar", 200, kTypeValue)));
+
+  // When limit user key is prefix of start user key
+  ASSERT_EQ(IKey("foobar", 100, kTypeValue),
+            Shorten(IKey("foobar", 100, kTypeValue),
+                    IKey("foo", 200, kTypeValue)));
+}
+
+TEST(FormatTest, InternalKeyShortestSuccessor) {
+  ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek),
+            ShortSuccessor(IKey("foo", 100, kTypeValue)));
+  ASSERT_EQ(IKey("\xff\xff", 100, kTypeValue),
+            ShortSuccessor(IKey("\xff\xff", 100, kTypeValue)));
+}
+
+}  // namespace leveldb
+
+int main(int argc, char** argv) {
+  return leveldb::test::RunAllTests();
+}

+ 139 - 0
ecoin/src/leveldb/db/filename.cc

@@ -0,0 +1,139 @@
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#include <ctype.h>
+#include <stdio.h>
+#include "db/filename.h"
+#include "db/dbformat.h"
+#include "leveldb/env.h"
+#include "util/logging.h"
+
+namespace leveldb {
+
+// A utility routine: write "data" to the named file and Sync() it.
+extern Status WriteStringToFileSync(Env* env, const Slice& data,
+                                    const std::string& fname);
+
+static std::string MakeFileName(const std::string& name, uint64_t number,
+                                const char* suffix) {
+  char buf[100];
+  snprintf(buf, sizeof(buf), "/%06llu.%s",
+           static_cast<unsigned long long>(number),
+           suffix);
+  return name + buf;
+}
+
+std::string LogFileName(const std::string& name, uint64_t number) {
+  assert(number > 0);
+  return MakeFileName(name, number, "log");
+}
+
+std::string TableFileName(const std::string& name, uint64_t number) {
+  assert(number > 0);
+  return MakeFileName(name, number, "sst");
+}
+
+std::string DescriptorFileName(const std::string& dbname, uint64_t number) {
+  assert(number > 0);
+  char buf[100];
+  snprintf(buf, sizeof(buf), "/MANIFEST-%06llu",
+           static_cast<unsigned long long>(number));
+  return dbname + buf;
+}
+
+std::string CurrentFileName(const std::string& dbname) {
+  return dbname + "/CURRENT";
+}
+
+std::string LockFileName(const std::string& dbname) {
+  return dbname + "/LOCK";
+}
+
+std::string TempFileName(const std::string& dbname, uint64_t number) {
+  assert(number > 0);
+  return MakeFileName(dbname, number, "dbtmp");
+}
+
+std::string InfoLogFileName(const std::string& dbname) {
+  return dbname + "/LOG";
+}
+
+// Return the name of the old info log file for "dbname".
+std::string OldInfoLogFileName(const std::string& dbname) {
+  return dbname + "/LOG.old";
+}
+
+
+// Owned filenames have the form:
+//    dbname/CURRENT
+//    dbname/LOCK
+//    dbname/LOG
+//    dbname/LOG.old
+//    dbname/MANIFEST-[0-9]+
+//    dbname/[0-9]+.(log|sst)
+bool ParseFileName(const std::string& fname,
+                   uint64_t* number,
+                   FileType* type) {
+  Slice rest(fname);
+  if (rest == "CURRENT") {
+    *number = 0;
+    *type = kCurrentFile;
+  } else if (rest == "LOCK") {
+    *number = 0;
+    *type = kDBLockFile;
+  } else if (rest == "LOG" || rest == "LOG.old") {
+    *number = 0;
+    *type = kInfoLogFile;
+  } else if (rest.starts_with("MANIFEST-")) {
+    rest.remove_prefix(strlen("MANIFEST-"));
+    uint64_t num;
+    if (!ConsumeDecimalNumber(&rest, &num)) {
+      return false;
+    }
+    if (!rest.empty()) {
+      return false;
+    }
+    *type = kDescriptorFile;
+    *number = num;
+  } else {
+    // Avoid strtoull() to keep filename format independent of the
+    // current locale
+    uint64_t num;
+    if (!ConsumeDecimalNumber(&rest, &num)) {
+      return false;
+    }
+    Slice suffix = rest;
+    if (suffix == Slice(".log")) {
+      *type = kLogFile;
+    } else if (suffix == Slice(".sst")) {
+      *type = kTableFile;
+    } else if (suffix == Slice(".dbtmp")) {
+      *type = kTempFile;
+    } else {
+      return false;
+    }
+    *number = num;
+  }
+  return true;
+}
+
+Status SetCurrentFile(Env* env, const std::string& dbname,
+                      uint64_t descriptor_number) {
+  // Remove leading "dbname/" and add newline to manifest file name
+  std::string manifest = DescriptorFileName(dbname, descriptor_number);
+  Slice contents = manifest;
+  assert(contents.starts_with(dbname + "/"));
+  contents.remove_prefix(dbname.size() + 1);
+  std::string tmp = TempFileName(dbname, descriptor_number);
+  Status s = WriteStringToFileSync(env, contents.ToString() + "\n", tmp);
+  if (s.ok()) {
+    s = env->RenameFile(tmp, CurrentFileName(dbname));
+  }
+  if (!s.ok()) {
+    env->DeleteFile(tmp);
+  }
+  return s;
+}
+
+}  // namespace leveldb

+ 0 - 0
ecoin/src/leveldb/db/filename.h


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