diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..912eeb9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +src/diamondd +src/test_diamond + +# Ignore build dir +build/ + +# Compilation and Qt preprocessor part +*.qm +Makefile +paycoin-qt + +# Resources cpp +qrc_*.cpp + +# Qt creator +*.pro.user + +# Mac specific +.DS_Store diff --git a/COPYING b/COPYING index 8a76dbc..1554b1a 100644 --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -Copyright (c) 2014 Diamond Developers +Copyright (c) 2013-2016 Diamond Foundation Copyright (c) 2011-2012 PPCoin Developers Copyright (c) 2009-2012 Bitcoin Developers diff --git a/README.md b/README.md index e6e0674..026f3e4 100644 --- a/README.md +++ b/README.md @@ -1 +1,46 @@ +------------------------------------------ +Diamond (DMD) Old Version - Do not use this Source !! +--- + + +please be aware this codebase is soon set inactive +DMD Diamond move forward to DMDv3 codebase +balance from DMDv2 addresses will be available with same private keys in DMDv3 + +to stay up to date following us on bitcointalk +https://bitcointalk.org/index.php?topic=580725.0 +and twitter @dmdcoin + + +------------------------------------------ Diamond (DMD) + +Instructions for compiling in Linux. + +Update ubuntu + + sudo apt-get update + +*install git to download the source code + + sudo apt-get install git + +*install the other necessary components + + sudo apt-get install build-essential libboost1.55-all-dev libcurl4-openssl-dev libdb5.1-dev libdb5.1++-dev + +*navigate to the home directory + + cd ~ *download the diamond source code + + git clone https://github.com/DMDcoin/Diamond.git + +*navigate to the downloaded files + + cd ~ *navigate to the src file in the source code + cd Diamond/src + + +*build diamondd !This will take a while! + + make -f makefile.unix USE_UPNP=- diff --git a/contrib/macdeploy/fancy.plist b/contrib/macdeploy/fancy.plist index 08c2a06..f7668b2 100644 --- a/contrib/macdeploy/fancy.plist +++ b/contrib/macdeploy/fancy.plist @@ -22,7 +22,7 @@ 370 156 - NovaCoin-Qt.app + Diamond-Qt.app 128 156 diff --git a/contrib/macdeploy/notes.txt b/contrib/macdeploy/notes.txt index 9b592fc..fd50d23 100644 --- a/contrib/macdeploy/notes.txt +++ b/contrib/macdeploy/notes.txt @@ -6,7 +6,7 @@ You will need the appscript package for the fancy disk image creation to work. Install it by invoking "sudo easy_install appscript". Ths script should be invoked in the target directory like this: -$source_dir/contrib/macdeploy/macdeployqtplus NovaCoin-Qt.app -add-qt-tr da,de,es,hu,ru,uk,zh_CN,zh_TW -dmg -fancy $source_dir/contrib/macdeploy/fancy.plist -verbose 2 +$source_dir/contrib/macdeploy/macdeployqtplus Diamond-Qt.app -add-qt-tr da,de,es,hu,ru,uk,zh_CN,zh_TW -dmg -fancy $source_dir/contrib/macdeploy/fancy.plist -verbose 2 During the process, the disk image window will pop up briefly where the fancy settings are applied. This is normal, please do not interfere. @@ -19,8 +19,8 @@ Fill in the following. Enable custom process step: [x] Command: %{sourceDir}/contrib/macdeploy/macdeployqtplus Working directory: %{buildDir} -Command arguments: NovaCoin-Qt.app -add-qt-tr da,de,es,hu,ru,uk,zh_CN,zh_TW -dmg -fancy %{sourceDir}/contrib/macdeploy/fancy.plist -verbose 2 +Command arguments: Diamond-Qt.app -add-qt-tr da,de,es,hu,ru,uk,zh_CN,zh_TW -dmg -fancy %{sourceDir}/contrib/macdeploy/fancy.plist -verbose 2 After that you can start the deployment process through the menu with -Build -> Deploy Project "novacoin-qt" +Build -> Deploy Project "diamond-qt" diff --git a/diamond.pro b/diamond.pro index fa29a64..f4d851d 100644 --- a/diamond.pro +++ b/diamond.pro @@ -1,6 +1,6 @@ TEMPLATE = app TARGET = diamond-qt -VERSION = 0.1.0.2 +VERSION = 2.1.0.4 INCLUDEPATH += src src/json src/qt QT += core gui network DEFINES += QT_GUI BOOST_THREAD_USE_LIB BOOST_SPIRIT_THREADSAFE BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN __NO_SYSTEM_INCLUDES @@ -15,23 +15,34 @@ UI_DIR = build # UNCOMMENT THIS SECTION TO BUILD ON WINDOWS # Change paths if needed, these use the foocoin/deps.git repository locations +win32 { BOOST_LIB_SUFFIX=-mgw48-mt-s-1_55 BOOST_INCLUDE_PATH=C:/deps/boost_1_55_0 BOOST_LIB_PATH=C:/deps/boost_1_55_0/stage/lib BDB_INCLUDE_PATH=C:/deps/db-4.8.30.NC/build_windows BDB_LIB_PATH=C:/deps/db-4.8.30.NC/build_windows -OPENSSL_INCLUDE_PATH=C:/deps/openssl-1.0.1g/include -OPENSSL_LIB_PATH=C:/deps/openssl-1.0.1g +OPENSSL_INCLUDE_PATH=C:/deps/openssl-1.0.1h/include +OPENSSL_LIB_PATH=C:/deps/openssl-1.0.1h MINIUPNPC_INCLUDE_PATH=C:/deps -LIBPNG_INCLUDE_PATH=C:/deps/libpng-1.6.8 -LIBPNG_LIB_PATH=C:/deps/libpng-1.6.8/.libs +LIBPNG_INCLUDE_PATH=C:/deps/libpng-1.6.10 +LIBPNG_LIB_PATH=C:/deps/libpng-1.6.10/.libs MINIUPNPC_LIB_PATH=C:/deps/miniupnpc +QRENCODE_INCLUDE_PATH=C:/deps/qrencode-3.4.3 QRENCODE_LIB_PATH=C:/deps/qrencode-3.4.3/.libs +RELEASE=1 +CONFIG += static +} + +# OS X Mavericks 10.9 build +macx:QMAKE_MAC_SDK = macosx10.9 +macx:QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.9 +macx:USE_QRCODE=1 # 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 i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk +# macx:QMAKE_CXXFLAGS += -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk + !windows:!macx { # Linux: static link @@ -48,6 +59,7 @@ QMAKE_LFLAGS *= -fstack-protector-all --param ssp-buffer-size=1 } # for extra security on Windows: enable ASLR and DEP via GCC linker flags win32:QMAKE_LFLAGS *= -Wl,--dynamicbase -Wl,--nxcompat +win32:QMAKE_LFLAGS *= -Wl,--large-address-aware -static lessThan(QT_MAJOR_VERSION, 5): win32: QMAKE_LFLAGS *= -static # use: qmake "USE_QRCODE=1" @@ -111,8 +123,27 @@ contains(BITCOIN_NEED_QT_PLUGINS, 1) { DEFINES += HAVE_BUILD_INFO } -QMAKE_CXXFLAGS += -msse2 -QMAKE_CFLAGS += -msse2 +# If we have an ARM device, we can't use SSE2 instructions, so don't try to use them +# Because of scrypt_mine.cpp, we also have to add a compile +# flag that states we *really* don't have SSE +QMAKE_XCPUARCH = $$QMAKE_HOST.arch +equals(QMAKE_XCPUARCH, armv7l) { + message(Building without SSE2 support) + QMAKE_CXXFLAGS += -DNOSSE + QMAKE_CFLAGS += -DNOSSE +} +else:equals(QMAKE_XCPUARCH, armv6l) { + message(Building without SSE2 support) + QMAKE_CXXFLAGS += -DNOSSE + QMAKE_CFLAGS += -DNOSSE +} +else { + message(Building with SSE2 support) + QMAKE_CXXFLAGS += -msse2 + QMAKE_CFLAGS += -msse2 +} +#endif + QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter -Wstack-protector # Input @@ -121,6 +152,8 @@ HEADERS += src/qt/bitcoingui.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 \ @@ -133,6 +166,7 @@ HEADERS += src/qt/bitcoingui.h \ src/bignum.h \ src/checkpoints.h \ src/compat.h \ + src/coincontrol.h \ src/sync.h \ src/util.h \ src/uint256.h \ @@ -194,13 +228,17 @@ HEADERS += src/qt/bitcoingui.h \ src/hash.h \ src/scrypt.h \ src/sph_groestl.h \ - src/sph_types.h + src/sph_types.h \ + src/auxpow.h \ + src/reactors.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.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 \ @@ -258,16 +296,21 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/kernel.cpp \ src/scrypt-x86.S \ src/scrypt-x86_64.S \ + src/scrypt-arm.S \ src/scrypt_mine.cpp \ src/pbkdf2.cpp \ src/hash.cpp \ src/scrypt.cpp \ - src/groestl.c + src/groestl.c \ + src/auxpow.cpp \ + src/reactors.cpp \ + src/reactorlist.cpp RESOURCES += \ src/qt/bitcoin.qrc FORMS += \ + src/qt/forms/coincontroldialog.ui \ src/qt/forms/sendcoinsdialog.ui \ src/qt/forms/addressbookpage.ui \ src/qt/forms/signverifymessagedialog.ui \ @@ -372,7 +415,7 @@ 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/bitcoin.icns +macx:ICON = src/qt/res/icons/Diamond.icns macx:TARGET = "diamond-qt" macx:QMAKE_CFLAGS_THREAD += -pthread macx:QMAKE_LFLAGS_THREAD += -pthread diff --git a/doc/README b/doc/README index b567cb8..902345c 100644 --- a/doc/README +++ b/doc/README @@ -1,10 +1,6 @@ +Diamond 2.0.5.7 -Copyright (c) 2013-2014 Diamond Developers - - -Diamond 0.3.0 BETA - -Copyright (c) 2013-2014 Diamond Developers +Copyright (c) 2013-2015 Diamond Foundation Copyright (c) 2013 NovaCoin Developers Copyright (c) 2011-2012 Bitcoin Developers Distributed under the MIT/X11 software license, see the accompanying @@ -45,6 +41,6 @@ Start up the new diamondd. See the documentation/wiki at the Diamond site: - http://diamond.cc/ + http://bit.diamonds/ for help and more information. diff --git a/doc/README_windows.txt b/doc/README_windows.txt index 68f0a9e..12aaa1c 100644 --- a/doc/README_windows.txt +++ b/doc/README_windows.txt @@ -1,10 +1,6 @@ +Diamond 2.0.5.7 -Copyright (c) 2013-2014 Diamond Developers - - -Diamond 0.3.0 BETA - -Copyright (c) 2013-2014 Diamond Developers +Copyright (c) 2013-2015 Diamond Foundation Copyright (c) 2013 NovaCoin Developers Copyright (c) 2011-2012 Bitcoin Developers Distributed under the MIT/X11 software license, see the accompanying @@ -47,6 +43,6 @@ Start up the new diamondd. See the documentation/wiki at the Diamond site: - http://diamond.cc/ + http://bit.diamonds/ for help and more information. diff --git a/doc/build-unix.txt b/doc/build-unix.txt index 3dffb22..3388063 100644 --- a/doc/build-unix.txt +++ b/doc/build-unix.txt @@ -10,16 +10,26 @@ software written by Thomas Bernard. UNIX BUILD NOTES ================ -To Build +To Build On i386, amd64 -------- cd src/ -make -f makefile.unix # Headless bitcoin +make -f makefile.unix # Headless diamond + +To Build On armv6l +-------- +cd src/ +make -f makefile.unix xCPUARCH=armv6l # Headless diamond + +To Build On armv7l +-------- +cd src/ +make -f makefile.unix xCPUARCH=armv7l # Headless diamond See readme-qt.rst for instructions on building Bitcoin QT, the graphical bitcoin. -Dependencies +Dependencies for i386, amd64 ------------ Library Purpose Description @@ -29,6 +39,19 @@ Dependencies libboost Boost C++ Library miniupnpc UPnP Support Optional firewall-jumping support libqrencode QRCode generation Optional QRCode generation + libgmp3 GMP Multiple precision arithmetic library + +Dependencies for armv6l, armv7l +------------ + + Library Purpose Description + ------- ------- ----------- + libssl SSL Support Secure communications + libdb5.1 Berkeley DB Blockchain & wallet storage + libboost Boost C++ Library + miniupnpc UPnP Support Optional firewall-jumping support + libqrencode QRCode generation Optional QRCode generation + libgmp3 GMP Multiple precision arithmetic library Note that libexecinfo should be installed, if you building under *BSD systems. This library provides backtrace facility. diff --git a/doc/readme-qt.rst b/doc/readme-qt.rst index 294f31a..faa28ea 100644 --- a/doc/readme-qt.rst +++ b/doc/readme-qt.rst @@ -1,20 +1,20 @@ -Bitcoin-qt: Qt4 GUI for Bitcoin +Diamond-qt: Qt5 GUI for Diamond =============================== Build instructions =================== -Debian +Debian (i386, amd64) ------- -First, make sure that the required packages for Qt4 development of your -distribution are installed, for Debian and Ubuntu these are: +First, make sure that the required packages for Qt5 development of your +distribution are installed, for Debian and Ubuntu (i386, amd64) these are: :: - apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \ + apt-get install qt5-qmake libqt5-dev build-essential libboost-dev libboost-system-dev \ libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ - libssl-dev libdb4.8++-dev + libssl-dev libdb4.8++-dev libminiupnpc-dev libminiupnpc8 then execute the following: @@ -23,9 +23,28 @@ then execute the following: qmake make -Alternatively, install Qt Creator and open the `bitcoin-qt.pro` file. +Debian (armv6l, armv7l) +------- + +First, make sure that the required packages for Qt5 development of your +distribution are installed, for Debian and Ubuntu (i386, amd64) these are: + +:: + + apt-get install qt5-qmake libqt5-dev build-essential libboost-dev libboost-system-dev \ + libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ + libssl-dev libdb5.1++-dev libminiupnpc-dev libminiupnpc8 + +then execute the following: + +:: + + qmake + make + +Alternatively, install Qt Creator and open the `diamond.pro` file. -An executable named `bitcoin-qt` will be built. +An executable named `diamond-qt` will be built. Windows @@ -59,7 +78,7 @@ Mac OS X :: sudo port selfupdate - sudo port install boost db48 miniupnpc + sudo port install boost db48 miniupnpc qrencode - Open the .pro file in Qt Creator and build as normal (cmd-B) @@ -138,7 +157,7 @@ Ubuntu 11.10 warning ==================== Ubuntu 11.10 has a package called 'qt-at-spi' installed by default. At the time of writing, having that package -installed causes bitcoin-qt to crash intermittently. The issue has been reported as `launchpad bug 857790`_, but +installed causes Diamond-qt to crash intermittently. The issue has been reported as `launchpad bug 857790`_, but isn't yet fixed. Until the bug is fixed, you can remove the qt-at-spi package to work around the problem, though this will presumably diff --git a/src/auxpow.cpp b/src/auxpow.cpp new file mode 100644 index 0000000..f8248c8 --- /dev/null +++ b/src/auxpow.cpp @@ -0,0 +1,129 @@ +// Copyright (c) 2011 Vince Durham +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#include "script.h" +#include "auxpow.h" +#include "init.h" + +using namespace std; +using namespace boost; + +unsigned char pchMergedMiningHeader[] = { 0xfa, 0xbe, 'm', 'm' } ; + +void RemoveMergedMiningHeader(vector& vchAux) +{ + if (vchAux.begin() != std::search(vchAux.begin(), vchAux.end(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader))) + throw runtime_error("merged mining aux too short"); + vchAux.erase(vchAux.begin(), vchAux.begin() + sizeof(pchMergedMiningHeader)); +} + +bool CAuxPow::Check(uint256 hashAuxBlock, int nChainID) +{ + if (nIndex != 0) + return error("AuxPow is not a generate"); + + if (!fTestNet && parentBlock.GetChainID() == nChainID) + return error("Aux POW parent has our chain ID"); + + if (vChainMerkleBranch.size() > 30) + return error("Aux POW chain merkle branch too long"); + + // Check that the chain merkle root is in the coinbase + uint256 nRootHash = CBlock::CheckMerkleBranch(hashAuxBlock, vChainMerkleBranch, nChainIndex); + vector vchRootHash(nRootHash.begin(), nRootHash.end()); + std::reverse(vchRootHash.begin(), vchRootHash.end()); // correct endian + + // Check that we are in the parent block merkle tree + if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != parentBlock.hashMerkleRoot) + return error("Aux POW merkle root incorrect"); + + const CScript script = vin[0].scriptSig; + + // Check that the same work is not submitted twice to our chain. + // + + CScript::const_iterator pcHead = + std::search(script.begin(), script.end(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader)); + + CScript::const_iterator pc = + std::search(script.begin(), script.end(), vchRootHash.begin(), vchRootHash.end()); + + if (pc == script.end()) + return error("Aux POW missing chain merkle root in parent coinbase"); + + if (pcHead != script.end()) + { + // Enforce only one chain merkle root by checking that a single instance of the merged + // mining header exists just before. + if (script.end() != std::search(pcHead + 1, script.end(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader))) + return error("Multiple merged mining headers in coinbase"); + if (pcHead + sizeof(pchMergedMiningHeader) != pc) + return error("Merged mining header is not just before chain merkle root"); + } + else + { + // For backward compatibility. + // Enforce only one chain merkle root by checking that it starts early in the coinbase. + // 8-12 bytes are enough to encode extraNonce and nBits. + if (pc - script.begin() > 20) + return error("Aux POW chain merkle root must start in the first 20 bytes of the parent coinbase"); + } + + + // Ensure we are at a deterministic point in the merkle leaves by hashing + // a nonce and our chain ID and comparing to the index. + pc += vchRootHash.size(); + if (script.end() - pc < 8) + return error("Aux POW missing chain merkle tree size and nonce in parent coinbase"); + + int nSize; + memcpy(&nSize, &pc[0], 4); + if (nSize != (1 << vChainMerkleBranch.size())) + return error("Aux POW merkle branch size does not match parent coinbase"); + + int nNonce; + memcpy(&nNonce, &pc[4], 4); + + // Choose a pseudo-random slot in the chain merkle tree + // but have it be fixed for a size/nonce/chain combination. + // + // This prevents the same work from being used twice for the + // same chain while reducing the chance that two chains clash + // for the same slot. + int rand = nNonce; + rand = rand * 1103515245 + 12345; + rand += nChainID; + rand = rand * 1103515245 + 12345; + + if (nChainIndex != (rand % nSize)) + return error("Aux POW wrong index"); + + return true; +} + +CScript MakeCoinbaseWithAux(unsigned int nHeight, unsigned int nExtraNonce, + vector& vchAux) { + vector vchAuxWithHeader(UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader)); + vchAuxWithHeader.insert(vchAuxWithHeader.end(), vchAux.begin(), vchAux.end()); + + // Push OP_2 just in case we want versioning later + /* BIP34: v2 coin base must start with nHeight */ + return(((CScript() << nHeight << CBigNum(nExtraNonce)) + COINBASE_FLAGS) << OP_2 << vchAuxWithHeader); +} + + +void IncrementExtraNonceWithAux(CBlock* pblock, CBlockIndex* pindexPrev, + unsigned int& nExtraNonce, vector& vchAux) { + + // Update nExtraNonce + static uint256 hashPrevBlock; + if (hashPrevBlock != pblock->hashPrevBlock) + { + nExtraNonce = 0; + hashPrevBlock = pblock->hashPrevBlock; + } + ++nExtraNonce; + + pblock->vtx[0].vin[0].scriptSig = MakeCoinbaseWithAux(pindexPrev->nHeight + 1, nExtraNonce, vchAux); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); +} diff --git a/src/auxpow.h b/src/auxpow.h new file mode 100644 index 0000000..854d172 --- /dev/null +++ b/src/auxpow.h @@ -0,0 +1,86 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_AUXPOW_H +#define BITCOIN_AUXPOW_H + +#include "main.h" + +class CAuxPow : public CMerkleTx +{ +public: + CAuxPow(const CTransaction& txIn) : CMerkleTx(txIn) + { + } + + CAuxPow() :CMerkleTx() + { + } + + // Merkle branch with root vchAux + // root must be present inside the coinbase + std::vector vChainMerkleBranch; + // Index of chain in chains merkle tree + int nChainIndex; + CBlock parentBlock; + + IMPLEMENT_SERIALIZE + ( + nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion, ser_action); + nVersion = this->nVersion; + READWRITE(vChainMerkleBranch); + READWRITE(nChainIndex); + + // Always serialize the saved parent block as header so that the size of CAuxPow + // is consistent. + nSerSize += SerReadWrite(s, parentBlock, nType | SER_BLOCKHEADERONLY, nVersion, ser_action); + ) + + bool Check(uint256 hashAuxBlock, int nChainID); + + uint256 GetParentBlockHash() { + return(parentBlock.GetHashGroestl()); + } +}; + +template +int ReadWriteAuxPow(Stream& s, const boost::shared_ptr& auxpow, int nType, int nVersion, CSerActionGetSerializeSize ser_action) +{ + if (nVersion & BLOCK_VERSION_AUXPOW) + { + return ::GetSerializeSize(*auxpow, nType, nVersion); + } + return 0; +} + +template +int ReadWriteAuxPow(Stream& s, const boost::shared_ptr& auxpow, int nType, int nVersion, CSerActionSerialize ser_action) +{ + if (nVersion & BLOCK_VERSION_AUXPOW) + { + return SerReadWrite(s, *auxpow, nType, nVersion, ser_action); + } + return 0; +} + +template +int ReadWriteAuxPow(Stream& s, boost::shared_ptr& auxpow, int nType, int nVersion, CSerActionUnserialize ser_action) +{ + if (nVersion & BLOCK_VERSION_AUXPOW) + { + auxpow.reset(new CAuxPow()); + return SerReadWrite(s, *auxpow, nType, nVersion, ser_action); + } + else + { + auxpow.reset(); + return 0; + } +} + +extern void RemoveMergedMiningHeader(std::vector& vchAux); +extern void IncrementExtraNonceWithAux(CBlock* pblock, CBlockIndex* pindexPrev, + unsigned int& nExtraNonce, std::vector& vchAux); +extern CScript MakeCoinbaseWithAux(unsigned int nBits, unsigned int nExtraNonce, + std::vector& vchAux); +#endif diff --git a/src/base58.h b/src/base58.h index 3451e7a..2c616a9 100644 --- a/src/base58.h +++ b/src/base58.h @@ -280,7 +280,6 @@ class CBitcoinAddress : public CBase58Data SCRIPT_ADDRESS = 8, PUBKEY_ADDRESS_TEST = 111, SCRIPT_ADDRESS_TEST = 196, - }; bool Set(const CKeyID &id) { @@ -403,10 +402,16 @@ bool inline CBitcoinAddressVisitor::operator()(const CNoDestination &id) const { class CBitcoinSecret : public CBase58Data { public: + enum + { + PRIVKEY_ADDRESS = CBitcoinAddress::PUBKEY_ADDRESS + 128, + PRIVKEY_ADDRESS_TEST = CBitcoinAddress::PUBKEY_ADDRESS_TEST + 128, + }; + void SetSecret(const CSecret& vchSecret, bool fCompressed) { assert(vchSecret.size() == 32); - SetData(128 + (fTestNet ? CBitcoinAddress::PUBKEY_ADDRESS_TEST : CBitcoinAddress::PUBKEY_ADDRESS), &vchSecret[0], vchSecret.size()); + SetData(fTestNet ? PRIVKEY_ADDRESS_TEST : PRIVKEY_ADDRESS, &vchSecret[0], vchSecret.size()); if (fCompressed) vchData.push_back(1); } @@ -425,10 +430,10 @@ class CBitcoinSecret : public CBase58Data bool fExpectTestNet = false; switch(nVersion) { - case (128 + CBitcoinAddress::PUBKEY_ADDRESS): + case PRIVKEY_ADDRESS: break; - case (128 + CBitcoinAddress::PUBKEY_ADDRESS_TEST): + case PRIVKEY_ADDRESS_TEST: fExpectTestNet = true; break; diff --git a/src/bignum.h b/src/bignum.h index c214275..c04fb0b 100644 --- a/src/bignum.h +++ b/src/bignum.h @@ -250,7 +250,6 @@ class CBigNum : public BIGNUM return n; } - void setvch(const std::vector& vch) { std::vector vch2(vch.size() + 4); diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 725e127..a2f4c68 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -208,8 +208,9 @@ static const CRPCCommand vRPCCommands[] = { "getpeerinfo", &getpeerinfo, true, false }, { "getdifficulty", &getdifficulty, true, false }, { "getgenerate", &getgenerate, true, false }, - //{ "setgenerate", &setgenerate, true, false }, + { "setgenerate", &setgenerate, true, false }, { "gethashespersec", &gethashespersec, true, false }, + { "getnetworkhashps", &getnetworkhashps, true, false }, { "getinfo", &getinfo, true, false }, { "getmininginfo", &getmininginfo, true, false }, { "getnewaddress", &getnewaddress, true, false }, @@ -246,10 +247,11 @@ static const CRPCCommand vRPCCommands[] = { "signmessage", &signmessage, false, false }, { "verifymessage", &verifymessage, false, false }, { "getwork", &getwork, true, false }, - { "getworkex", &getworkex, true, false }, + { "getworkaux", &getworkaux, true, false }, { "listaccounts", &listaccounts, false, false }, { "settxfee", &settxfee, false, false }, { "getblocktemplate", &getblocktemplate, true, false }, + { "getauxblock", &getauxblock, true, false }, { "submitblock", &submitblock, false, false }, { "listsinceblock", &listsinceblock, false, false }, { "dumpprivkey", &dumpprivkey, false, false }, @@ -261,12 +263,19 @@ static const CRPCCommand vRPCCommands[] = { "signrawtransaction", &signrawtransaction, false, false }, { "sendrawtransaction", &sendrawtransaction, false, false }, { "getcheckpoint", &getcheckpoint, true, false }, - { "reservebalance", &reservebalance, false, true}, - { "checkwallet", &checkwallet, false, true}, - { "repairwallet", &repairwallet, false, true}, - { "resendtx", &resendtx, false, true}, - { "makekeypair", &makekeypair, false, true}, - { "sendalert", &sendalert, false, false}, + { "reservebalance", &reservebalance, false, true }, + { "checkwallet", &checkwallet, false, true }, + { "repairwallet", &repairwallet, false, true }, + { "resendtx", &resendtx, false, true }, + { "makekeypair", &makekeypair, false, true }, + { "sendalert", &sendalert, false, false }, + { "setchangeaddress", &setchangeaddress, true, false }, + { "getchangeaddress", &getchangeaddress, true, false }, + { "getscrapeaddress", &getscrapeaddress, true, false }, + { "listscrapeaddresses", &listscrapeaddresses, true, false }, + { "setscrapeaddress", &setscrapeaddress, true, true }, + { "deletescrapeaddress", &deletescrapeaddress, true, true }, + { "listreactordata", &listreactordata, true, false } }; CRPCTable::CRPCTable() @@ -511,7 +520,8 @@ bool ClientAllowed(const boost::asio::ip::address& address) || address.to_v6().is_v4_mapped())) return ClientAllowed(address.to_v6().to_v4()); - std::string ipv4addr = address.to_string(); + // danbi: not used + // std::string ipv4addr = address.to_string(); if (address == asio::ip::address_v4::loopback() || address == asio::ip::address_v6::loopback() @@ -742,7 +752,7 @@ void ThreadRPCServer2(void* parg) uiInterface.ThreadSafeMessageBox(strprintf( _("%s, you must set a rpcpassword in the configuration file:\n %s\n" "It is recommended you use the following random password:\n" - "rpcuser=bitcoinrpc\n" + "rpcuser=diamondrpc\n" "rpcpassword=%s\n" "(you do not need to remember this password)\n" "If the file does not exist, create it with owner-readable-only file permissions.\n"), diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 09472eb..47d04c0 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -74,6 +74,9 @@ int CommandLineRPC(int argc, char *argv[]); /** Convert parameter values for RPC call from strings to command-specific JSON objects. */ json_spirit::Array RPCConvertValues(const std::string &strMethod, const std::vector &strParams); +/** Call the RPC service directly (placed here to allow for more in depth tests). */ +json_spirit::Object CallRPC(const std::string &strMethod, const json_spirit::Array ¶ms); + /* Type-check arguments; throws JSONRPCError if wrong type given. Does not check that the right number of arguments are passed, just that any passed are the correct type. @@ -127,6 +130,9 @@ 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 GetPoSKernelPS(); + extern std::string HexBits(unsigned int nBits); extern std::string HelpRequiringPassphrase(); extern void EnsureWalletIsUnlocked(); @@ -143,9 +149,10 @@ extern json_spirit::Value setgenerate(const json_spirit::Array& params, bool fHe extern json_spirit::Value gethashespersec(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getmininginfo(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 getworkaux(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getauxblock(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); @@ -185,6 +192,7 @@ extern json_spirit::Value makekeypair(const json_spirit::Array& params, bool fHe 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 getnetworkhashps(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listreactordata(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); @@ -201,5 +209,12 @@ extern json_spirit::Value getblockhash(const json_spirit::Array& params, bool fH 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); +extern json_spirit::Value setchangeaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getchangeaddress(const json_spirit::Array& params, bool fHelp); + +extern json_spirit::Value setscrapeaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getscrapeaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listscrapeaddresses(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value deletescrapeaddress(const json_spirit::Array& params, bool fHelp); #endif diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index fa9d3c1..8cc9d25 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -36,7 +36,10 @@ namespace Checkpoints (317997, uint256("0x000000007458dc82830320e592b9e0585efa68d01c3de479c14843af4942528e")) (386222, uint256("0x000006a8c97fcba6e69278f2c5e21c2266ed12ecea07045e9214cb23f3d5eeaa")) (386232, uint256("0x000004bf5ce28d4bf81a7f993de4443d5a5b2bd3290e7935f3096e5b7c40fe36")) - + (443923, uint256("0x0000000001f9b10759fa67418e4c20a772ec39c3eff33742cb5899c7b50d7348")) + (468356, uint256("0x000000000543a19eaf97aa0ad933cb6423dedacb09786f4891126ac02bd8b7d4")) + (895493, uint256("0x0000000003140f322a3913d0d67b77b462b0bcdd2a4a0ec0cd791de743cf9202")) + (1500000, uint256("0x0000000008040b20b2e7ebdea7cf0c57d43195ec1da886b652339cb87253f369")) ; static MapCheckpoints mapCheckpointsTestnet = @@ -359,8 +362,8 @@ namespace Checkpoints // 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()); + return((nBestHeight >= pindexSync->nHeight + nCoinbaseMaturity) || + (pindexSync->GetBlockTime() + nStakeMinAgeFixed < GetAdjustedTime())); } // Is the sync-checkpoint too old? diff --git a/src/checkpoints.h b/src/checkpoints.h index 4f5db11..46147fb 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -118,7 +118,7 @@ class CSyncCheckpoint : public CUnsignedSyncCheckpoint uint256 GetHash() const { - return SerializeHash(*this); + return(SerializeHash2(*this)); } bool RelayTo(CNode* pnode) const diff --git a/src/clientversion.h b/src/clientversion.h index 2bfa6e6..55df318 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -7,9 +7,9 @@ // These need to be macros, as version.cpp's and bitcoin-qt.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 2 -#define CLIENT_VERSION_MINOR 0 -#define CLIENT_VERSION_REVISION 1 -#define CLIENT_VERSION_BUILD 0 +#define CLIENT_VERSION_MINOR 1 +#define CLIENT_VERSION_REVISION 0 +#define CLIENT_VERSION_BUILD 4 // Converts the parameter X to a string after macro replacement on X has been performed. // Don't merge these into one macro! diff --git a/src/coincontrol.h b/src/coincontrol.h new file mode 100644 index 0000000..236b586 --- /dev/null +++ b/src/coincontrol.h @@ -0,0 +1,57 @@ +#ifndef COINCONTROL_H +#define COINCONTROL_H + +/** Coin Control Features. */ +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& vOutpoints) + { + vOutpoints.assign(setSelected.begin(), setSelected.end()); + } + +private: + std::set setSelected; + +}; + +#endif // COINCONTROL_H diff --git a/src/db.cpp b/src/db.cpp index 221cadf..dbde1a7 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -9,6 +9,7 @@ #include "util.h" #include "main.h" #include "kernel.h" +#include "auxpow.h" #include #include #include @@ -85,7 +86,11 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_) dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1); dbenv.set_lg_bsize(1048576); dbenv.set_lg_max(10485760); - dbenv.set_lk_max_locks(10000); + + // Bugfix: Bump lk_max_locks default to 537000, to safely handle reorgs with up to 5 blocks reversed + // dbenv.set_lk_max_locks(10000); + 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); @@ -106,6 +111,30 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_) fDbEnvInit = true; fMockDb = false; + + // Check that the number of locks is sufficient (to prevent chain fork possibility, read http://bitcoin.org/may15 for more info) + u_int32_t nMaxLocks; + if (!dbenv.get_lk_max_locks(&nMaxLocks)) + { + int nBlocks, nDeepReorg; + std::string strMessage; + + nBlocks = nMaxLocks / 48768; + nDeepReorg = (nBlocks - 1) / 2; + + printf("Final lk_max_locks is %lu, sufficient for (worst case) %d block%s in a single transaction (up to a %d-deep reorganization)\n", (unsigned long)nMaxLocks, nBlocks, (nBlocks == 1) ? "" : "s", nDeepReorg); + if (nDeepReorg < 3) + { + if (nBlocks < 1) + strMessage = strprintf(("Warning: DB_CONFIG has set_lk_max_locks %lu, which may be too low for a single block. If this limit is reached, Diamond may stop working."), (unsigned long)nMaxLocks); + else + strMessage = strprintf(("Warning: DB_CONFIG has set_lk_max_locks %lu, which may be too low for a common blockchain reorganization. If this limit is reached, Diamond may stop working."), (unsigned long)nMaxLocks); + + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + } + } + return true; } @@ -622,7 +651,6 @@ bool CTxDB::LoadBlockIndex() // Calculate bnChainTrust vector > vSortedByHeight; - map a = mapBlockIndex; vSortedByHeight.reserve(mapBlockIndex.size()); BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) { @@ -652,7 +680,6 @@ bool CTxDB::LoadBlockIndex() pindexBest = mapBlockIndex[hashBestChain]; nBestHeight = pindexBest->nHeight; bnBestChainTrust = pindexBest->bnChainTrust; - printf("LoadBlockIndex(): hashBestChain=%s height=%d trust=%s date=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainTrust.ToString().c_str(), DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); @@ -677,13 +704,16 @@ bool CTxDB::LoadBlockIndex() map, CBlockIndex*> mapBlockPos; for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) { + // calculate totalCoin for other routines that get called + totalCoin = pindex->nMoneySupply / COIN; if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth) break; CBlock block; if (!block.ReadFromDisk(pindex)) return error("LoadBlockIndex() : block.ReadFromDisk failed"); // check level 1: verify block validity - if (nCheckLevel>0 && !block.CheckBlock()) + // DK properly pass totalCoin + if (nCheckLevel>0 && !block.CheckBlock(true, true, totalCoin)) { printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); pindexFork = pindex->pprev; @@ -805,10 +835,12 @@ bool CTxDB::LoadBlockIndexGuts() if (!pcursor) return false; + int count=0; // Load mapBlockIndex unsigned int fFlags = DB_SET_RANGE; while (true) { + count++; // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) @@ -824,75 +856,117 @@ bool CTxDB::LoadBlockIndexGuts() // Unserialize try { - string strType; - ssKey >> strType; - if (strType == "blockindex" && !fRequestShutdown) - { - CDiskBlockIndex diskindex; - ssValue >> diskindex; - - totalCoinDB = diskindex.nMoneySupply / COIN; - // Construct block index object - uint256 blockHash; - if(totalCoinDB <= VALUE_CHANGE) - blockHash = diskindex.GetBlockHashScrypt(); - else - blockHash = diskindex.GetBlockHashGroest(); - - // Construct block index object - CBlockIndex* pindexNew = InsertBlockIndex(blockHash); - pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); - pindexNew->pnext = InsertBlockIndex(diskindex.hashNext); - pindexNew->nFile = diskindex.nFile; - pindexNew->nBlockPos = diskindex.nBlockPos; - pindexNew->nHeight = diskindex.nHeight; - pindexNew->nMint = diskindex.nMint; - pindexNew->nMoneySupply = diskindex.nMoneySupply; - pindexNew->nFlags = diskindex.nFlags; - pindexNew->nStakeModifier = diskindex.nStakeModifier; - pindexNew->prevoutStake = diskindex.prevoutStake; - pindexNew->nStakeTime = diskindex.nStakeTime; - pindexNew->hashProofOfStake = diskindex.hashProofOfStake; - pindexNew->nVersion = diskindex.nVersion; - pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; - pindexNew->nTime = diskindex.nTime; - pindexNew->nBits = diskindex.nBits; - pindexNew->nNonce = diskindex.nNonce; - - if(pindexNew->nMoneySupply / COIN == VALUE_CHANGE) + string strType; + ssKey >> strType; + if (strType == "blockindex" && !fRequestShutdown) { - pindexSave = pindexNew; + CDiskBlockIndex diskindex; + ssValue >> diskindex; + + totalCoin = diskindex.nMoneySupply / COIN; + uint256 blockHash = diskindex.GetBlockHash(); + + // clean up junk from the block index + if (totalCoin == 0) { + // printf("money supply = 0\n"); + // diskindex.print(); + if (blockHash != (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) + { + // not the genesis block, garbage anyway + // printf("deleted\n"); + continue; + } + } + if(!fTestNet && (totalCoin == VALUE_CHANGE)) { +// printf("height = %d, hash = %s\n", diskindex.nHeight, diskindex.GetBlockHash().ToString().c_str()); +// diskindex.print(); + if (diskindex.hashNext == uint256("0x92134c4608025b6bd945731158391079590d0e7e0c60bd7d09a50c0b0251c6ac")) + { + // assign proper hash value +// printf("changed\n"); + diskindex.hashNext = uint256("0x00000d652b612a94e1c830bf4e05106438ea6b53372b29206f0b820d91a9b67b"); + } + if (diskindex.GetBlockHash() == uint256("0xe12ddb2c35d84403b0a045574ecce223f7e2f0db4506e76ed3d43bc464ace40c")) + { + // this hash version should not be here, delete +// printf("deleted\n"); + continue; + } + } + // end cleanup + + // Construct block index object + CBlockIndex* pindexNew = InsertBlockIndex(blockHash); + pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); + pindexNew->pnext = InsertBlockIndex(diskindex.hashNext); + pindexNew->nFile = diskindex.nFile; + pindexNew->nBlockPos = diskindex.nBlockPos; + pindexNew->nHeight = diskindex.nHeight; + pindexNew->nMint = diskindex.nMint; + pindexNew->nMoneySupply = diskindex.nMoneySupply; + pindexNew->nFlags = diskindex.nFlags; + pindexNew->nStakeModifier = diskindex.nStakeModifier; + pindexNew->prevoutStake = diskindex.prevoutStake; + pindexNew->nStakeTime = diskindex.nStakeTime; + pindexNew->hashProofOfStake = diskindex.hashProofOfStake; + pindexNew->nVersion = diskindex.nVersion; + pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; + pindexNew->nTime = diskindex.nTime; + pindexNew->nBits = diskindex.nBits; + pindexNew->nNonce = diskindex.nNonce; + pindexNew->auxpow = diskindex.auxpow; + + if(!fTestNet && (totalCoin == VALUE_CHANGE)) + pindexSave = pindexNew; + if(!fTestNet && (totalCoin == VALUE_CHANGE + 1)) + pindexSaveNext = pindexNew; + + // Watch for genesis block + if (pindexGenesisBlock == NULL && blockHash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) + pindexGenesisBlock = pindexNew; + + if (!pindexNew->CheckIndex()) + return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight); + + // ppcoin: build setStakeSeen + if (pindexNew->IsProofOfStake()) + setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime)); } - if(pindexNew->nMoneySupply / COIN == VALUE_CHANGE + 1) + else { - pindexSaveNext = pindexNew; + break; // if shutdown requested or finished loading block index } - - // Watch for genesis block - if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) - pindexGenesisBlock = pindexNew; - - if (!pindexNew->CheckIndex()) - return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight); - - // ppcoin: build setStakeSeen - if (pindexNew->IsProofOfStake()) - setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime)); - } - else - { - break; // if shutdown requested or finished loading block index - } } // try catch (std::exception &e) { return error("%s() : deserialize error", __PRETTY_FUNCTION__); } } + +// printf("loaded %d in block index\n", count); + if(pindexSaveNext != NULL && pindexSave != NULL && pindexSave->pnext == NULL) + { +// printf("linked pnext at switch block\n"); pindexSave->pnext = pindexSaveNext; + } pcursor->close(); +#if 0 + printf ("verify mapBlockIndex\n"); + count=0; + BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) + { + CBlockIndex* pindex = item.second; + if (pindex->nHeight == 0) { + printf("nHeight=0 count=%d\n", count); + pindex->print(); + } + count++; + } + printf("end verify\n"); +#endif + return true; } diff --git a/src/hash.h b/src/hash.h index d46a463..97e366f 100644 --- a/src/hash.h +++ b/src/hash.h @@ -13,12 +13,19 @@ #include #include +// danbi: try to improve Groestl hashing +#if defined(__GNUC__) + #define DATA_ALIGN16(x) x __attribute__ ((aligned(16))) +#else + #define DATA_ALIGN16(x) __declspec(align(16)) x +#endif + template inline uint256 HashGroestl(const T1 pbegin, const T1 pend) { sph_groestl512_context ctx_gr[2]; - static unsigned char pblank[1]; - uint512 hash[2]; + DATA_ALIGN16(static unsigned char pblank[1]); + DATA_ALIGN16(uint512 hash[2]); sph_groestl512_init(&ctx_gr[0]); sph_groestl512 (&ctx_gr[0], (pbegin == pend ? pblank : static_cast(&pbegin[0])), (pend - pbegin) * sizeof(pbegin[0])); diff --git a/src/init.cpp b/src/init.cpp index 990d191..07d3e98 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -10,6 +10,7 @@ #include "util.h" #include "ui_interface.h" #include "checkpoints.h" +#include "reactors.h" #include #include #include @@ -27,6 +28,8 @@ using namespace boost; CWallet* pwalletMain; CClientUIInterface uiInterface; +bool fUseFastIndex; + ////////////////////////////////////////////////////////////////////////////// // // Shutdown @@ -81,7 +84,7 @@ void Shutdown(void* parg) delete pwalletMain; NewThread(ExitTimeout, NULL); Sleep(50); - printf("DiamondCoin exited\n\n"); + printf("Diamond exited\n\n"); fExit = true; #ifndef QT_GUI // ensure non-UI client gets exited here, but let Bitcoin-Qt reach 'return 0;' in bitcoin.cpp @@ -124,7 +127,7 @@ bool AppInit(int argc, char* argv[]) // // Parameters // - // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main() + // If Qt is used, parameters/diamond.conf are parsed in qt/bitcoin.cpp's main() ParseParameters(argc, argv); if (!boost::filesystem::is_directory(GetDataDir(false))) { @@ -136,12 +139,12 @@ bool AppInit(int argc, char* argv[]) if (mapArgs.count("-?") || mapArgs.count("--help")) { // First part of help message is specific to bitcoind / RPC client - std::string strUsage = _("DiamondCoin version") + " " + FormatFullVersion() + "\n\n" + + std::string strUsage = _("Diamond version") + " " + FormatFullVersion() + "\n\n" + _("Usage:") + "\n" + - " DiamondCoind [options] " + "\n" + - " DiamondCoind [options] [params] " + _("Send command to -server or DiamondCoind") + "\n" + - " DiamondCoind [options] help " + _("List commands") + "\n" + - " DiamondCoind [options] help " + _("Get help for a command") + "\n"; + " Diamondd [options] " + "\n" + + " Diamondd [options] [params] " + _("Send command to -server or Diamondd") + "\n" + + " Diamondd [options] help " + _("List commands") + "\n" + + " Diamondd [options] help " + _("Get help for a command") + "\n"; strUsage += "\n" + HelpMessage(); @@ -151,7 +154,7 @@ bool AppInit(int argc, char* argv[]) // Command-line RPC for (int i = 1; i < argc; i++) - if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "DiamondCoin:")) + if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "Diamond:")) fCommandLine = true; if (fCommandLine) @@ -191,13 +194,13 @@ int main(int argc, char* argv[]) bool static InitError(const std::string &str) { - uiInterface.ThreadSafeMessageBox(str, _("DiamondCoin"), CClientUIInterface::OK | CClientUIInterface::MODAL); + uiInterface.ThreadSafeMessageBox(str, _("Diamond"), CClientUIInterface::OK | CClientUIInterface::MODAL); return false; } bool static InitWarning(const std::string &str) { - uiInterface.ThreadSafeMessageBox(str, _("DiamondCoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); + uiInterface.ThreadSafeMessageBox(str, _("Diamond"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); return true; } @@ -219,8 +222,8 @@ std::string HelpMessage() { string strUsage = _("Options:") + "\n" + " -? " + _("This help message") + "\n" + - " -conf= " + _("Specify configuration file (default: DiamondCoin.conf)") + "\n" + - " -pid= " + _("Specify pid file (default: DiamondCoind.pid)") + "\n" + + " -conf= " + _("Specify configuration file (default: Diamond.conf)") + "\n" + + " -pid= " + _("Specify pid file (default: Diamondd.pid)") + "\n" + " -gen " + _("Generate coins") + "\n" + " -gen=0 " + _("Don't generate coins") + "\n" + " -datadir= " + _("Specify data directory") + "\n" + @@ -239,7 +242,7 @@ std::string HelpMessage() " -externalip= " + _("Specify your own public address") + "\n" + " -onlynet= " + _("Only connect to nodes in network (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" + + " -irc " + _("Find peers using internet relay chat (default: 0)") + "\n" + " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n" + " -bind= " + _("Bind to given address. Use [host]:port notation for IPv6") + "\n" + " -dnsseed " + _("Find peers using DNS lookup (default: 0)") + "\n" + @@ -278,11 +281,13 @@ std::string HelpMessage() " -rpcallowip= " + _("Allow JSON-RPC connections from specified IP address") + "\n" + " -rpcconnect= " + _("Send commands to node running on (default: 127.0.0.1)") + "\n" + " -blocknotify= " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + - " -walletnotify= " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" + + " -walletnotify= " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" + " -upgradewallet " + _("Upgrade wallet to latest format") + "\n" + " -keypool= " + _("Set key pool size to (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" + + " -zapwallettxes= " + _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup\n") + + " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)\n") + " -checkblocks= " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + " -checklevel= " + _("How thorough the block verification is (0-6, default: 1)") + "\n" + " -loadblock= " + _("Imports blocks from external blk000?.dat file") + "\n" + @@ -351,7 +356,6 @@ bool AppInit2() // ********************************************************* Step 2: parameter interactions fTestNet = GetBoolArg("-testnet"); - //fTestNet = true; if (fTestNet) { SoftSetBoolArg("-irc", true); } @@ -389,6 +393,11 @@ bool AppInit2() SoftSetBoolArg("-rescan", true); } + if (GetBoolArg("-zapwallettxes", false)) { + // -zapwallettx implies a rescan + SoftSetBoolArg("-rescan", true); + } + // ********************************************************* Step 3: parameter-to-internal-flags fDebug = GetBoolArg("-debug"); @@ -419,7 +428,7 @@ bool AppInit2() fPrintToConsole = GetBoolArg("-printtoconsole"); fPrintToDebugger = GetBoolArg("-printtodebugger"); fLogTimestamps = GetBoolArg("-logtimestamps"); - fLogTimestamps = true; + if (mapArgs.count("-timeout")) { int nNewTimeout = GetArg("-timeout", 5000); @@ -427,6 +436,16 @@ bool AppInit2() nConnectTimeout = nNewTimeout; } + fUseFastIndex = GetBoolArg("-fastindex", true); + + if (mapArgs.count("-changeaddress")) + { + CBitcoinAddress address(GetArg("-changeaddress", "")); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Diamond address"); + changeAddress = address.Get(); + } + // Continue to put "/P2SH/" in the coinbase to monitor // BIP16 support. // This can be removed eventually... @@ -452,7 +471,7 @@ bool AppInit2() 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. DiamondCoin is probably already running."), strDataDir.c_str())); + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Diamond is probably already running."), strDataDir.c_str())); #if !defined(WIN32) && !defined(QT_GUI) if (fDaemon) @@ -479,7 +498,7 @@ bool AppInit2() 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("DiamondCoin version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str()); + printf("Diamond 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()); @@ -488,13 +507,14 @@ bool AppInit2() std::ostringstream strErrors; if (fDaemon) - fprintf(stdout, "DiamondCoin server starting\n"); + fprintf(stdout, "Diamond server starting\n"); int64 nStart; // ********************************************************* Step 5: verify database integrity uiInterface.InitMessage(_("Verifying database integrity...")); + printf("Verifying database integrity...\n"); if (!bitdb.Open(GetDataDir())) { @@ -520,7 +540,7 @@ bool AppInit2() " 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, _("DiamondCoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); + uiInterface.ThreadSafeMessageBox(msg, _("Diamond"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); } if (r == CDBEnv::RECOVER_FAIL) return InitError(_("wallet.dat corrupt, salvage failed")); @@ -647,7 +667,6 @@ bool AppInit2() BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"]) AddOneShot(strDest); - AddOneShot("54.229.220.59"); // TODO: replace this by DNSseed // AddOneShot(string("")); @@ -717,6 +736,25 @@ bool AppInit2() // ********************************************************* Step 8: load wallet + // needed to restore wallet transaction meta data after -zapwallettxes + std::vector vWtx; + + if (GetBoolArg("-zapwallettxes", false)) { + uiInterface.InitMessage(_("Zapping all transactions from wallet...")); + printf("Zapping all transactions from wallet...\n"); + + pwalletMain = new CWallet("wallet.dat"); + DBErrors nZapWalletRet = pwalletMain->ZapWalletTx(vWtx); + if (nZapWalletRet != DB_LOAD_OK) { + uiInterface.InitMessage(_("Error loading wallet.dat: Wallet corrupted")); + printf("Error loading wallet.dat: Wallet corrupted\n"); + return false; + } + + delete pwalletMain; + pwalletMain = NULL; + } + uiInterface.InitMessage(_("Loading wallet...")); printf("Loading wallet...\n"); nStart = GetTimeMillis(); @@ -731,13 +769,13 @@ bool AppInit2() { 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, _("DiamondCoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); + uiInterface.ThreadSafeMessageBox(msg, _("Diamond"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); } else if (nLoadWalletRet == DB_TOO_NEW) - strErrors << _("Error loading wallet.dat: Wallet requires newer version of DiamondCoin") << "\n"; + strErrors << _("Error loading wallet.dat: Wallet requires newer version of Diamond") << "\n"; else if (nLoadWalletRet == DB_NEED_REWRITE) { - strErrors << _("Wallet needed to be rewritten: restart DiamondCoin to complete") << "\n"; + strErrors << _("Wallet needed to be rewritten: restart Diamond to complete") << "\n"; printf("%s", strErrors.str().c_str()); return InitError(strErrors.str()); } @@ -796,6 +834,31 @@ bool AppInit2() nStart = GetTimeMillis(); pwalletMain->ScanForWalletTransactions(pindexRescan, true); printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); + + nWalletDBUpdated++; + + // Restore wallet transaction metadata after -zapwallettxes=1 + if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2") + { + BOOST_FOREACH(const CWalletTx& wtxOld, vWtx) + { + uint256 hash = wtxOld.GetHash(); + std::map::iterator mi = pwalletMain->mapWallet.find(hash); + if (mi != pwalletMain->mapWallet.end()) + { + const CWalletTx* copyFrom = &wtxOld; + CWalletTx* copyTo = &mi->second; + copyTo->mapValue = copyFrom->mapValue; + copyTo->vOrderForm = copyFrom->vOrderForm; + copyTo->nTimeReceived = copyFrom->nTimeReceived; + copyTo->nTimeSmart = copyFrom->nTimeSmart; + copyTo->fFromMe = copyFrom->fFromMe; + copyTo->strFromAccount = copyFrom->strFromAccount; + copyTo->nOrderPos = copyFrom->nOrderPos; + copyTo->WriteToDisk(); + } + } + } } // ********************************************************* Step 9: import blocks @@ -803,6 +866,7 @@ bool AppInit2() if (mapArgs.count("-loadblock")) { uiInterface.InitMessage(_("Importing blockchain data file.")); + printf("Importing blockchain data file.\n"); BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"]) { @@ -815,6 +879,7 @@ bool AppInit2() filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; if (filesystem::exists(pathBootstrap)) { uiInterface.InitMessage(_("Importing bootstrap blockchain data file.")); + printf("Importing bootstrap blockchain data file.\n"); FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); if (file) { @@ -839,7 +904,16 @@ bool AppInit2() printf("Loaded %i addresses from peers.dat %"PRI64d"ms\n", addrman.size(), GetTimeMillis() - nStart); - // ********************************************************* Step 11: start node + // ********************************************************* Step 11: initialize reactor db + + uiInterface.InitMessage(_("Loading reactors...")); + printf("Loading reactors...\n"); + nStart = GetTimeMillis(); + + InitReactors(); + printf(" reactors %" PRI64d "ms\n", GetTimeMillis() - nStart); + + // ********************************************************* Step 12: start node if (!CheckDiskSpace()) return false; @@ -859,7 +933,7 @@ bool AppInit2() if (fServer) NewThread(ThreadRPCServer, NULL); - // ********************************************************* Step 12: finished + // ********************************************************* Step 13: finished uiInterface.InitMessage(_("Done loading")); printf("Done loading\n"); diff --git a/src/irc.cpp b/src/irc.cpp index 157a031..4132ba8 100644 --- a/src/irc.cpp +++ b/src/irc.cpp @@ -214,7 +214,7 @@ void ThreadIRCSeed2(void* parg) return; // ... or if IRC is not enabled. - if (!GetBoolArg("-irc", true)) + if (!GetBoolArg("-irc", false)) return; printf("ThreadIRCSeed started\n"); diff --git a/src/json/json_spirit_writer_template.h b/src/json/json_spirit_writer_template.h index 28c49dd..3cd8eb0 100644 --- a/src/json/json_spirit_writer_template.h +++ b/src/json/json_spirit_writer_template.h @@ -28,7 +28,7 @@ namespace json_spirit template< class String_type > String_type non_printable_to_string( unsigned int c ) { - typedef typename String_type::value_type Char_type; + //typedef typename String_type::value_type Char_type; String_type result( 6, '\\' ); diff --git a/src/kernel.cpp b/src/kernel.cpp index 0b8498e..3fcad2d 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -3,9 +3,10 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include -//#include + #include "kernel.h" #include "db.h" +#include "auxpow.h" using namespace std; @@ -32,6 +33,29 @@ static std::map mapStakeModifierCheckpoints = ( 317997, 0xfd318368u ) ; +/* Selects the appropriate minimal stake age */ +uint GetStakeMinAge(uint nStakeTime) { + + if(fTestNet) + return(nStakeMinAge); + + if(nStakeTime > nLiveTimeSwitch) + return(nStakeMinAgeFixed); + else + return(nStakeMinAge); +} + +// Get time weight +int64 GetWeight(int64 nIntervalBeginning, int64 nIntervalEnd) { + uint nStakeMinAgeCurrent = GetStakeMinAge(nIntervalEnd); + + // Kernel hash weight starts from 0 at the min age + // this change increases active coins participating the hash and helps + // to secure the network when proof-of-stake difficulty is low + + return(min(nIntervalEnd - nIntervalBeginning - nStakeMinAgeCurrent, (int64)nStakeMaxAge)); +} + // Get the last stake modifier and its generation time from a given block static bool GetLastStakeModifier(const CBlockIndex* pindex, uint64& nStakeModifier, int64& nModifierTime) { @@ -59,9 +83,7 @@ static int64 GetStakeModifierSelectionInterval() { int64 nSelectionInterval = 0; for (int nSection=0; nSection<64; nSection++) - { nSelectionInterval += GetStakeModifierSelectionIntervalSection(nSection); - } return nSelectionInterval; } @@ -143,10 +165,16 @@ bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64& nStakeModif 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()); + printf("ComputeNextStakeModifier: prev modifier=0x%016"PRI64x" time=%s epoch=%u\n", nStakeModifier, DateTimeStrFormat(nModifierTime).c_str(), (unsigned int)nModifierTime); } if (nModifierTime / nModifierInterval >= pindexPrev->GetBlockTime() / nModifierInterval) + { + if (fDebug) + { + printf("ComputeNextStakeModifier: no new interval keep current modifier: pindexPrev nHeight=%d nTime=%u\n", pindexPrev->nHeight, (unsigned int)pindexPrev->GetBlockTime()); + } return true; + } // Sort candidate blocks by timestamp vector > vSortedByTimestamp; @@ -221,27 +249,25 @@ static bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64& nStakeModifier { nStakeModifier = 0; if (!mapBlockIndex.count(hashBlockFrom)) - return false; + return error("GetKernelStakeModifier() : block not indexed"); const CBlockIndex* pindexFrom = mapBlockIndex[hashBlockFrom]; nStakeModifierHeight = pindexFrom->nHeight; nStakeModifierTime = pindexFrom->GetBlockTime(); + + uint nStakeMinAgeCurrent = GetStakeMinAge(nStakeModifierTime); + int64 nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval(); const CBlockIndex* pindex = pindexFrom; - // loop to find the stake modifier later by a selection interval 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())) + if(fPrintProofOfStake || (pindex->GetBlockTime() + nStakeMinAgeCurrent - 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 - { - // printf(">> nStakeModifierTime = %"PRI64d", pindexFrom->GetBlockTime() = %"PRI64d", nStakeModifierSelectionInterval = %"PRI64d"\n", - // nStakeModifierTime, pindexFrom->GetBlockTime(), nStakeModifierSelectionInterval); return false; - } } pindex = pindex->pnext; if (pindex->GeneratedStakeModifier()) @@ -279,60 +305,35 @@ static bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64& nStakeModifier // bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, bool fPrintProofOfStake) { - if(totalCoin < VALUE_CHANGE && pindexBest->nHeight > 376497 && !fTestNet) - return false; - if (nTimeTx < txPrev.nTime) // Transaction timestamp violation return error("CheckStakeKernelHash() : nTime violation"); unsigned int nTimeBlockFrom = blockFrom.GetBlockTime(); - if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement + + uint nStakeMinAgeCurrent = GetStakeMinAge(nTimeBlockFrom); + + if(nTimeBlockFrom + nStakeMinAgeCurrent > 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(true); + // v0.3 protocol kernel hash weight starts from 0 at the min age // this change increases active coins participating the hash and helps // to secure the network when proof-of-stake difficulty is low - int64 nTimeWeight = min((int64)nTimeTx - txPrev.nTime, (int64)nStakeMaxAge) - nStakeMinAge; - CBigNum bnCoinDayWeight; - - if(fTestNet) -// bnCoinDayWeight = CBigNum((nValueIn) * nTimeWeight * 10000 / COIN / (24 * 60 * 60)); - if(nValueIn < 100) - bnCoinDayWeight = CBigNum(100 * nTimeWeight * 10000 / (24 * 60 * 60)); - else - bnCoinDayWeight = CBigNum((nValueIn) * nTimeWeight * 10000 / COIN / (24 * 60 * 60)); - else - { - if(totalCoin < VALUE_CHANGE) - bnCoinDayWeight = CBigNum(nValueIn) * nTimeWeight / COIN / (24 * 60 * 60); - else - { - if(nValueIn < 100) - bnCoinDayWeight = CBigNum(100 * nTimeWeight * 10000 / (24 * 60 * 60)); - else - bnCoinDayWeight = CBigNum((nValueIn) * nTimeWeight * 10000 / COIN / (24 * 60 * 60)); - } - } - - // printf(">>> CheckStakeKernelHash: nTimeWeight = %"PRI64d"\n", nTimeWeight); + int64 nTimeWeight = min((int64)nTimeTx - txPrev.nTime, (int64)nStakeMaxAge) - nStakeMinAgeCurrent; + CBigNum bnCoinDayWeight = CBigNum(nValueIn) * nTimeWeight / COIN / (24 * 60 * 60); // Calculate hash CDataStream ss(SER_GETHASH, 0); uint64 nStakeModifier = 0; int nStakeModifierHeight = 0; int64 nStakeModifierTime = 0; - if (!GetKernelStakeModifier(blockFrom.GetHashScrypt(), nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake) && - - !GetKernelStakeModifier(blockFrom.GetHashGroestl(), nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake)) - { - // printf(">>> CheckStakeKernelHash: GetKernelStakeModifier return false\n"); + if (!GetKernelStakeModifier(hashBlockFrom, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake)) return false; - } - // printf(">>> CheckStakeKernelHash: passed GetKernelStakeModifier\n"); ss << nStakeModifier; ss << nTimeBlockFrom << nTxPrevOffset << txPrev.nTime << prevout.n << nTimeTx; @@ -342,10 +343,9 @@ bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned 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[blockFrom.GetHash()]->nHeight, + mapBlockIndex[hashBlockFrom]->nHeight, DateTimeStrFormat(blockFrom.GetBlockTime()).c_str()); - printf("CheckStakeKernelHash() : check protocol=%s modifier=0x%016"PRI64x" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", - "0.3", + 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()); @@ -354,10 +354,11 @@ bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned // Now check if proof-of-stake hash meets target protocol if (CBigNum(hashProofOfStake) > bnCoinDayWeight * bnTargetPerCoinDay) { - //qDebug() << "hashProofOfStake = " << CBigNum(hashProofOfStake).ToString().c_str(); - //qDebug() << "bnCoinDayWeight = " << bnCoinDayWeight.ToString().c_str(); - //qDebug() << "bnTargetPerCoinDay = " << bnTargetPerCoinDay.ToString().c_str(); - //printf(">>> CheckStakeKernelHash - hashProofOfStake too much\n"); +// printf("hashProofOfStake = %s\n", CBigNum(hashProofOfStake).ToString().c_str()); +// printf("bnCoinDayWeight = %s\n", bnCoinDayWeight.ToString().c_str()); +// printf("bnTargetPerCoinDay = %s\n", bnTargetPerCoinDay.ToString().c_str()); +// printf("nBits = %d\n", nBits); +// printf(">>> CheckStakeKernelHash - hashProofOfStake too much\n"); return false; } @@ -366,10 +367,9 @@ bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned 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[blockFrom.GetHash()]->nHeight, + mapBlockIndex[hashBlockFrom]->nHeight, DateTimeStrFormat(blockFrom.GetBlockTime()).c_str()); - printf("CheckStakeKernelHash() : pass protocol=%s modifier=0x%016"PRI64x" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", - "0.3", + 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()); @@ -435,8 +435,6 @@ bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierCheck { if (fTestNet) return true; // Testnet has no checkpoints if (mapStakeModifierCheckpoints.count(nHeight)) - { return nStakeModifierChecksum == mapStakeModifierCheckpoints[nHeight]; - } return true; } diff --git a/src/kernel.h b/src/kernel.h index 9241173..cf0e3f4 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -14,6 +14,9 @@ extern unsigned int nModifierInterval; // ratio of group interval length between the last group and the first group static const int MODIFIER_INTERVAL_RATIO = 3; +/* Selects the appropriate minimal stake age */ +uint GetStakeMinAge(uint nStakeTime); + // Compute the hash modifier for proof-of-stake bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64& nStakeModifier, bool& fGeneratedStakeModifier); @@ -34,4 +37,7 @@ 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 // PPCOIN_KERNEL_H diff --git a/src/main.cpp b/src/main.cpp index 1e40a7e..53d08b7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,11 +7,12 @@ #include "checkpoints.h" #include "db.h" #include "net.h" -#include "init.h" +#include "init.h" +#include "auxpow.h" #include "ui_interface.h" #include "kernel.h" #include "scrypt_mine.h" -#include "scrypt.h" +#include "reactors.h" #include #include #include @@ -26,7 +27,7 @@ using namespace boost; // CCriticalSection cs_setpwalletRegistered; -set setpwalletRegistered; +set setpwalletRegistered; CCriticalSection cs_main; @@ -40,16 +41,18 @@ static CBigNum bnProofOfWorkLimit(~uint256(0) >> 20); static CBigNum bnProofOfWorkLimit_1(~uint256(0) >> 32); static CBigNum bnProofOfStakeLimit(~uint256(0) >> 20); -static CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >> 16); -static CBigNum bnProofOfStakeLimitTestNet(~uint256(0) >> 20); -uint256 nPoWBase = uint256("0x00000000ffff0000000000000000000000000000000000000000000000000000"); // difficulty-1 target -unsigned int nStakeMinAge = 60 * 60 * 24 * 7; // minimum age for coin age: 2d +static CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >> 24); +static CBigNum bnProofOfStakeLimitTestNet(~uint256(0) >> 16); +unsigned int nStakeMinAge = 60 * 60 * 24 * 7; // minimum age for coin age: 7d +unsigned int nStakeMinAgeFixed = 60 * 60 * 24 * 9; /* must be > 8.82 days */ unsigned int nStakeMaxAge = 60 * 60 * 24 * 30; // stake age of full weight: 30d int64 nStakeTargetSpacing = 60; // 1-minute block spacing int64 nWorkTargetSpacing = 60; // 1-minute block spacing -int64 coinMax = 0; -int64 totalCoinDB = 0; +static const int64 POW_RESTART = 577850; // When (block) to unstuck PoW + +bool fSignWorkBlock = true; + int64 totalCoin = -1; int64 nChainStartTime = 1373654826; int nCoinbaseMaturity = 30; @@ -421,7 +424,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) } // Update the tx's hashBlock - hashBlock = pblock->GetHash(); + hashBlock = pblock->GetHash(true); // Locate the transaction for (nIndex = 0; nIndex < (int)pblock->vtx.size(); nIndex++) @@ -472,15 +475,15 @@ bool CTransaction::CheckTransaction() const if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake()) return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction")); - if(totalCoin < VALUE_CHANGE) - { + if(fTestNet || + (!fTestNet && ((totalCoin <= VALUE_CHANGE) || (totalCoin > POS_RESTART)))) { // ppcoin: enforce minimum output amount - if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT) + if ((!txout.IsEmpty()) && (!IsCoinStake()) && txout.nValue < MIN_TXOUT_AMOUNT) return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue below minimum")); } else { - if (txout.nValue < 0) + if ((!txout.IsEmpty()) && txout.nValue < 0) return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); } @@ -503,7 +506,7 @@ bool CTransaction::CheckTransaction() const if (IsCoinBase()) { if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) - return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); + return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size is invalid")); } else { @@ -623,7 +626,7 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs, return error("CTxMemPool::accept() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); if (pfMissingInputs) *pfMissingInputs = true; - return false; + return error("CTxMemPool::accept() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str()); } // Check for non-standard pay-to-script-hash in inputs @@ -772,7 +775,7 @@ int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const // Make sure the merkle branch connects to this block if (!fMerkleVerified) { - if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) + if(CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) return 0; fMerkleVerified = true; } @@ -786,7 +789,7 @@ int CMerkleTx::GetBlocksToMaturity() const { if (!(IsCoinBase() || IsCoinStake())) return 0; - return max(0, (nCoinbaseMaturity+20) - GetDepthInMainChain()); + return max(0, (nCoinbaseMaturity + 1) - GetDepthInMainChain()); } @@ -832,14 +835,12 @@ bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) return false; } - bool CWalletTx::AcceptWalletTransaction() { CTxDB txdb("r"); return AcceptWalletTransaction(txdb); } - int CTxIndex::GetDepthInMainChain() const { // Read block header @@ -847,7 +848,7 @@ int CTxIndex::GetDepthInMainChain() const if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false)) return 0; // Find the block in the index - map::iterator mi = mapBlockIndex.find(block.GetHash()); + map::iterator mi = mapBlockIndex.find(block.GetHash(true)); if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; @@ -875,7 +876,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock) { CBlock block; if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) - hashBlock = block.GetHash(); + hashBlock = block.GetHash(true); return true; } } @@ -916,15 +917,20 @@ bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions) } if (!ReadFromDisk(pindex->nFile, pindex->nBlockPos, fReadTransactions)) return false; - totalCoinDB = 1; - uint256 hash1 = GetHashScrypt(); - totalCoinDB = coinMax; - uint256 hash2 = GetHashGroestl(); - if (hash1 != pindex->GetBlockHash() && hash2 != pindex->GetBlockHash()) +// if (GetHashScrypt() != pindex->GetBlockHash() && GetHashGroestl() != pindex->GetBlockHash()) + // send totalCoins to speed up GetHash() + if (GetHash(false, pindex->nMoneySupply / COIN ) != pindex->GetBlockHash()) return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); return true; } +void CBlock::SetAuxPow(CAuxPow* pow) { + if(pow != NULL) + nVersion |= BLOCK_VERSION_AUXPOW; + else + nVersion &= ~BLOCK_VERSION_AUXPOW; + auxpow.reset(pow); +} uint256 static GetOrphanRoot(const CBlock* pblock) { @@ -934,7 +940,6 @@ uint256 static GetOrphanRoot(const CBlock* pblock) return pblock->GetHash(); } - // ppcoin: find block wanted by given orphan block uint256 WantedByOrphan(const CBlock* pblockOrphan) { @@ -953,15 +958,12 @@ int generateMTRandom(unsigned int s, int range) } -static const int64 nMinSubsidy = 1 * COIN; -static const int CUTOFF_HEIGHT = 100800; // Height at the end of 5 weeks // miner's coin base reward based on nBits int64 GetProofOfWorkReward(int nHeight, int64 nFees, uint256 prevHash) { int64 nSubsidy = COIN; - if(totalCoin < VALUE_CHANGE) - { + if(fTestNet || (totalCoin <= VALUE_CHANGE)) { std::string cseed_str = prevHash.ToString().substr(6,7); const char* cseed = cseed_str.c_str(); long seed = hex2long(cseed); @@ -983,86 +985,90 @@ int64 GetProofOfWorkReward(int nHeight, int64 nFees, uint256 prevHash) } else { - if(totalCoin > 1000000) - nSubsidy = 10 * CENT; - else if(totalCoin > 2500000) - nSubsidy = 2 * CENT; + // Diamond v2 coin mechanics + // 0.20 reward after 1,000,000 created + // 0.04 reward after 2,500,000 created + if (totalCoin >= 2500000) + nSubsidy = 4 * CENT; + else if (totalCoin >= 1000000) + nSubsidy = 20 * CENT; } return nSubsidy + nFees; } // miner's coin stake reward based on nBits and coin age spent (coin-days) // simple algorithm, not depend on the diff -const int YEARLY_BLOCKCOUNT = 525600; // 365 * 1440 -int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTime, int nHeight) +int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTime, int nHeight, float reactorRate) { - if(totalCoin >= VALUE_CHANGE || fTestNet) + int64 nRewardCoinYear; + int64 nSubsidy = 0; + + if (totalCoin > VALUE_CHANGE || fTestNet) { - int64 nRewardCoinYear; - int nSubsidy = 0; nRewardCoinYear = MAX_MINT_PROOF_OF_STAKE; - if(fTestNet) - nSubsidy = nCoinAge * 50 * MAX_MINT_PROOF_OF_STAKE * 33 / (365 * 33 + 8) / COIN; - if(totalCoin > VALUE_CHANGE) - { - nSubsidy = nCoinAge * 50 * MAX_MINT_PROOF_OF_STAKE * 33 / (365 * 33 + 8) / COIN; - } - else if(totalCoin > VALUE_CHANGE + 4000) - { - nSubsidy = nCoinAge / COIN * 50 * MAX_MINT_PROOF_OF_STAKE * 33 / (365 * 33 + 8); - } - else if(totalCoin > 1500000) - { - nSubsidy = nCoinAge / COIN * 25 * MAX_MINT_PROOF_OF_STAKE * 33 / (365 * 33 + 8); - } - else if(totalCoin > 2500000) - { - nSubsidy = nCoinAge / COIN * 5 * MAX_MINT_PROOF_OF_STAKE * 33 / (365 * 33 + 8); - } - else if(totalCoin > 3500000) - { - nSubsidy = nCoinAge / COIN * 1 * MAX_MINT_PROOF_OF_STAKE * 33 / (365 * 33 + 8); - } + if(fTestNet) { + nRewardCoinYear = 25 * CENT; + nRewardCoinYear = GetAdjustedCoinYear(nRewardCoinYear, nTime, reactorRate); + nSubsidy = nCoinAge * nRewardCoinYear / 365; + } else { + // Diamond v2 PoS spec: + // 50% algorithm switch to 1,500,000 coins + // 25% from 1,500,000 to 2,500,000 coins + // 5% from 2,500,000 to 3,500,000 coins + // 1% ever since 3,500,000 coins + if (totalCoin > 3500000) + nRewardCoinYear = 1 * CENT; + else if (totalCoin > 2500000) + nRewardCoinYear = 5 * CENT; + else if (totalCoin > 1500000) + nRewardCoinYear = 25 * CENT; + else + nRewardCoinYear = 50 * CENT; + + if(nTime > REACTOR_START_TIME) + nRewardCoinYear = GetAdjustedCoinYear(nRewardCoinYear, nTime, reactorRate); + + nSubsidy = nCoinAge * nRewardCoinYear / 365; + } if (fDebug && GetBoolArg("-printcreation")) printf("GetProofOfStakeReward(): create=%s nCoinAge=%"PRI64d" nBits=%d\n", FormatMoney(nSubsidy).c_str(), nCoinAge, nBits); return nSubsidy; } + // DEAD CODE, nothing past this line runs anymore... else { - int64 nRewardCoinYear; - - CBigNum bnRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE; // Base stake mint rate, 100% year interest - CBigNum bnTarget; - bnTarget.SetCompact(nBits); - CBigNum bnTargetLimit = bnProofOfStakeLimit; - bnTargetLimit.SetCompact(bnTargetLimit.GetCompact()); + CBigNum bnRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE; // Base stake mint rate, 100% year interest + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + CBigNum bnTargetLimit = bnProofOfStakeLimit; + bnTargetLimit.SetCompact(bnTargetLimit.GetCompact()); - // Diamond: reward for coin-year is cut in half every 64x multiply of PoS difficulty - // A reasonably continuous curve is used to avoid shock to market - // (nRewardCoinYearLimit / nRewardCoinYear) ** 4 == bnProofOfStakeLimit / bnTarget - // - // Human readable form: - // - // nRewardCoinYear = 1 / (posdiff ^ 1/4) + // Diamond: reward for coin-year is cut in half every 64x multiply of PoS difficulty + // A reasonably continuous curve is used to avoid shock to market + // (nRewardCoinYearLimit / nRewardCoinYear) ** 4 == bnProofOfStakeLimit / bnTarget + // + // Human readable form: + // + // nRewardCoinYear = 1 / (posdiff ^ 1/4) - CBigNum bnLowerBound = 1 * CENT; // Lower interest bound is 1% per year - CBigNum bnUpperBound = bnRewardCoinYearLimit; - while (bnLowerBound + CENT <= bnUpperBound) - { - CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2; - if (fDebug && GetBoolArg("-printcreation")) - printf("GetProofOfStakeReward() : lower=%"PRI64d" upper=%"PRI64d" mid=%"PRI64d"\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64()); - if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnTarget) - bnUpperBound = bnMidValue; - else - bnLowerBound = bnMidValue; - } + CBigNum bnLowerBound = 1 * CENT; // Lower interest bound is 1% per year + CBigNum bnUpperBound = bnRewardCoinYearLimit; + while (bnLowerBound + CENT <= bnUpperBound) + { + CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2; + if (fDebug && GetBoolArg("-printcreation")) + printf("GetProofOfStakeReward() : lower=%"PRI64d" upper=%"PRI64d" mid=%"PRI64d"\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64()); + if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnTarget) + bnUpperBound = bnMidValue; + else + bnLowerBound = bnMidValue; + } - nRewardCoinYear = bnUpperBound.getuint64(); - nRewardCoinYear = min((nRewardCoinYear / CENT) * CENT, MAX_MINT_PROOF_OF_STAKE); + nRewardCoinYear = bnUpperBound.getuint64(); + nRewardCoinYear = min((nRewardCoinYear / CENT) * CENT, MAX_MINT_PROOF_OF_STAKE); - int64 nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear; + nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear; if (fDebug && GetBoolArg("-printcreation")) printf("GetProofOfStakeReward(): create=%s nCoinAge=%"PRI64d" nBits=%d\n", FormatMoney(nSubsidy).c_str(), nCoinAge, nBits); return nSubsidy; @@ -1119,18 +1125,20 @@ const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfSta return pindex; } -unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake) +unsigned int GetNextTargetRequired_v1(const CBlockIndex* pindexLast, bool fProofOfStake) { if(fTestNet && !fProofOfStake && pindexLast->nHeight <= 100) return bnProofOfWorkLimit.GetCompact(); - CBigNum bnTargetLimit = !fProofOfStake ? bnProofOfWorkLimit : bnProofOfStakeLimit; + // cruft from alorithm switch time if(pindexLast->nHeight >= 386221 && pindexLast->nHeight <= 386226) return bnProofOfWorkLimit.GetCompact(); if(pindexLast->nHeight >= 386232 && pindexLast->nHeight <= 386233) return bnProofOfWorkLimit_1.GetCompact(); + CBigNum bnTargetLimit = fProofOfStake ? bnProofOfStakeLimit : bnProofOfWorkLimit; + if (pindexLast == NULL) return bnTargetLimit.GetCompact(); // genesis block @@ -1141,42 +1149,116 @@ unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfS if (pindexPrevPrev->pprev == NULL) return bnTargetLimit.GetCompact(); // second block - int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime(); - // ppcoin: target change every block // ppcoin: retarget with exponential moving toward target spacing CBigNum bnNew; bnNew.SetCompact(pindexPrev->nBits); int64 nTargetSpacing = fProofOfStake? nStakeTargetSpacing : min(nTargetSpacingWorkMax, (int64) nWorkTargetSpacing * (1 + pindexLast->nHeight - pindexPrev->nHeight)); - int64 nInterval = nTargetTimespan / nTargetSpacing; + int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime(); + + // fix block spacing + // danbi: post 2.0.4 implement new pacing algorithm for PoW & PoS + if (nBestHeight > POW_RESTART) + { + if (nActualSpacing < 0) + { + if (fDebug && GetBoolArg("-printjunk")) printf(">> %s nActualSpacing = %"PRI64d" corrected to nTargetSpacing (%"PRI64d").\n", fProofOfStake ? "PoS" : "PoW", nActualSpacing, nTargetSpacing); + nActualSpacing=nTargetSpacing; + } + else if (nActualSpacing > nTargetTimespan) + { + if (fDebug && GetBoolArg("-printjunk")) printf(">> %s nActualSpacing = %"PRI64d" corrected to nTargetTimespan (%"PRI64d").\n", fProofOfStake ? "PoS" : "PoW", nActualSpacing, nTargetTimespan); + nActualSpacing=nTargetTimespan; + } + } + // danbi: old pre 2.0.4 PoS pacing algorithm + else if (fProofOfStake && GetTotalCoin() > POS_RESTART) + { + if(nActualSpacing < 0) + { + if (fDebug && GetBoolArg("-printjunk")) printf(">> %s nActualSpacing = %"PRI64d" corrected to 1.\n", fProofOfStake ? "PoS" : "PoW", nActualSpacing); + nActualSpacing = 1; + } + else if(nActualSpacing > nTargetTimespan) + { + if (fDebug && GetBoolArg("-printjunk")) printf(">> %s nActualSpacing = %"PRI64d" corrected to nTargetTimespan (%"PRI64d").\n", fProofOfStake ? "PoS" : "PoW", nActualSpacing, nTargetTimespan); + nActualSpacing = nTargetTimespan; + } + } + + bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing); bnNew /= ((nInterval + 1) * nTargetSpacing); - if (bnNew > bnTargetLimit) + // danbi: make sure we don't emit negative numbers even if we miscalculated + if ((bnNew <= 0 && nBestHeight > POW_RESTART) || bnNew > bnTargetLimit) bnNew = bnTargetLimit; return bnNew.GetCompact(); } -bool CheckProofOfWork(uint256 hash, unsigned int nBits) +unsigned int GetNextTargetRequired_v2(const CBlockIndex* pindexLast, bool fProofOfStake) { - if(pindexBest != NULL) - printf("pindexBest = %d", pindexBest->nMoneySupply / COIN); - CBigNum bnTarget; - bnTarget.SetCompact(nBits); + CBigNum bnTargetLimit = bnProofOfWorkLimit; - // Check range - if (bnTarget <= 0 || bnTarget > bnProofOfWorkLimit) - return false; + if(fProofOfStake) + bnTargetLimit = bnProofOfStakeLimit; - // Check proof of work matches claimed amount - if (hash > bnTarget.getuint256()) - return false; + if (pindexLast == NULL) + return bnTargetLimit.GetCompact(); // genesis block - return true; + const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake); + if (pindexPrev->pprev == NULL) + return bnTargetLimit.GetCompact(); // first block + const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake); + if (pindexPrevPrev->pprev == NULL) + return bnTargetLimit.GetCompact(); // second block + + int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime(); + if(nActualSpacing < 0) + { + nActualSpacing = 1; + } + else if(nActualSpacing > nTargetTimespan) + { + nActualSpacing = nTargetTimespan; + } + + // ppcoin: target change every block + // ppcoin: retarget with exponential moving toward target spacing + CBigNum bnNew; + bnNew.SetCompact(pindexPrev->nBits); + + int64 nTargetSpacing = fProofOfStake? nStakeTargetSpacing : nWorkTargetSpacing; + int64 nInterval = nTargetTimespan / nTargetSpacing; + bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing); + bnNew /= ((nInterval + 1) * nTargetSpacing); + + // danbi: make sure we don't emit negative numbers even if we miscalculated + if (bnNew <= 0 || bnNew > bnTargetLimit) + bnNew = bnTargetLimit; + + /* Disable legacy PoW block signature */ + if(fSignWorkBlock) { + if((fTestNet && (pindexPrev->nHeight + 1 >= nTestStage2)) || + (!fTestNet && (pindexPrev->nHeight + 1 >= nLiveFork1))) { + fSignWorkBlock = false; + } + } + + return bnNew.GetCompact(); +} + + +unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake) +{ + if(!fTestNet && (totalCoin < 1000000)) + return GetNextTargetRequired_v1(pindexLast, fProofOfStake); + else return GetNextTargetRequired_v2(pindexLast, fProofOfStake); } + // Return maximum amount of blocks that other nodes claim to have int GetNumBlocksOfPeers() { @@ -1333,7 +1415,6 @@ bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTes return true; } - const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const { MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash); @@ -1347,7 +1428,6 @@ const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& in return txPrev.vout[input.prevout.n]; } - int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const { if (IsCoinBase()) @@ -1362,7 +1442,6 @@ int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const } - unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const { if (IsCoinBase()) @@ -1378,7 +1457,6 @@ unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const return nSigOps; } - bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, map& mapTestPool, const CDiskTxPos& posThisTx, const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash) @@ -1466,9 +1544,16 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, uint64 nCoinAge; if (!GetCoinAge(txdb, nCoinAge)) return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str()); - int64 nStakeReward = GetValueOut() - nValueIn; - if (nStakeReward > GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime, pindexBlock->nHeight) - GetMinFee() + MIN_TX_FEE) - return DoS(100, error("ConnectInputs() : %s stake reward exceeded", GetHash().ToString().substr(0,10).c_str())); + + // If vout[0] is not empty check if this is a reactor stake. + if (!vout[0].IsEmpty()) { + // OP_REACTOR is stored in vout[0] destination is stored in vout[1] + return IsReactorStake(GetReactorDBFile(), vout[0].scriptPubKey, vout[1].scriptPubKey, nTime, nValueIn, GetValueOut(), nCoinAge, pindexBlock->nBits, pindexBlock->nHeight); + } else { + int64 nStakeReward = GetValueOut() - nValueIn; + if (nStakeReward > GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime, pindexBlock->nHeight) - GetMinFee() + MIN_TX_FEE) + return DoS(100, error("ConnectInputs() : %s stake reward exceeded", GetHash().ToString().substr(0,10).c_str())); + } } else { @@ -1567,7 +1652,7 @@ bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) { // Check it again in case a previous version let a bad block in - if (!CheckBlock(!fJustCheck, !fJustCheck)) + if(!CheckBlock(!fJustCheck, !fJustCheck, GetTotalCoin())) return false; // Do not allow blocks that contain transactions which 'overwrite' older transactions, @@ -1592,7 +1677,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) // Since we're just checking the block and not actually connecting it, it might not (and probably shouldn't) be on the disk to get the transaction from nTxPos = 1; else - nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size()); + nTxPos = pindex->nBlockPos + ::GetSerializeSize(*this, SER_DISK | SER_BLOCKHEADERONLY, CLIENT_VERSION) + GetSizeOfCompactSize(vtx.size()); map mapQueuedChanges; int64 nFees = 0; @@ -1681,24 +1766,35 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) // printf("==> Got prevHash = %s\n", prevHash.ToString().c_str()); } - if(totalCoin < VALUE_CHANGE) - { - if (vtx[0].GetValueOut() > GetProofOfWorkReward(pindex->nHeight, nFees, prevHash)) - return false; - } - else - if (vtx[0].GetValueOut() > GetProofOfWorkReward(pindex->nHeight, nFees, prevHash) + GetDevCoin(totalCoin)) - return false; - - if(totalCoin >= VALUE_CHANGE && IsProofOfWork()) - { - CBitcoinAddress address(!fTestNet ? DEV_ADDRESS : DEV_ADDRESS_TEST); - CScript scriptPubKey; - scriptPubKey.SetDestination(address.Get()); - if (vtx[0].vout[1].scriptPubKey != scriptPubKey) - return error("ConnectBlock() : coinbase does not pay to the dev address)"); - if (vtx[0].vout[1].nValue < GetDevCoin(totalCoin)) - return error("ConnectBlock() : coinbase does not pay enough to dev addresss"); + // danbi: update totalCoin as we are one behind here + // XXX: this might backfire in case of error... + totalCoin = pindex->nMoneySupply / COIN; + + int nPrevHeight = 0; + if(pindex->pprev) + nPrevHeight = pindex->pprev->nHeight; + + bool fContribution = false; + if((fTestNet && (nPrevHeight + 1 < nTestStage1)) || + (!fTestNet && ((totalCoin > VALUE_CHANGE) && (nPrevHeight + 1 < nLiveFork1)))) + fContribution = true; + + if(IsProofOfWork()) { + if(fContribution) { + if(vtx[0].GetValueOut() > GetProofOfWorkReward(pindex->nHeight, nFees, prevHash) + GetContributionAmount(totalCoin)) + return(error("ConnectBlock() : claiming to have created too much (contribution included)")); + + CBitcoinAddress address = GetFoundationAddress(totalCoin); + CScript scriptPubKey; + scriptPubKey.SetDestination(address.Get()); + if(vtx[0].vout[1].scriptPubKey != scriptPubKey) + return(error("ConnectBlock() : coinbase does not pay to the foundation address)")); + if(vtx[0].vout[1].nValue < GetContributionAmount(totalCoin)) + return(error("ConnectBlock() : coinbase does not pay enough to foundation addresss")); + } else { + if(vtx[0].GetValueOut() > GetProofOfWorkReward(pindex->nHeight, nFees, prevHash)) + return(error("ConnectBlock() : claiming to have created too much")); + } } // Update block index on disk without changing it in memory. @@ -1708,7 +1804,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) CDiskBlockIndex blockindexPrev(pindex->pprev); blockindexPrev.hashNext = pindex->GetBlockHash(); if (!txdb.WriteBlockIndex(blockindexPrev)) - return error("ConnectBlock() : WriteBlockIndex failed"); + return error("ConnectBlock() : WriteBlockIndex for blockindexPrev failed"); } // Watch for transactions paying to me @@ -1840,7 +1936,6 @@ bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew) return true; } - bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) { uint256 hash = GetHash(); @@ -1922,8 +2017,9 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) bnBestChainTrust = pindexNew->bnChainTrust; nTimeBestReceived = GetTime(); nTransactionsUpdated++; - printf("SetBestChain: new best=%s height=%d trust=%s date=%s\n", + printf("SetBestChain: new best=%s height=%d trust=%s moneysupply=%s date=%s\n", hashBestChain.ToString().c_str(), nBestHeight, bnBestChainTrust.ToString().c_str(), + FormatMoney(pindexBest->nMoneySupply).c_str(), DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); printf("Stake checkpoint: %x\n", pindexBest->nStakeModifierChecksum); @@ -1960,7 +2056,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) // ppcoin: total coin age spent in transaction, in the unit of coin-days. // Only those coins meeting minimum age requirement counts. As those // transactions not in main chain are not currently indexed so we -// might not find out about their coin age. Older transactions are +// might not find out about their coin age. Older transactions are // guaranteed to be in main chain by sync-checkpoint. This rule is // introduced to help nodes establish a consistent view of the coin // age (trust score) of competing branches. @@ -1986,7 +2082,7 @@ bool CTransaction::GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const CBlock block; if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) return false; // unable to read block of previous transaction - if (block.GetBlockTime() + nStakeMinAge > nTime) + if(block.GetBlockTime() + GetStakeMinAge(block.GetBlockTime()) > nTime) continue; // only count coins meeting min age requirement int64 nValueIn = txPrev.vout[txin.prevout.n].nValue; @@ -1996,11 +2092,7 @@ bool CTransaction::GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const printf("coin age nValueIn=%"PRI64d" nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nTime - txPrev.nTime, bnCentSecond.ToString().c_str()); } - CBigNum bnCoinDay; - if(totalCoin >= VALUE_CHANGE) - bnCoinDay = bnCentSecond * CENT / (24 * 60 * 60); - else - bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60); + CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60); if (fDebug && GetBoolArg("-printcoinage")) printf("coin age bnCoinDay=%s\n", bnCoinDay.ToString().c_str()); nCoinAge = bnCoinDay.getuint64(); @@ -2046,17 +2138,8 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) { pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; - - //test - if(pindexNew->pprev->nHeight >= 3) - { - int i = 1; - i++; - i++; - } } - // ppcoin: compute chain trust score pindexNew->bnChainTrust = (pindexNew->pprev ? pindexNew->pprev->bnChainTrust : 0) + pindexNew->GetBlockTrust(); @@ -2115,25 +2198,84 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) return true; } +// Start accepting AUX POW at this block +// +// Even if we do not accept AUX POW ourselves, we can always be the parent chain. -bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const -{ - totalCoin = GetTotalCoin(); - if(totalCoin >= VALUE_CHANGE && !fTestNet) +int GetAuxPowStartBlock() { + if(fTestNet) + return(nTestStage4); + else + return(INT_MAX); +} + +int GetOurChainID() { + return(0x0000); +} + +bool CBlock::CheckProofOfWork(int64 totalCoin) const { + int nHeight = GetBlockHeight(); + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + + if(nHeight >= GetAuxPowStartBlock()) { + + // Prevent same work from being submitted twice: + // - this block must have our chain ID + // - parent block must not have the same chain ID (see CAuxPow::Check) + // - index of this chain in chain merkle tree must be pre-determined (see CAuxPow::Check) + if (!fTestNet && nHeight != INT_MAX && GetChainID() != GetOurChainID()) + return error("CheckProofOfWork() : block does not have our chain ID"); + + if(auxpow.get() != NULL) { + + if(!(nVersion & BLOCK_VERSION_AUXPOW)) + return(error("CheckProofOfWork() : AuxPoW block version is not valid")); + + if(!auxpow->Check(GetHashGroestl(), GetChainID())) + return(error("CheckProofOfWork() : AuxPoW is not valid")); + + if(bnTarget <= 0 || (bnTarget > bnProofOfWorkLimit) || (auxpow->GetParentBlockHash() > bnTarget.getuint256())) + return(error("CheckProofOfWork() : AuxPoW failed")); + } + else + { + + if(bnTarget <= 0 || (bnTarget > bnProofOfWorkLimit) || (GetHashGroestl() > bnTarget.getuint256())) + return(error("CheckProofOfWork() : PoW failed")); + + } + } + else { - nStakeTargetSpacing = 10 * 60; //pos block spacing is 10 mins - nCoinbaseMaturity = 180; //coinbase maturity change to 180 blocks + if(auxpow.get() != NULL) + return(error("CheckProofOfWork() : AuxPoW is not allowed at this block height")); + + if(bnTarget <= 0 || (bnTarget > bnProofOfWorkLimit) || (GetHash(false, totalCoin) > bnTarget.getuint256())) + return(error("CheckProofOfWork() : proof of work failed")); } - //just for test - if(pindexBest != NULL && pindexBest->nHeight > 50 && fTestNet) - nCoinbaseMaturity = 15; + return(true); +} - if(totalCoin > VALUE_CHANGE) +bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, int64 totalCoin) const { + + // Update the coin mechanics variables post algorithm change + // Changing any of these requires a fork + if(!fTestNet) { + if (totalCoin >= 1000000) { + // after first reward reduction + nStakeTargetSpacing = 100; // PoS block spacing set to 100 seconds + nWorkTargetSpacing = 100; // PoW block spacing set to 100 seconds + nCoinbaseMaturity = 180; // coinbase maturity does not change + nStakeMinAge = 60 * 60 * 24 * 3; // min age is lowered from 7 to 3 days + } + else if (totalCoin > VALUE_CHANGE) { - nStakeMinAge = 30 * 24 * 60 * 60; - nStakeMaxAge = -1; + nStakeTargetSpacing = 10 * 60; //pos block spacing is 10 mins + nCoinbaseMaturity = 180; //coinbase maturity change to 180 blocks } + } // These are checks that are independent of context // that can be verified before saving an orphan block. @@ -2142,13 +2284,14 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return DoS(100, error("CheckBlock() : size limits failed")); - totalCoinDB = 1; - uint256 hash1 = GetHashScrypt(); - totalCoinDB = coinMax; - uint256 hash2 = GetHashGroestl(); // Check proof of work matches claimed amount - if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(hash1, nBits) && !CheckProofOfWork(hash2, nBits)) - return DoS(50, error("CheckBlock() : proof of work failed")); + // XXX: danbi - need to check both algos or we make it hard for initial sync + // can't use GetHash() optimization, because we are also called when + // new block comes during initial sync and that might come from "future" + // compromise: use optimization, but decrease penalty (was 50) +// if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetHashScrypt(), nBits) && !CheckProofOfWork(GetHashGroestl(), nBits)) + if(fCheckPOW && IsProofOfWork() && !CheckProofOfWork(totalCoin)) + return(error("CheckBlock() : proof of work failed")); // Check timestamp if (GetBlockTime() > GetAdjustedTime() + nMaxClockDrift) @@ -2166,17 +2309,6 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const if (vtx[i].IsCoinStake()) return DoS(100, error("CheckBlock() : coinstake in wrong position")); - if(totalCoin >= VALUE_CHANGE) - { - if (IsProofOfStake() && (vtx[0].vout.size() != 2 || !vtx[0].vout[0].IsEmpty() || !vtx[0].vout[1].IsEmpty() )) - return error("CheckBlock() : coinbase output not empty for proof-of-stake block"); - } - else - { - if (IsProofOfStake() && (vtx[0].vout.size() != 1 || !vtx[0].vout[0].IsEmpty())) - return error("CheckBlock() : coinbase output not empty for proof-of-stake block"); - } - // Check coinbase timestamp if (GetBlockTime() > (int64)vtx[0].nTime + nMaxClockDrift) return DoS(50, error("CheckBlock() : coinbase timestamp is too early")); @@ -2216,16 +2348,11 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const // Check merkle root if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) - return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); - - // ppcoin: check block signature - if (!CheckBlockSignature()) - return DoS(100, error("CheckBlock() : bad block signature")); + return(error("CheckBlock() : block %d merkle root hash mismatch", GetBlockHeight())); return true; } - bool CBlock::AcceptBlock() { // Check for duplicate @@ -2240,14 +2367,42 @@ bool CBlock::AcceptBlock() CBlockIndex* pindexPrev = (*mi).second; int nHeight = pindexPrev->nHeight+1; + int64 nMaxDrift = nMaxClockDrift; + if((fTestNet && (nHeight >= nTestStage3)) || + (!fTestNet && (nHeight >= nLiveFork1))) { + nMaxDrift = nNewMaxClockDrift; + } + + if(GetBlockTime() > GetAdjustedTime() + nMaxDrift) + return(error("AcceptBlock() : block time stamp too far in the future")); + + if(GetBlockTime() > (int64)vtx[0].nTime + nMaxDrift) + return(error("AcceptBlock() : coin base time stamp is too early")); + + if((GetBlockTime() <= pindexPrev->GetMedianTimePast()) || + (GetBlockTime() + nMaxDrift < pindexPrev->GetBlockTime())) + return(error("AcceptBlock() : block time stamp is too early")); + + if(IsProofOfStake()) { + if((fTestNet && (nHeight < nTestStage1)) || + (!fTestNet && ((totalCoin > VALUE_CHANGE) && (nHeight < nLiveFork1)))) { + /* vtx[0] must have 2 empty outputs */ + if((vtx[0].vout.size() != 2) || !vtx[0].vout[0].IsEmpty() || !vtx[0].vout[1].IsEmpty()) + return(error("AcceptBlock() : coin base outputs invalid for proof-of-stake block")); + } else { + /* vtx[0] must have 1 empty output */ + if((vtx[0].vout.size() != 1) || !vtx[0].vout[0].IsEmpty()) + return(error("AcceptBlock() : coin base output invalid for proof-of-stake block")); + } + } + + if(!CheckBlockSignature(nHeight)) + return(error("AcceptBlock() : bad block signature")); + // Check proof-of-work or proof-of-stake if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake())) return DoS(100, error("AcceptBlock() : incorrect %s", IsProofOfWork() ? "proof-of-work" : "proof-of-stake")); - // Check timestamp against prev - if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || GetBlockTime() + nMaxClockDrift < pindexPrev->GetBlockTime()) - return error("AcceptBlock() : block's timestamp is too early"); - // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, vtx) if (!tx.IsFinal(nHeight, GetBlockTime())) @@ -2308,13 +2463,11 @@ bool CBlock::AcceptBlock() CBigNum CBlockIndex::GetBlockTrust() const { - CBigNum bnTarget; - bnTarget.SetCompact(nBits); - if (bnTarget <= 0) - return 0; - return (IsProofOfStake()? (CBigNum(1)<<256) / (bnTarget+1) : 1); - - + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + if (bnTarget <= 0) + return 0; + return (IsProofOfStake()? (CBigNum(1)<<256) / (bnTarget+1) : 1); } bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck) @@ -2346,7 +2499,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str()); // Preliminary checks - if (!pblock->CheckBlock()) + if(!pblock->CheckBlock(true, true, GetTotalCoin())) return error("ProcessBlock() : CheckBlock FAILED"); // ppcoin: verify hash target and signature of coinstake tx @@ -2379,8 +2532,19 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) if (bnNewBlock > bnRequired) { if (pfrom) - pfrom->Misbehaving(100); - return error("ProcessBlock() : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work"); + pfrom->Misbehaving(10); // danbi: was 100, but that's way too heavy handed + if (fDebug) + { + printf("ProcessBlock(): "); + pblock->print(); + printf("\n"); + } + // danbi: Only refuse this block if time distance between the last sync checkpoint + // and the block's time is less than the checkpoints max span + if (deltaTime < CHECKPOINT_MAX_SPAN) + return error("ProcessBlock() : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work"); + else + return printf("ProcessBlock(CHECKPOINT_MAX_SPAN) : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work"); } } @@ -2433,9 +2597,10 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) ++mi) { CBlock* pblockOrphan = (*mi).second; + uint256 ohash = pblockOrphan->GetHash(); if (pblockOrphan->AcceptBlock()) - vWorkQueue.push_back(pblockOrphan->GetHash()); - mapOrphanBlocks.erase(pblockOrphan->GetHash()); + vWorkQueue.push_back(ohash); + mapOrphanBlocks.erase(ohash); setStakeSeenOrphan.erase(pblockOrphan->GetProofOfStake()); delete pblockOrphan; } @@ -2459,6 +2624,8 @@ bool CBlock::SignBlock(const CKeyStore& keystore) if(!IsProofOfStake()) { + if(!fSignWorkBlock) return(true); + for(unsigned int i = 0; i < vtx[0].vout.size(); i++) { const CTxOut& txout = vtx[0].vout[i]; @@ -2501,7 +2668,6 @@ bool CBlock::SignBlock(const CKeyStore& keystore) if (key.GetPubKey() != vchPubKey) return false; - return key.Sign(GetHashScrypt(), vchBlockSig); } } @@ -2511,7 +2677,7 @@ bool CBlock::SignBlock(const CKeyStore& keystore) } // ppcoin: check block signature -bool CBlock::CheckBlockSignature() const +bool CBlock::CheckBlockSignature(int nHeight) const { if (GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) return vchBlockSig.empty(); @@ -2533,15 +2699,17 @@ bool CBlock::CheckBlockSignature() const return false; if (vchBlockSig.empty()) return false; - totalCoinDB = 1; - uint256 hash1 = GetHashScrypt(); - totalCoinDB = coinMax; - uint256 hash2 = GetHashGroestl(); - return (key.Verify(hash1, vchBlockSig) || key.Verify(hash2, vchBlockSig)); + return (key.Verify(GetHashScrypt(), vchBlockSig) || key.Verify(GetHashGroestl(), vchBlockSig)); } } else { + if((fTestNet && (nHeight >= nTestStage2)) || + (!fTestNet && (nHeight >= nLiveFork1))) { + /* Insist on empty PoW block signatures */ + return(vchBlockSig.empty()); + } + for(unsigned int i = 0; i < vtx[0].vout.size(); i++) { const CTxOut& txout = vtx[0].vout[i]; @@ -2558,11 +2726,7 @@ bool CBlock::CheckBlockSignature() const continue; if (vchBlockSig.empty()) continue; - totalCoinDB = 1; - uint256 hash1 = GetHashScrypt(); - totalCoinDB = coinMax; - uint256 hash2 = GetHashGroestl(); - if(!key.Verify(hash1, vchBlockSig) && !key.Verify(hash2, vchBlockSig)) + if(!key.Verify(GetHashScrypt(), vchBlockSig) && !key.Verify(GetHashGroestl(), vchBlockSig)) continue; return true; @@ -2651,13 +2815,15 @@ bool LoadBlockIndex(bool fAllowNew) pchMessageStart[2] = 0xc0; pchMessageStart[3] = 0xef; - bnProofOfStakeLimit = bnProofOfStakeLimitTestNet; // 0x00000fff PoS base target is fixed in testnet - bnProofOfWorkLimit = bnProofOfWorkLimitTestNet; // 0x0000ffff PoW base target is fixed in testnet - nStakeMinAge = 5 * 60; // test net min age is 5 min - nStakeMaxAge = 60 * 60; // test net max age is 60 min - nModifierInterval = 60; // test modifier interval is 2 minutes - nCoinbaseMaturity = 180; // test maturity is 10 blocks - nStakeTargetSpacing = 3 * 60; // test block spacing is 3 minutes + bnProofOfStakeLimit = bnProofOfStakeLimitTestNet; + bnProofOfWorkLimit = bnProofOfWorkLimitTestNet; + + nStakeMinAge = 60 * 60; + nStakeMinAgeFixed = 60 * 60; + nStakeMaxAge = 2 * 24 * 60 * 60; + nModifierInterval = 5 * 60; + nCoinbaseMaturity = 10; + nStakeTargetSpacing = 100; } // @@ -2676,34 +2842,90 @@ bool LoadBlockIndex(bool fAllowNew) if (!fAllowNew) return false; - // Genesis block - const char* pszTimestamp = "Friday, July 12, 11:19 AM: Cavendish wins eventful 13th stage of Tour de France as Froome loses chunk of overall lead"; CTransaction txNew; - txNew.nTime = nChainStartTime; - txNew.vin.resize(1); - txNew.vout.resize(1); - txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(9999) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); - txNew.vout[0].SetEmpty(); - txNew.strTxComment = "text:Diamond genesis block"; - CBlock block; - block.vtx.push_back(txNew); - block.hashPrevBlock = 0; - block.hashMerkleRoot = block.BuildMerkleTree(); - block.nVersion = 1; - block.nTime = 1373654846; - block.nBits = bnProofOfWorkLimit.GetCompact(); - block.nNonce = 11111111; + + if(!fTestNet) { + + /* Livenet genesis */ + const char* pszTimestamp = "Friday, July 12, 11:19 AM: Cavendish wins eventful 13th stage of Tour de France as Froome loses chunk of overall lead"; + txNew.nVersion = 2; + txNew.nTime = nChainStartTime; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(9999) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vout[0].SetEmpty(); + txNew.strTxComment = "text:Diamond genesis block"; + + block.vtx.push_back(txNew); + block.hashPrevBlock = 0; + block.hashMerkleRoot = block.BuildMerkleTree(); + block.nVersion = 1; + block.nTime = 1373654846; + block.nBits = bnProofOfWorkLimit.GetCompact(); + block.nNonce = 11111111; + + } else { + + /* Testnet genesis */ + const char* pszTimestamp = "Let it ride!"; + txNew.nVersion = 2; + txNew.nTime = 1453766400; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(9999) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vout[0].SetEmpty(); + txNew.strTxComment = "text:Diamond testnet genesis"; + + block.vtx.push_back(txNew); + block.hashPrevBlock = 0; + block.hashMerkleRoot = block.BuildMerkleTree(); + block.nVersion = 1; + block.nTime = 1453939200; + block.nBits = bnProofOfWorkLimit.GetCompact(); + block.nNonce = 10211336; + + } //// debug print block.print(); - printf("block.GetHash() == %s\n", block.GetHashScrypt().ToString().c_str()); + printf("block.GetHash() == %s\n", fTestNet ? block.GetHashGroestl().ToString().c_str() : block.GetHashScrypt().ToString().c_str()); printf("block.hashMerkleRoot == %s\n", block.hashMerkleRoot.ToString().c_str()); printf("block.nTime = %u \n", block.nTime); printf("block.nNonce = %u \n", block.nNonce); - assert(block.hashMerkleRoot == uint256("0xaaaa88b5c5c937bcd7709c86903197009e02495dba5b919488d996dec10d26c4")); - assert(block.GetHashScrypt() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)); + if(!fTestNet) assert(block.hashMerkleRoot == uint256("0xaaaa88b5c5c937bcd7709c86903197009e02495dba5b919488d996dec10d26c4")); + else assert(block.hashMerkleRoot == uint256("0x6db63a9f5ba6b72c48781a584e859909ada5430b216804d08fbbd9f1c3129273")); + + /* Groestl testnet block mining */ + if(false && (block.GetHashGroestl() != hashGenesisBlockTestNet)) { + + printf("Genesis block mining...\n"); + + uint256 hashTarget = CBigNum().SetCompact(block.nBits).getuint256(); + uint256 hash; + + while(true) { + hash = block.GetHashGroestl(); + if(hash <= hashTarget) break; + if(!(block.nNonce & 0xFFF)) + printf("nonce %08X: hash = %s (target = %s)\n", + block.nNonce, hash.ToString().c_str(), + hashTarget.ToString().c_str()); + ++block.nNonce; + if(!block.nNonce) { + printf("nonce limit reached, incrementing time\n"); + ++block.nTime; + } + } + printf("block.nTime = %u \n", block.nTime); + printf("block.nNonce = %u \n", block.nNonce); + printf("block.GetHash = %s\n", block.GetHashGroestl().ToString().c_str()); + } + + block.print(); + if(!fTestNet) assert(block.GetHashScrypt() == hashGenesisBlock); + else assert(block.GetHashGroestl() == hashGenesisBlockTestNet); // Start new block file unsigned int nFile; @@ -2786,11 +3008,12 @@ void PrintBlockTree() // print item CBlock block; block.ReadFromDisk(pindex); +// danbi: optimization for GetHash() possible here printf("%d (%u,%u) %s %08x %s mint %7s tx %"PRIszu"", pindex->nHeight, pindex->nFile, pindex->nBlockPos, - block.GetHash().ToString().c_str(), + block.GetHash(true).ToString().c_str(), block.nBits, DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(), FormatMoney(pindex->nMint).c_str(), @@ -3004,9 +3227,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return true; } + // print what we got + if (fDebug) printf("%s from %s\n", strCommand.c_str(), pfrom->addr.ToString().c_str()); - - + // make sure we have current totalCoin + totalCoin = GetTotalCoin(); if (strCommand == "version") { @@ -3031,8 +3256,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return false; } - if (pfrom->nVersion == 10300) - pfrom->nVersion = 300; if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; if (!vRecv.empty()) @@ -3054,6 +3277,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return true; } + /* Disconnect all obsolete clients after 20 June 2016 12:00:00 UTC */ + uint nAdjTime = GetAdjustedTime(); + if(nAdjTime > 1466424000) { + if(pfrom->nVersion < MIN_PROTOCOL_VERSION) { + printf("obsolete node %s with client %d, disconnecting\n", + pfrom->addr.ToString().c_str(), pfrom->nVersion); + pfrom->fDisconnect = true; + return(true); + } + } + // ppcoin: record my external IP reported by peer if (addrFrom.IsRoutable() && addrMe.IsRoutable()) addrSeenByPeer = addrMe; @@ -3203,7 +3437,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } } // Do not store addresses outside our network - if (fReachable) + // danbi: ignore reachable if we do OneShot + if (fReachable || pfrom->fOneShot) vAddrOk.push_back(addr); } addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60); @@ -3213,7 +3448,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fDisconnect = true; } - else if (strCommand == "inv") { vector vInv; @@ -3274,14 +3508,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return error("message getdata size() = %"PRIszu"", vInv.size()); } - if (fDebugNet || (vInv.size() != 1)) + if (fDebugNet && (vInv.size() != 1)) printf("received getdata (%"PRIszu" invsz)\n", vInv.size()); BOOST_FOREACH(const CInv& inv, vInv) { if (fShutdown) return true; - if (fDebugNet || (vInv.size() == 1)) + if (fDebugNet && (vInv.size() == 1)) printf("received getdata for: %s\n", inv.ToString().c_str()); if (inv.type == MSG_BLOCK) @@ -3298,7 +3532,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (inv.hash == pfrom->hashContinue) { // ppcoin: send latest proof-of-work block to allow the - // download node to accept as orphan (proof-of-stake + // download node to accept as orphan (proof-of-stake // block might be rejected by stake connection check) vector vInv; vInv.push_back(CInv(MSG_BLOCK, GetLastBlockIndex(pindexBest, false)->GetBlockHash())); @@ -3349,25 +3583,30 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Send the rest of the chain if (pindex) pindex = pindex->pnext; - int nLimit = 500; - printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit); + int nLimit = 500 + locator.GetDistanceBack(); + unsigned int nBytes = 0; + if (fDebug) printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit); for (; pindex; pindex = pindex->pnext) { if (pindex->GetBlockHash() == hashStop) { - printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); + if (fDebug) printf(" getblocks (hashStop) stopping at %d %s (%u bytes)\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str(), nBytes); // ppcoin: tell downloading node about the latest block if it's // without risk being rejected due to stake connection check - if (hashStop != hashBestChain && pindex->GetBlockTime() + nStakeMinAge > pindexBest->GetBlockTime()) + if((hashStop != hashBestChain) && + (pindex->GetBlockTime() + nStakeMinAgeFixed > pindexBest->GetBlockTime())) pfrom->PushInventory(CInv(MSG_BLOCK, hashBestChain)); break; } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); - if (--nLimit <= 0) + CBlock block; + block.ReadFromDisk(pindex, true); + nBytes += block.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION); + if (--nLimit <= 0 || nBytes >= SendBufferSize()/2) { // When this block is requested, we'll send an inv that'll make them // getblocks the next batch of inventory. - printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); + if (fDebug) printf(" getblocks (nLimit) stopping at limit %d %s (%u bytes)\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str(), nBytes); pfrom->hashContinue = pindex->GetBlockHash(); break; } @@ -3497,11 +3736,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { CBlock block; vRecv >> block; + uint256 hashBlock = block.GetHash(); - printf("received block %s\n", block.GetHash().ToString().substr(0,20).c_str()); + printf("received block %s height %d\n", + hashBlock.ToString().substr(0,20).c_str(), block.GetBlockHeight()); // block.print(); - CInv inv(MSG_BLOCK, block.GetHash()); + CInv inv(MSG_BLOCK, hashBlock); pfrom->AddInventoryKnown(inv); if (ProcessBlock(pfrom, &block)) @@ -3933,50 +4174,6 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } - - - -////////////////////////////////////////////////////////////////////////////// -// -// BitcoinMiner -// - -int static FormatHashBlocks(void* pbuffer, unsigned int len) -{ - unsigned char* pdata = (unsigned char*)pbuffer; - unsigned int blocks = 1 + ((len + 8) / 64); - unsigned char* pend = pdata + 64 * blocks; - memset(pdata + len, 0, 64 * blocks - len); - pdata[len] = 0x80; - unsigned int bits = len * 8; - pend[-1] = (bits >> 0) & 0xff; - pend[-2] = (bits >> 8) & 0xff; - pend[-3] = (bits >> 16) & 0xff; - pend[-4] = (bits >> 24) & 0xff; - return blocks; -} - -static const unsigned int pSHA256InitState[8] = -{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; - -void SHA256Transform(void* pstate, void* pinput, const void* pinit) -{ - SHA256_CTX ctx; - unsigned char data[64]; - - SHA256_Init(&ctx); - - for (int i = 0; i < 16; i++) - ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]); - - for (int i = 0; i < 8; i++) - ctx.h[i] = ((uint32_t*)pinit)[i]; - - SHA256_Update(&ctx, data, sizeof(data)); - for (int i = 0; i < 8; i++) - ((uint32_t*)pstate)[i] = ctx.h[i]; -} - // Some explaining would be appreciated class COrphan { @@ -4005,7 +4202,7 @@ class COrphan uint64 nLastBlockTx = 0; uint64 nLastBlockSize = 0; int64 nLastCoinStakeSearchInterval = 0; - + // We want to sort transactions by priority and fee, so: typedef boost::tuple TxPriority; class TxPriorityCompare @@ -4041,21 +4238,29 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) if (!pblock.get()) return NULL; + int nPrevHeight = pindexBest->nHeight; + + bool fContribution = false; + if((fTestNet && (nPrevHeight + 1 < nTestStage1)) || + (!fTestNet && ((totalCoin > VALUE_CHANGE) && (nPrevHeight + 1 < nLiveFork1)))) + fContribution = true; + // Create coinbase tx CTransaction txNew; txNew.vin.resize(1); txNew.vin[0].prevout.SetNull(); - if(totalCoin >= VALUE_CHANGE) - { - CBitcoinAddress address(!fTestNet ? DEV_ADDRESS : DEV_ADDRESS_TEST); + if(fContribution) { + CBitcoinAddress address = GetFoundationAddress(totalCoin); txNew.vout.resize(2); - txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG; - txNew.vout[1].scriptPubKey.SetDestination(address.Get()); - } - else - { + if(!fProofOfStake) { + txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG; + txNew.vout[1].scriptPubKey.SetDestination(address.Get()); + } + } else { txNew.vout.resize(1); - txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG; + if(!fProofOfStake) { + txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG; + } } // Add our coinbase tx as first transaction @@ -4096,17 +4301,17 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) int64 nSearchTime = txCoinStake.nTime; // search to current time if (nSearchTime > nLastCoinStakeSearchTime) { - // printf(">>> OK1\n"); if (pwallet->CreateCoinStake(*pwallet, pblock->nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake)) { - if (txCoinStake.nTime >= max(pindexPrev->GetMedianTimePast()+1, pindexPrev->GetBlockTime() - nMaxClockDrift)) + if(txCoinStake.nTime >= max(pindexPrev->GetMedianTimePast() + 1, + pindexPrev->GetBlockTime() - nNewMaxClockDrift)) { // make sure coinstake would meet timestamp protocol // as it would be the same as the block timestamp - pblock->vtx.push_back(txCoinStake); pblock->vtx[0].vout[0].SetEmpty(); - if(totalCoin >= VALUE_CHANGE) + if(fContribution) pblock->vtx[0].vout[1].SetEmpty(); pblock->vtx[0].nTime = txCoinStake.nTime; + pblock->vtx.push_back(txCoinStake); } } nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime; @@ -4153,7 +4358,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) if (!mempool.mapTx.count(txin.prevout.hash)) { printf("ERROR: mempool transaction missing input\n"); - if (fDebug) assert("mempool transaction missing input" == 0); + // if (fDebug) assert("mempool transaction missing input" == 0); fMissingInputs = true; if (porphan) vOrphan.pop_back(); @@ -4311,8 +4516,8 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) if (pblock->IsProofOfWork()) { pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pindexPrev->nHeight+1, nFees, pindexPrev->GetBlockHash()); - if(totalCoin >= VALUE_CHANGE) - pblock->vtx[0].vout[1].nValue = GetDevCoin(totalCoin); + if(fContribution) + pblock->vtx[0].vout[1].nValue = GetContributionAmount(totalCoin); } // Fill in header @@ -4320,7 +4525,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) if (pblock->IsProofOfStake()) pblock->nTime = pblock->vtx[1].nTime; //same as coinstake timestamp pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime()); - pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift); + pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nNewMaxClockDrift); if (pblock->IsProofOfWork()) pblock->UpdateTime(pindexPrev); pblock->nNonce = 0; @@ -4348,49 +4553,33 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& } -void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1) -{ - // - // Pre-build hash buffers - // - struct - { - struct unnamed2 - { - int nVersion; - uint256 hashPrevBlock; - uint256 hashMerkleRoot; - unsigned int nTime; - unsigned int nBits; - unsigned int nNonce; - } - block; - unsigned char pchPadding0[64]; - uint256 hash1; - unsigned char pchPadding1[64]; - } - tmp; - memset(&tmp, 0, sizeof(tmp)); - - tmp.block.nVersion = pblock->nVersion; - tmp.block.hashPrevBlock = pblock->hashPrevBlock; - tmp.block.hashMerkleRoot = pblock->hashMerkleRoot; - tmp.block.nTime = pblock->nTime; - tmp.block.nBits = pblock->nBits; - tmp.block.nNonce = pblock->nNonce; - - FormatHashBlocks(&tmp.block, sizeof(tmp.block)); - FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1)); - - // Byte swap all the input buffer - for (unsigned int i = 0; i < sizeof(tmp)/4; i++) - ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]); - - // Precalc the first half of the first hash, which stays constant - SHA256Transform(pmidstate, &tmp.block, pSHA256InitState); - - memcpy(pdata, &tmp.block, 128); - memcpy(phash1, &tmp.hash1, 64); +void FormatDataBuffer(CBlock *pblock, uint *pdata) { + uint i; + + struct { + int nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + uint nTime; + uint nBits; + uint nNonce; + } data; + + data.nVersion = pblock->nVersion; + data.hashPrevBlock = pblock->hashPrevBlock; + data.hashMerkleRoot = pblock->hashMerkleRoot; + data.nTime = pblock->nTime; + data.nBits = pblock->nBits; + data.nNonce = pblock->nNonce; + + /* Block header size in bits */ + pdata[31] = 640; + /* Convert LE to BE and copy */ + for(i = 0; i < 20; i++) + pdata[i] = ByteReverse(((uint *) &data)[i]); + /* Erase the remaining part */ + for(i = 20; i < 31; i++) + pdata[i] = 0; } @@ -4399,17 +4588,37 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) uint256 hash = pblock->GetHash(); uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - if (hash > hashTarget && pblock->IsProofOfWork()) - return error("BitcoinMiner : proof-of-work not meeting target"); + if(pblock->IsProofOfWork()) { + CAuxPow *auxpow = pblock->auxpow.get(); + + if(auxpow != NULL) { + if(!auxpow->Check(hash, pblock->GetChainID())) + return(error("AuxPoW is not valid")); + + if(auxpow->GetParentBlockHash() > hashTarget) + return(error("AuxPoW parent hash %s is not under target %s", + auxpow->GetParentBlockHash().GetHex().c_str(), hashTarget.GetHex().c_str())); + + printf("DiamondMiner:\n"); + printf("AUX proof-of-work found \n our hash: %s \n parent hash: %s \n target: %s\n", + hash.GetHex().c_str(), auxpow->GetParentBlockHash().GetHex().c_str(), hashTarget.GetHex().c_str()); + } else { + if(hash > hashTarget) + return(error("DiamondMiner : proof-of-work not meeting target")); + + printf("DiamondMiner:\n"); + printf("proof-of-work found \n hash: %s \ntarget: %s\n", + hash.GetHex().c_str(), hashTarget.GetHex().c_str()); + } + } else { + + printf("DiamondMiner:\n"); + printf("proof-of-stake found \n hash: %s \ntarget: %s\n", + hash.GetHex().c_str(), hashTarget.GetHex().c_str()); + } - //// debug print - printf("BitcoinMiner:\n"); - printf("new block found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); pblock->print(); - if(totalCoin >= VALUE_CHANGE) - printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue + pblock->vtx[0].vout[1].nValue).c_str()); - else - printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); + printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); // Found a solution { @@ -4423,7 +4632,7 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) // Track how many getdata requests this block gets { LOCK(wallet.cs_wallet); - wallet.mapRequestCount[pblock->GetHash()] = 0; + wallet.mapRequestCount[hash] = 0; } // Process this block the same as if we had received it from another node @@ -4442,8 +4651,6 @@ static int nLimitProcessors = -1; void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) { - void *scratchbuf = scrypt_buffer_alloc(); - printf("CPUMiner started for proof-of-%s\n", fProofOfStake? "stake" : "work"); SetThreadPriority(THREAD_PRIORITY_LOWEST); @@ -4453,20 +4660,20 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) // Each thread has its own key and counter CReserveKey reservekey(pwallet); unsigned int nExtraNonce = 0; - totalCoin = GetTotalCoin(); + while (fGenerateBitcoins || fProofOfStake) { totalCoin = GetTotalCoin(); - if(totalCoin < VALUE_CHANGE && fProofOfStake) - break; if (fShutdown) return; - while (vNodes.empty() || IsInitialBlockDownload()) + + while (vNodes.empty() || IsInitialBlockDownload() || pwallet->IsLocked()) { + nLastCoinStakeSearchInterval = 0; Sleep(1000); if (fShutdown) return; - if ((!fGenerateBitcoins) && !fProofOfStake) + if (!fGenerateBitcoins && !fProofOfStake) return; } @@ -4510,20 +4717,19 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) continue; } -// printf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(), -// ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); + //printf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(), + // ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); // // Pre-build hash buffers // - char pmidstatebuf[32+16]; char* pmidstate = alignup<16>(pmidstatebuf); - char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf); - char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf); + char pdatabuf[128+16]; + char *pdata = alignup<16>(pdatabuf); - FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1); + FormatDataBuffer(pblock.get(), (uint *) pdata); unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4); - unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12); +// unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12); // @@ -4532,96 +4738,31 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) int64 nStart = GetTime(); uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - unsigned int max_nonce = 0xffff0000; - block_header res_header; - uint256 result; - while (true) { - + // always calculate totalCoin totalCoin = GetTotalCoin(); - unsigned int nHashesDone = 0; - - uint256 thash; - char scratchpad[SCRYPT_SCRATCHPAD_SIZE]; - - if(totalCoin < VALUE_CHANGE) + // new block, use groestl + uint256 hash = pblock->GetHashGroestl(); + if (hash <= hashTarget) { - //scrypt_1024_1_1_256_sp(BEGIN(pblock->nVersion), BEGIN(thash), scratchpad); - - if (thash <= hashTarget) + if (!pblock->SignBlock(*pwalletMain)) { - // Found a solution - SetThreadPriority(THREAD_PRIORITY_NORMAL); - CheckWork(pblock.get(), *pwallet, reservekey); - SetThreadPriority(THREAD_PRIORITY_LOWEST); + strMintWarning = strMintMessage; break; } - pblock->nNonce += 1; - nHashesDone += 1; - if ((pblock->nNonce & 0xFF) == 0) - break; -// unsigned int nNonceFound; - -// nNonceFound = scanhash_scrypt( -// (block_header *)&pblock->nVersion, -// scratchbuf, -// max_nonce, -// nHashesDone, -// UBEGIN(result), -// &res_header -// ); - -// // Check if something found -// if (nNonceFound != (unsigned int) -1 && totalCoin < VALUE_CHANGE) -// { -// if (result <= hashTarget && totalCoin < VALUE_CHANGE) -// { -// // Found a solution -// pblock->nNonce = nNonceFound; -// assert(result == pblock->GetHash()); -// if (!pblock->SignBlock(*pwalletMain)) -// { -// // strMintWarning = strMintMessage; -// break; -// } -// strMintWarning = ""; - -// SetThreadPriority(THREAD_PRIORITY_NORMAL); -// CheckWork(pblock.get(), *pwalletMain, reservekey); -// SetThreadPriority(THREAD_PRIORITY_LOWEST); -// break; -// } -// else -// break; -// } -// else -// break; - } - else - { - uint256 hash; - hash = pblock->GetHash(); - if (hash <= hashTarget) - { - if (!pblock->SignBlock(*pwalletMain)) - { -// strMintWarning = strMintMessage; - break; - } - strMintWarning = ""; - // nHashesDone += pblock->nNonce; - SetThreadPriority(THREAD_PRIORITY_NORMAL); + strMintWarning = ""; + SetThreadPriority(THREAD_PRIORITY_NORMAL); - printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); - pblock->print(); + printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); + pblock->print(); - CheckWork(pblock.get(), *pwalletMain, reservekey); - SetThreadPriority(THREAD_PRIORITY_LOWEST); - break; - } - ++pblock->nNonce; + CheckWork(pblock.get(), *pwalletMain, reservekey); + SetThreadPriority(THREAD_PRIORITY_LOWEST); + break; } + ++pblock->nNonce; + // Meter hashes/sec static int64 nHashCounter; if (nHPSTimerStart == 0) @@ -4630,12 +4771,7 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) nHashCounter = 0; } else - { - if(totalCoin < VALUE_CHANGE) - nHashCounter += nHashesDone; - else - nHashCounter += 1; - } + nHashCounter++; if (GetTimeMillis() - nHPSTimerStart > 4000) { static CCriticalSection cs; @@ -4646,12 +4782,12 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart); nHPSTimerStart = GetTimeMillis(); nHashCounter = 0; -// static int64 nLogTime; -// if (GetTime() - nLogTime > 30 * 60) -// { -// nLogTime = GetTime(); + static int64 nLogTime; + if (GetTime() - nLogTime > 30 * 60) + { + nLogTime = GetTime(); printf("hashmeter %3d CPUs %6.0f khash/s\n", vnThreadsRunning[THREAD_MINER], dHashesPerSec/1000.0); -// } + } } } } @@ -4665,16 +4801,8 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) return; if (vNodes.empty()) break; - if(totalCoin < VALUE_CHANGE) - { - if (nBlockNonce >= 0xffff0000) - break; - } - else - { - if (++pblock->nNonce >= 0xffff0000) - break; - } + if (pblock->nNonce >= 0xffff0000) + break; if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60) break; if (pindexPrev != pindexBest) @@ -4682,16 +4810,13 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) // Update nTime every few seconds pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime()); - pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift); + pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nNewMaxClockDrift); pblock->UpdateTime(pindexPrev); nBlockTime = ByteReverse(pblock->nTime); - - if (pblock->GetBlockTime() >= (int64)pblock->vtx[0].nTime + nMaxClockDrift) + if (pblock->GetBlockTime() >= (int64)pblock->vtx[0].nTime + nNewMaxClockDrift) break; // need to update coinbase timestamp } } - - scrypt_buffer_free(scratchbuf); } void static ThreadBitcoinMiner(void* parg) @@ -4744,30 +4869,103 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet) } } - -int64 GetDevCoin(int64 totalCoin) { - if(totalCoin < 1000000) +// Diamond coin mechanics +// Foundation contribution +// 0.05 until 1000000 coins generated +// 0.01 afterwards +// Changing this requires a fork +int64 GetContributionAmount(int64 totalCoin) { + if (totalCoin < 1000000) return 0.05 * COIN; else return 0.01 * COIN; } -uint256 CBlock::GetHash() const +// Return Foundation Address to send contributions to +// address changes every 500000 coin in order to reduce wallet bloat +// +CBitcoinAddress GetFoundationAddress(int64 totalCoin) { + if (fTestNet) + return CBitcoinAddress("mzN2GRXyBuw7uWxEEBcxUeyga4Ka7xAscJ"); // test Foundation address + + if (totalCoin < 1000000) + return CBitcoinAddress("dZi9hpA5nBC6tSAbPSsiMjb6HeQTprcWHz"); + else if (totalCoin < 1500000) + return CBitcoinAddress("dTZMqKTYGUwF3aLFx31anxsNKrtaWn6U3x"); + else if (totalCoin < 2000000) + return CBitcoinAddress("dPbhK6rPtaBeGepU1nqghZRy4NWot5T7dG"); + else if (totalCoin < 2500000) + return CBitcoinAddress("dbXsr1iVzF6KDRxDspAbvtCpdscF28bD8J"); + else + return CBitcoinAddress ("dSDEptc8gJzbEe3Kfta8McvnBmapk6fcrR"); +} + +uint256 CBlock::GetHash(bool existingBlock, int64 coins) const { - if((totalCoinDB <= VALUE_CHANGE && totalCoin == -1) || (totalCoin >= 0 && totalCoin < VALUE_CHANGE)) - { - uint256 thash; - void * scratchbuff = scrypt_buffer_alloc(); + if(fTestNet) + return(GetHashGroestl()); - scrypt_hash(CVOIDBEGIN(nVersion), sizeof(block_header), UINTBEGIN(thash), scratchbuff); + // existingBlock is set by default to false + // coins is set by default to (global) totalCoin + // + // There are two distinct cases when we are called + // existingBlock=true - a block already in the blockchain index + // both coins and totalCoin are ignored + // existingBlock=false - for a new block or when totalCoins is known + // in the context. The parameter coins is used in this case + +if (fDebug && GetBoolArg("-printjunk") && coins != totalCoin) { + printf("COMP: coins(%"PRI64d") != totalCoin(%"PRI64d")\n", coins, totalCoin); +} +// special case +if (coins == 0 && !(totalCoin == 0)) { + printf("CBlock::GetHash: coins is 0, totalCoin is %"PRI64d"\n", totalCoin); + coins=totalCoin; +} - scrypt_buffer_free(scratchbuff); + if (existingBlock) + { + //printf("CBlock::GetHash() look up an existing block\n"); + // We first check Groestl hash as that's less expensive and + // there are enough groestl blocks now + uint256 hash_groestl = GetHashGroestl(); + if (hash_groestl == uint256("0xe12ddb2c35d84403b0a045574ecce223f7e2f0db4506e76ed3d43bc464ace40c")) { + printf("GetHash(true): hash fixed up (groestl)\n"); + return uint256("0x000009d32c4f8ec5d66a65e88c8099da31452de0daec3b0b68926659b50b4e8f"); + } + CBlockIndex* pblockindex_groestl = mapBlockIndex[hash_groestl]; + if (pblockindex_groestl) + return hash_groestl; + + // we are here so it must be Scrypt + uint256 hash_scrypt = GetHashScrypt(); + if (hash_scrypt == uint256("0x92134c4608025b6bd945731158391079590d0e7e0c60bd7d09a50c0b0251c6ac")) { + printf("GetHash(true): hash fixed up (scrypt)\n"); + return uint256("0x00000d652b612a94e1c830bf4e05106438ea6b53372b29206f0b820d91a9b67b"); + } + // find the index position(s) + CBlockIndex* pblockindex_scrypt = mapBlockIndex[hash_scrypt]; + if (pblockindex_scrypt) + return hash_scrypt; - return thash; + // XXX: We are here, asked for an existing hash but did not find it! + printf("CBlock::GetHash(true): neither scrypt nor groestl hash found in the block index! Failing back..\n"); } - else if((totalCoinDB > VALUE_CHANGE && totalCoin == -1) || (totalCoin >= VALUE_CHANGE)) + + if (coins <= VALUE_CHANGE) { - return HashGroestl(BEGIN(nVersion), END(nNonce)); + uint256 hash_scrypt = GetHashScrypt(); + if (hash_scrypt == uint256("0x92134c4608025b6bd945731158391079590d0e7e0c60bd7d09a50c0b0251c6ac")) { + printf("GetHash(): hash fixed up (scrypt)\n"); + return uint256("0x00000d652b612a94e1c830bf4e05106438ea6b53372b29206f0b820d91a9b67b"); + } + return hash_scrypt; + } + + uint256 hash_groestl = GetHashGroestl(); + if (hash_groestl == uint256("0xe12ddb2c35d84403b0a045574ecce223f7e2f0db4506e76ed3d43bc464ace40c")) { + printf("GetHash(): hash fixed up (groestl)\n"); + return uint256("0x000009d32c4f8ec5d66a65e88c8099da31452de0daec3b0b68926659b50b4e8f"); } - return HashGroestl(BEGIN(nVersion), END(nNonce)); + return hash_groestl; } diff --git a/src/main.h b/src/main.h index 0418f93..43a6607 100644 --- a/src/main.h +++ b/src/main.h @@ -13,6 +13,7 @@ #include "bitcoinrpc.h" #include #include "hash.h" +#include "base58.h" class CWallet; class CBlock; @@ -25,6 +26,7 @@ class CAddress; class CInv; class CRequestTracker; class CNode; +class CAuxPow; static const unsigned int MAX_BLOCK_SIZE = 1000000; static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; @@ -39,12 +41,21 @@ static const int64 CIRCULATION_MONEY = MAX_MONEY; static const double TAX_PERCENTAGE = 0.01; static const int64 MAX_MINT_PROOF_OF_STAKE = 1 * CENT; static const int64 MIN_TXOUT_AMOUNT = MIN_TX_FEE; -static const int64 VALUE_CHANGE = 369494; -#define DEV_ADDRESS "dZi9hpA5nBC6tSAbPSsiMjb6HeQTprcWHz" -#define DEV_ADDRESS_TEST "mwmPTAA7cSDY8Dd5rRHuYitwS2hByXQpdA" +static const int64 VALUE_CHANGE = 369494; // When to switch to Groestl +static const int64 POS_RESTART = 450000; // When to apply fixes to enable PoS -inline int64_t PastDrift(int64 nTime) { return nTime - 15 * 60; } // up to 1 day from the past -inline int64_t FutureDrift(int64 nTime) { return nTime + 15 * 60; } // up to 1 day from the future +static const int nTestStage1 = 315; /* Testnet: disable foundation PoW share */ +static const int nTestStage2 = 330; /* Testnet: disable PoW block signature */ +static const int nTestStage3 = 340; /* Testnet: reduce time drifts */ +static const int nTestStage4 = 440; /* Testnet: enable merged mining */ + +/* Testnet: SHA-256 transaction hashing after 8-Feb-2016 15:00 UTC */ +static const unsigned int nTestTxSwitch = 1454943600; + +static const int nLiveFork1 = 1549200; /* Livenet: cumulative hard fork */ + +/* Livenet: SHA-256 transaction hashing and fixed min. stake age */ +static const unsigned int nLiveTimeSwitch = ~0U; inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } // Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. @@ -57,14 +68,15 @@ static const int fHaveUPnP = false; #endif static const uint256 hashGenesisBlockOfficial("0x2d8b2c67b7f56e70b9b16b377b988bb1bcb7a92b5b01c0fedcc60dc9c46ab511"); -static const uint256 hashGenesisBlockTestNet ("0xa0d5631064182289fbd9cf2451ddada6d698f65dfa44bc20b3c6e6548fdd2666"); +static const uint256 hashGenesisBlockTestNet ("0x0000004b5393b6564f68472ce799a25c56fc63f45d55b7b1f450ac5836598a20"); static const int64 nMaxClockDrift = 2 * 60 * 60; // two hours +static const int64 nNewMaxClockDrift = 15 * 60; + +extern bool fOldMerkleHash; extern CScript COINBASE_FLAGS; -extern int64 coinMax; -extern int64 totalCoinDB; extern int64 totalCoin; extern CCriticalSection cs_main; extern std::map mapBlockIndex; @@ -72,6 +84,7 @@ extern std::set > setStakeSeen; extern uint256 hashGenesisBlock; extern CBlockIndex* pindexGenesisBlock; extern unsigned int nStakeMinAge; +extern unsigned int nStakeMinAgeFixed; extern int nCoinbaseMaturity; extern int nBestHeight; extern CBigNum bnBestChainTrust; @@ -93,17 +106,19 @@ extern std::map mapOrphanBlocks; // Settings extern int64 nTransactionFee; +extern bool fUseFastIndex; +// where to deposit change +extern CTxDestination changeAddress; // Minimum disk space required - used in CheckDiskSpace() static const uint64 nMinDiskSpace = 52428800; - class CReserveKey; class CTxDB; class CTxIndex; - -int64 GetDevCoin(int64 totalCoin); +int64 GetContributionAmount(int64 totalCoin); +CBitcoinAddress GetFoundationAddress(int64 totalCoin); void RegisterWallet(CWallet* pwalletIn); void UnregisterWallet(CWallet* pwalletIn); void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false, bool fConnect = true); @@ -120,11 +135,10 @@ bool LoadExternalBlockFile(FILE* fileIn); void GenerateBitcoins(bool fGenerate, CWallet* pwallet); CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake=false); void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); -void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); +void FormatDataBuffer(CBlock *pblock, unsigned int *pdata); bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); -bool CheckProofOfWork(uint256 hash, unsigned int nBits); int64 GetProofOfWorkReward(int nHeight, int64 nFees, uint256 prevHash); -int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTime, int nHeight); +int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTime, int nHeight, float reactorRate = 0); unsigned int ComputeMinWork(unsigned int nBase, int64 nTime); unsigned int ComputeMinStake(unsigned int nBase, int64 nTime, unsigned int nBlockTime); int GetNumBlocksOfPeers(); @@ -143,8 +157,6 @@ void ResendWalletTransactions(); - - bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); /** Position on disk for a particular transaction. */ @@ -390,7 +402,7 @@ class CTxOut uint256 GetHash() const { - return SerializeHash(*this); + return(SerializeHash2(*this)); } friend bool operator==(const CTxOut& a, const CTxOut& b) @@ -441,8 +453,7 @@ typedef std::map > MapPrevTx; class CTransaction { public: - static const int LEGACY_VERSION_1=1; - static const int CURRENT_VERSION = 2; + static const int CURRENT_VERSION = 3; int nVersion; unsigned int nTime; @@ -468,14 +479,19 @@ class CTransaction READWRITE(vin); READWRITE(vout); READWRITE(nLockTime); - if(this->nVersion > LEGACY_VERSION_1) { + if(this->nVersion > 1) { READWRITE(strTxComment); } ) void SetNull() { - nVersion = CTransaction::CURRENT_VERSION; nTime = GetAdjustedTime(); + if((fTestNet && (nTime < nTestTxSwitch)) || + (!fTestNet)) { + nVersion = 2; + } else { + nVersion = CTransaction::CURRENT_VERSION; + } vin.clear(); vout.clear(); nLockTime = 0; @@ -488,9 +504,13 @@ class CTransaction return (vin.empty() && vout.empty()); } - uint256 GetHash() const - { - return SerializeHash(*this); + uint256 GetHash() const { + if((fTestNet && (nTime < nTestTxSwitch)) || + (!fTestNet)) { + return(SerializeHash2(*this)); + } else { + return(SerializeHash1(*this)); + } } bool IsFinal(int nBlockHeight=0, int64 nBlockTime=0) const @@ -546,10 +566,28 @@ class CTransaction bool IsCoinStake() const { - // ppcoin: the coin stake transaction is marked with the first output empty - return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty()); + /* Reactor stakes are marked with an OP code in the first output all + * other stakes are marked by having the first output left empty. */ + if (!vout[0].IsEmpty() && vout[0].scriptPubKey[0] == OP_REACTOR) { + return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].scriptPubKey[0] == OP_REACTOR); + } else { + return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty()); + } } + bool IsCoinBaseOrStake() const + { + return (IsCoinBase() || IsCoinStake()); + } + + /** Check if a stake transaction is a reactor stake + this is defined in reactors.cpp and is utilized in ConnectInputs. + Passes the database filename so that tests can be ran on a mockdb + instead of using the db in the datadir. + @ return True for valid and DoS or false for all else. + */ + bool IsReactorStake(std::string strFileName, CScript scriptPubKeyType, CScript scriptPubKeyAddress, unsigned int nTime, int64 nValueIn, int64 nValueOut, uint64 nCoinAge, unsigned int nBits, int nHeight); + /** Check for standard transaction types @return True if all outputs (scriptPubKeys) use only standard transaction forms */ @@ -662,7 +700,7 @@ class CTransaction { std::string str; str += IsCoinBase()? "Coinbase" : (IsCoinStake()? "Coinstake" : "CTransaction"); - str += strprintf("(hash=%s, nTime=%d, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%d)\n", + str += strprintf("(hash=%s, nTime=%d, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%d), TxComment=%s\n", GetHash().ToString().substr(0,10).c_str(), nTime, nVersion, @@ -838,7 +876,29 @@ class CTxIndex }; +template +int ReadWriteAuxPow(Stream& s, const boost::shared_ptr& auxpow, int nType, + int nVersion, CSerActionSerialize ser_action); + +template +int ReadWriteAuxPow(Stream& s, boost::shared_ptr& auxpow, int nType, + int nVersion, CSerActionUnserialize ser_action); + +template +int ReadWriteAuxPow(Stream& s, const boost::shared_ptr& auxpow, int nType, + int nVersion, CSerActionGetSerializeSize ser_action); + +enum { + // primary version + BLOCK_VERSION_DEFAULT = (1 << 0), + + // modifiers + BLOCK_VERSION_AUXPOW = (1 << 8), + // bits allocated for chain ID + BLOCK_VERSION_CHAIN_START = (1 << 16), + BLOCK_VERSION_CHAIN_END = (1 << 30), +}; /** Nodes collect new transactions into a block, hash them into a hash tree, @@ -866,6 +926,9 @@ class CBlock // network and disk std::vector vtx; + // header + boost::shared_ptr auxpow; + // ppcoin: block signature - signed by one of the coin base txout[N]'s owner std::vector vchBlockSig; @@ -891,8 +954,10 @@ class CBlock READWRITE(nBits); READWRITE(nNonce); + nSerSize += ReadWriteAuxPow(s, auxpow, nType, nVersion, ser_action); + // ConnectBlock depends on vtx following header to generate CDiskTxPos - if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY))) + if(!(nType & (SER_BLOCKHEADERONLY))) { READWRITE(vtx); READWRITE(vchBlockSig); @@ -916,6 +981,7 @@ class CBlock vchBlockSig.clear(); vMerkleTree.clear(); nDoS = 0; + auxpow.reset(); } bool IsNull() const @@ -923,7 +989,37 @@ class CBlock return (nBits == 0); } - uint256 GetHash() const; + int GetChainID() const { + return(nVersion / BLOCK_VERSION_CHAIN_START); + } + + void SetAuxPow(CAuxPow* pow); + + /* Extracts block height from v2+ coin base; + * ignores nVersion because it's unreliable */ + int GetBlockHeight() const { + /* Prevents a crash if called on a block header alone */ + if(vtx.size()) { + /* Serialised CScript */ + std::vector::const_iterator scriptsig = vtx[0].vin[0].scriptSig.begin(); + unsigned char i, scount = scriptsig[0]; + /* Optimise: nTime is 4 bytes always, + * nHeight must be less for a long time; + * check against a threshold when the time comes */ + if(scount < 4) { + int height = 0; + unsigned char *pheight = (unsigned char *) &height; + for(i = 0; i < scount; i++) + pheight[i] = scriptsig[i + 1]; + /* v2+ block with nHeight in coin base */ + return(height); + } + } + /* Not found */ + return(-1); + } + + uint256 GetHash(bool existingBlock=false, int64 coins=totalCoin) const; uint256 GetHashScrypt() const { @@ -984,8 +1080,7 @@ class CBlock return maxTransactionTime; } - uint256 BuildMerkleTree() const - { + uint256 BuildMerkleTree() const { vMerkleTree.clear(); BOOST_FOREACH(const CTransaction& tx, vtx) vMerkleTree.push_back(tx.GetHash()); @@ -995,8 +1090,8 @@ class CBlock for (int i = 0; i < nSize; i += 2) { int i2 = std::min(i+1, nSize-1); - vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), - BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); + vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), + BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); } j += nSize; } @@ -1019,16 +1114,16 @@ class CBlock return vMerkleBranch; } - static uint256 CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) - { + static uint256 CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, + int nIndex) { if (nIndex == -1) return 0; BOOST_FOREACH(const uint256& otherside, vMerkleBranch) { - if (nIndex & 1) - hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); + if(nIndex & 1) + hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); else - hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); + hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); nIndex >>= 1; } return hash; @@ -1080,15 +1175,10 @@ class CBlock return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); } - totalCoinDB = 1; - uint256 hash1 = GetHashScrypt(); - totalCoinDB = coinMax; - uint256 hash2 = GetHashGroestl(); // Check the header - if (fReadTransactions && IsProofOfWork() && !CheckProofOfWork(hash1, nBits) && !CheckProofOfWork(hash2, nBits)) - { - return error("CBlock::ReadFromDisk() : errors in block header"); - } +// if (fReadTransactions && IsProofOfWork() && !CheckProofOfWork(GetHashScrypt(), nBits) && !CheckProofOfWork(GetHashGroestl(), nBits)) +// return error("CBlock::ReadFromDisk() : errors in block header"); + return true; } @@ -1120,11 +1210,12 @@ class CBlock bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true); bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew); bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); - bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; + bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true, int64 totalCoin=0) const; bool AcceptBlock(); bool GetCoinAge(uint64& nCoinAge) const; // ppcoin: calculate total coin age spent in block bool SignBlock(const CKeyStore& keystore); - bool CheckBlockSignature() const; + bool CheckBlockSignature(int nHeight) const; + bool CheckProofOfWork(int64 totalCoin) const; private: bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew); @@ -1157,7 +1248,7 @@ class CBlockIndex int64 nMoneySupply; unsigned int nFlags; // ppcoin: block index flags - enum + enum { BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier @@ -1179,6 +1270,9 @@ class CBlockIndex unsigned int nBits; unsigned int nNonce; + // if this is an aux work block + boost::shared_ptr auxpow; + CBlockIndex() { phashBlock = NULL; @@ -1202,6 +1296,7 @@ class CBlockIndex nTime = 0; nBits = 0; nNonce = 0; + auxpow.reset(); } CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block) @@ -1236,6 +1331,7 @@ class CBlockIndex nTime = block.nTime; nBits = block.nBits; nNonce = block.nNonce; + auxpow = block.auxpow; } CBlock GetBlockHeader() const @@ -1248,6 +1344,7 @@ class CBlockIndex block.nTime = nTime; block.nBits = nBits; block.nNonce = nNonce; + block.auxpow = auxpow; return block; } @@ -1355,11 +1452,11 @@ class CBlockIndex pprev, pnext, nFile, nBlockPos, nHeight, FormatMoney(nMint).c_str(), FormatMoney(nMoneySupply).c_str(), GeneratedStakeModifier() ? "MOD" : "-", GetStakeEntropyBit(), IsProofOfStake()? "PoS" : "PoW", - nStakeModifier, nStakeModifierChecksum, + nStakeModifier, nStakeModifierChecksum, hashProofOfStake.ToString().c_str(), prevoutStake.ToString().c_str(), nStakeTime, hashMerkleRoot.ToString().c_str(), - GetBlockHash().ToString().c_str()); + phashBlock ? GetBlockHash().ToString().c_str() : "*not initialized*"); } void print() const @@ -1373,12 +1470,16 @@ class CBlockIndex /** Used to marshal pointers into hashes for db storage. */ class CDiskBlockIndex : public CBlockIndex { +private: + uint256 blockHash; + public: uint256 hashPrev; uint256 hashNext; CDiskBlockIndex() { + blockHash = 0; hashPrev = 0; hashNext = 0; } @@ -1422,34 +1523,15 @@ class CDiskBlockIndex : public CBlockIndex READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); + READWRITE(blockHash); + ReadWriteAuxPow(s, auxpow, nType, this->nVersion, ser_action); ) uint256 GetBlockHash() const { - CBlock block; - block.nVersion = nVersion; - block.hashPrevBlock = hashPrev; - block.hashMerkleRoot = hashMerkleRoot; - block.nTime = nTime; - block.nBits = nBits; - block.nNonce = nNonce; - return block.GetHash(); - } - - uint256 GetBlockHashScrypt() const - { - CBlock block; - block.nVersion = nVersion; - block.hashPrevBlock = hashPrev; - block.hashMerkleRoot = hashMerkleRoot; - block.nTime = nTime; - block.nBits = nBits; - block.nNonce = nNonce; - return block.GetHashScrypt(); - } + if (fUseFastIndex && (nTime < GetAdjustedTime() - 24 * 60 * 60) && blockHash != 0) + return blockHash; - uint256 GetBlockHashGroest() const - { CBlock block; block.nVersion = nVersion; block.hashPrevBlock = hashPrev; @@ -1457,7 +1539,8 @@ class CDiskBlockIndex : public CBlockIndex block.nTime = nTime; block.nBits = nBits; block.nNonce = nNonce; - return block.GetHashGroestl(); + const_cast(this)->blockHash = block.GetHash(); + return blockHash; } std::string ToString() const diff --git a/src/makefile.bsd b/src/makefile.bsd index 10a6e66..7b5fcbf 100644 --- a/src/makefile.bsd +++ b/src/makefile.bsd @@ -2,8 +2,15 @@ # Distributed under the MIT/X11 software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -USE_UPNP:=0 -USE_IPV6:=1 +USE_UPNP:=- +USE_IPV6:=- +BOOST_INCLUDE_PATH=/usr/local/include +BDB_INCLUDE_PATH=/usr/local/include/db48 +BOOST_LIB_PATH=/usr/local/lib +BDB_LIB_PATH=/usr/local/lib/db48 +STATIC=1 +CC=clang +CXX=clang++ LINK:=$(CXX) @@ -35,7 +42,7 @@ LIBS += \ -l db_cxx$(BDB_LIB_SUFFIX) \ -l ssl \ -l crypto \ - -l execinfo + -l execinfo -l elf ifndef USE_UPNP override USE_UPNP = - @@ -52,7 +59,7 @@ endif LIBS+= \ -Wl,-B$(LMODE2) \ -l z \ - -l dl \ +# -l dl \ -l pthread @@ -94,7 +101,7 @@ DEBUGFLAGS=-g # CXXFLAGS can be specified on the make command line, so we use xCXXFLAGS that only # adds some defaults in front. Unfortunately, CXXFLAGS=... $(CXXFLAGS) does not work. -xCXXFLAGS=-O0 -msse2 -pthread -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \ +xCXXFLAGS=-O2 -msse2 -pthread -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \ $(DEBUGFLAGS) $(DEFS) $(HARDENING) $(CXXFLAGS) # LDFLAGS can be specified on the make command line, so we use xLDFLAGS that only @@ -102,6 +109,8 @@ xCXXFLAGS=-O0 -msse2 -pthread -Wall -Wextra -Wformat -Wformat-security -Wno-unus xLDFLAGS=$(LDHARDENING) $(LDFLAGS) OBJS= \ + obj/hash.o \ + obj/groestl.o \ obj/alert.o \ obj/version.o \ obj/checkpoints.o \ @@ -133,13 +142,15 @@ OBJS= \ obj/pbkdf2.o \ obj/scrypt_mine.o \ obj/scrypt-x86.o \ - obj/scrypt-x86_64.o - + obj/scrypt-x86_64.o \ + obj/auxpow.o \ + obj/reactors.o \ + obj/reactorlist.o all: diamondd test check: test_diamond FORCE - ./test_BottleCaps + ./test_diamond # auto-generated dependencies: -include obj/*.P @@ -163,6 +174,14 @@ obj/%.o: %.cpp -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ rm -f $(@:%.o=%.d) +obj/%.o: %.c + $(CC) -c $(xCXXFLAGS) -fpermissive -MMD -MF $(@:%.o=%.d) -o $@ $< + @cp $(@:%.o=%.d) $(@:%.o=%.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ + rm -f $(@:%.o=%.d) + + diamondd: $(OBJS:obj/%=obj/%) $(LINK) $(xCXXFLAGS) -o $@ $^ $(xLDFLAGS) $(LIBS) diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index 5f65ab2..34c4be4 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -87,7 +87,10 @@ OBJS= \ obj/pbkdf2.o \ obj/scrypt_mine.o \ obj/scrypt-x86.o \ - obj/scrypt-x86_64.o + obj/scrypt-x86_64.o \ + obj/auxpow.o \ + obj/reactors.o \ + obj/reactorlist.o all: diamondd.exe diff --git a/src/makefile.mingw b/src/makefile.mingw index 3ac9843..34f7061 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -6,20 +6,20 @@ USE_UPNP:=0 USE_IPV6:=1 INCLUDEPATHS= \ - -I"C:\boost_1_53_0" \ - -I"C:\db-4.8.30.NC\build_unix" \ - -I"C:\openssl-1.0.1e\include - + -I"C:\deps\boost_1_55_0" \ + -I"C:\deps\db-4.8.30.NC\build_unix" \ + -I"C:\deps\openssl-1.0.1e\include + LIBPATHS= \ - -L"C:\boost_1_53_0\stage/lib" \ - -L"C:\db-4.8.30.NC\build_unix" \ - -L"C:\openssl-1.0.1e" + -L"C:\deps\boost_1_55_0\stage/lib" \ + -L"C:\deps\db-4.8.30.NC\build_unix" \ + -L"C:\deps\openssl-1.0.1e" LIBS= \ - -l boost_system-mgw45-mt-d-1_53 \ - -l boost_filesystem-mgw45-mt-d-1_53 \ - -l boost_program_options-mgw45-mt-d-1_53 \ - -l boost_thread-mgw45-mt-d-1_53 \ + -l boost_system-mgw45-mt-d-1_55 \ + -l boost_filesystem-mgw45-mt-d-1_55 \ + -l boost_program_options-mgw45-mt-d-1_55 \ + -l boost_thread-mgw45-mt-d-1_55 \ -l db_cxx \ -l ssl \ -l crypto @@ -27,7 +27,8 @@ LIBS= \ DEFS=-DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE -D__NO_SYSTEM_INCLUDES DEBUGFLAGS=-g CFLAGS=-mthreads -O2 -msse2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) -LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat +LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -Wl,--large-address-aware -static + TESTDEFS = -DTEST_DATA_DIR=$(abspath test/data) @@ -35,8 +36,8 @@ ifndef USE_UPNP override USE_UPNP = - endif ifneq (${USE_UPNP}, -) - INCLUDEPATHS += -I"C:\miniupnpc-1.6-mgw" - LIBPATHS += -L"C:\miniupnpc-1.6-mgw" + INCLUDEPATHS += -I"C:\deps\miniupnpc-1.6-mgw" + LIBPATHS += -L"C:\deps\miniupnpc-1.6-mgw" LIBS += -l miniupnpc -l iphlpapi DEFS += -DSTATICLIB -DUSE_UPNP=$(USE_UPNP) endif @@ -82,7 +83,10 @@ OBJS= \ obj/pbkdf2.o \ obj/scrypt_mine.o \ obj/scrypt-x86.o \ - obj/scrypt-x86_64.o + obj/scrypt-x86_64.o \ + obj/auxpow.o \ + obj/reactors.o \ + obj/reactorlist.o all: diamondd.exe diff --git a/src/makefile.osx b/src/makefile.osx index 7e66fec..a08de85 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -6,7 +6,7 @@ # Mac OS X makefile for bitcoin # Originally by Laszlo Hanyecz (solar@heliacal.net) -CXX=llvm-g++ +CXX=clang++ DEPSDIR=/opt/local INCLUDEPATHS= \ @@ -59,14 +59,15 @@ DEFS=-DMAC_OSX -DMSG_NOSIGNAL=0 -DBOOST_SPIRIT_THREADSAFE ifdef RELEASE # Compile for maximum compatibility and smallest size. # This requires that dependencies are compiled -# the same way. -CFLAGS = -mmacosx-version-min=10.5 -arch x86_64 -O3 -msse2 +# the same way. Use this if you build on pre-Mavericks system +#xCXXFLAGS = -mmacosx-version-min=10.5 -arch x86_64 -O3 -msse2 +xCXXFLAGS = -O3 -msse2 else -CFLAGS = -g -msse2 +xCXXFLAGS = -g -msse2 endif # ppc doesn't work because we don't support big-endian -CFLAGS += -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \ +xCXXFLAGS += -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \ $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) OBJS= \ @@ -98,10 +99,15 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/pbkdf2.o \ + obj/hash.o \ + obj/groestl.o \ obj/kernel.o \ obj/scrypt_mine.o \ obj/scrypt-x86.o \ - obj/scrypt-x86_64.o + obj/scrypt-x86_64.o \ + obj/auxpow.o \ + obj/reactors.o \ + obj/reactorlist.o ifndef USE_UPNP override USE_UPNP = - @@ -134,12 +140,20 @@ version.cpp: obj/build.h DEFS += -DHAVE_BUILD_INFO obj/%.o: %.cpp - $(CXX) -c $(CFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< + $(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< @cp $(@:%.o=%.d) $(@:%.o=%.P); \ sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ rm -f $(@:%.o=%.d) +obj/%.o: %.c + $(CC) -c $(xCXXFLAGS) -fpermissive -MMD -MF $(@:%.o=%.d) -o $@ $< + @cp $(@:%.o=%.d) $(@:%.o=%.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ + rm -f $(@:%.o=%.d) + + obj/scrypt-x86.o: scrypt-x86.S $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< @@ -147,19 +161,19 @@ obj/scrypt-x86_64.o: scrypt-x86_64.S $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< diamondd: $(OBJS:obj/%=obj/%) - $(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + $(CXX) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) obj-test/%.o: test/%.cpp - $(CXX) -c $(TESTDEFS) $(CFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< + $(CXX) -c $(TESTDEFS) $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< @cp $(@:%.o=%.d) $(@:%.o=%.P); \ sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ rm -f $(@:%.o=%.d) test_diamond: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) - $(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) $(TESTLIBS) + $(CXX) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) $(TESTLIBS) clean: -rm -f diamondd test_diamond diff --git a/src/makefile.unix b/src/makefile.unix index 6d878e0..dd2369a 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -94,9 +94,19 @@ DEBUGFLAGS=-g # CXXFLAGS can be specified on the make command line, so we use xCXXFLAGS that only # adds some defaults in front. Unfortunately, CXXFLAGS=... $(CXXFLAGS) does not work. -xCXXFLAGS=-O2 -msse2 -pthread -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \ +xCXXFLAGS=-O2 -pthread -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \ $(DEBUGFLAGS) $(DEFS) $(HARDENING) $(CXXFLAGS) +# If we have an ARM device, we can't use SSE2 instructions, so don't try to use them +# xCPUARCH is passed in as a define (xCPUARCH=armv7l, xCPUARCH=armv6l) +ifeq ($(xCPUARCH),armv7l) + xCXXFLAGS+= -DNOSSE +else ifeq ($(xCPUARCH),armv6l) + xCXXFLAGS+= -DNOSSE +else + xCXXFLAGS+=-msse2 +endif + # LDFLAGS can be specified on the make command line, so we use xLDFLAGS that only # adds some defaults in front. Unfortunately, LDFLAGS=... $(LDFLAGS) does not work. xLDFLAGS=$(LDHARDENING) $(LDFLAGS) @@ -135,8 +145,11 @@ OBJS= \ obj/pbkdf2.o \ obj/scrypt_mine.o \ obj/scrypt-x86.o \ - obj/scrypt-x86_64.o - + obj/scrypt-x86_64.o \ + obj/scrypt-arm.o \ + obj/auxpow.o \ + obj/reactors.o \ + obj/reactorlist.o all: diamondd @@ -158,6 +171,9 @@ obj/scrypt-x86.o: scrypt-x86.S obj/scrypt-x86_64.o: scrypt-x86_64.S $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< +obj/scrypt-arm.o: scrypt-arm.S + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + obj/%.o: %.cpp $(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< @cp $(@:%.o=%.d) $(@:%.o=%.P); \ diff --git a/src/net.cpp b/src/net.cpp index 03ec7ef..9e263af 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -25,7 +25,7 @@ using namespace std; using namespace boost; -static const int MAX_OUTBOUND_CONNECTIONS = 12; +static const int MAX_OUTBOUND_CONNECTIONS = 16; void ThreadMessageHandler2(void* parg); void ThreadSocketHandler2(void* parg); @@ -57,7 +57,7 @@ static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; CAddress addrSeenByPeer(CService("0.0.0.0", 0), nLocalServices); uint64 nLocalHostNonce = 0; -array vnThreadsRunning; +boost::array vnThreadsRunning; static std::vector vhListenSocket; CAddrMan addrman; @@ -174,14 +174,16 @@ bool RecvLine(SOCKET hSocket, string& strLine) if (nBytes == 0) { // socket closed - printf("socket closed\n"); + if (fDebugNet) + printf("socket closed\n"); return false; } else { // socket error int nErr = WSAGetLastError(); - printf("recv failed: %d\n", nErr); + if (fDebugNet) + printf("recv failed: %d\n", nErr); return false; } } @@ -485,9 +487,10 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, int64 nTimeout) /// debug print - printf("trying connection %s lastseen=%.1fhrs\n", - pszDest ? pszDest : addrConnect.ToString().c_str(), - pszDest ? 0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); + if (fDebugNet) + printf("trying connection %s lastseen=%.1fhrs\n", + pszDest ? pszDest : addrConnect.ToString().c_str(), + pszDest ? 0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); // Connect SOCKET hSocket; @@ -496,7 +499,8 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, int64 nTimeout) addrman.Attempt(addrConnect); /// debug print - printf("connected %s\n", pszDest ? pszDest : addrConnect.ToString().c_str()); + if (fDebugNet) + printf("connected %s\n", pszDest ? pszDest : addrConnect.ToString().c_str()); // Set to non-blocking #ifdef WIN32 @@ -534,7 +538,8 @@ void CNode::CloseSocketDisconnect() fDisconnect = true; if (hSocket != INVALID_SOCKET) { - printf("disconnecting node %s\n", addrName.c_str()); + if (fDebugNet) + printf("disconnecting node %s\n", addrName.c_str()); closesocket(hSocket); hSocket = INVALID_SOCKET; vRecv.clear(); @@ -558,10 +563,6 @@ void CNode::PushVersion() nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector()), nBestHeight); } - - - - std::map CNode::setBanned; CCriticalSection CNode::cs_setBanned; @@ -570,22 +571,39 @@ void CNode::ClearBanned() setBanned.clear(); } -bool CNode::IsBanned(CNetAddr ip) +// return how many seconds left, if banned +int64 CNode::IsBanned(CNetAddr ip) { - bool fResult = false; { LOCK(cs_setBanned); std::map::iterator i = setBanned.find(ip); if (i != setBanned.end()) { int64 t = (*i).second; - if (GetTime() < t) - fResult = true; + int64 left = t - GetTime(); + if (left < 0) + left=0; + return left; } } - return fResult; + return 0; } +bool CNode::ConnectAllowed(CNetAddr ip) +{ + const string strAddress = ip.ToString(); + const vector& vAllow = mapMultiArgs["-connectallowip"]; + // no ACL specified, everyone plays + if (vAllow.empty()) + return true; + BOOST_FOREACH(string strAllow, vAllow) + if (WildcardMatch(strAddress, strAllow)) + return true; + return false; +} + +extern CMedianFilter cPeerBlockCounts; + bool CNode::Misbehaving(int howmuch) { if (addr.IsLocal()) @@ -605,6 +623,9 @@ bool CNode::Misbehaving(int howmuch) setBanned[addr] = banTime; } CloseSocketDisconnect(); + + cPeerBlockCounts.removeLast(nStartingHeight); // remove this node's reported number of blocks + return true; } else printf("Misbehaving: %s (%d -> %d)\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); @@ -787,7 +808,8 @@ void ThreadSocketHandler2(void* parg) if (have_fds) { int nErr = WSAGetLastError(); - printf("socket select error %d\n", nErr); + if (fDebugNet) + printf("socket select error %d\n", nErr); for (unsigned int i = 0; i <= hSocketMax; i++) FD_SET(i, &fdsetRecv); } @@ -812,6 +834,7 @@ void ThreadSocketHandler2(void* parg) SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); CAddress addr; int nInbound = 0; + int bsec = 0; // how many seconds left in a ban if (hSocket != INVALID_SOCKET) if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) @@ -838,9 +861,14 @@ void ThreadSocketHandler2(void* parg) closesocket(hSocket); } } - else if (CNode::IsBanned(addr)) + else if (!CNode::ConnectAllowed(addr)) + { + printf("connection from %s dropped (forbidden)\n", addr.ToString().c_str()); + closesocket(hSocket); + } + else if ((bsec = CNode::IsBanned(addr))) { - printf("connection from %s dropped (banned)\n", addr.ToString().c_str()); + printf("connection from %s dropped (banned %d sec)\n", addr.ToString().c_str(), bsec); closesocket(hSocket); } else @@ -1031,10 +1059,14 @@ void ThreadMapPort2(void* parg) #ifndef UPNPDISCOVER_SUCCESS /* miniupnpc 1.5 */ devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); -#else +#elif MINIUPNPC_API_VERSION < 14 /* miniupnpc 1.6 */ int error = 0; devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); +#else + /* miniupnpc 1.9 */ + int error = 0; + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error); #endif struct UPNPUrls urls; @@ -1149,6 +1181,8 @@ void MapPort() // The first name is used as information source for addrman. // The second name should resolve to a list of seed addresses. static const char *strDNSSeed[][2] = { + {"seed.bit.diamonds", "seed.bit.diamonds"}, + {"dmdseed.danbo.bg", "dmdseed.danbo.bg"}, {"", ""}, }; @@ -1195,6 +1229,7 @@ void ThreadDNSAddressSeed2(void* parg) int nOneDay = 24*3600; CAddress addr = CAddress(CService(ip, GetDefaultPort())); addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old + SetReachable(addr.GetNetwork()); vAdd.push_back(addr); found++; } @@ -1220,6 +1255,7 @@ void ThreadDNSAddressSeed2(void* parg) unsigned int pnSeed[] = { + 0x131544c1 }; void DumpAddresses() @@ -1380,6 +1416,7 @@ void ThreadOpenConnections2(void* parg) memcpy(&ip, &pnSeed[i], sizeof(ip)); CAddress addr(CService(ip, GetDefaultPort())); addr.nTime = GetTime()-GetRand(nOneWeek)-nOneWeek; + SetReachable(addr.GetNetwork()); vAdd.push_back(addr); } addrman.Add(vAdd, CNetAddr("127.0.0.1")); @@ -1845,18 +1882,12 @@ void StartNode(void* parg) // Start threads // - if (!GetBoolArg("-dnsseed", true)) printf("DNS seeding disabled\n"); else if (!NewThread(ThreadDNSAddressSeed, NULL)) printf("Error: NewThread(ThreadDNSAddressSeed) failed\n"); - if (!GetBoolArg("-dnsseed", false)) - printf("DNS seeding disabled\n"); - if (GetBoolArg("-dnsseed", false)) - printf("DNS seeding NYI\n"); - // Map ports with UPnP if (fUseUPnP) MapPort(); diff --git a/src/net.h b/src/net.h index 16cd95f..e8cdc7c 100644 --- a/src/net.h +++ b/src/net.h @@ -26,8 +26,8 @@ extern int nBestHeight; -inline unsigned int ReceiveBufferSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } -inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } +inline unsigned int ReceiveBufferSize() { return 1000*GetArg("-maxreceivebuffer", 10*1000); } +inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 10*1000); } void AddOneShot(std::string strDest); bool RecvLine(SOCKET hSocket, std::string& strLine); @@ -637,7 +637,8 @@ class CNode // between nodes running old code and nodes running // new code. static void ClearBanned(); // needed for unit testing - static bool IsBanned(CNetAddr ip); + static int64 IsBanned(CNetAddr ip); + static bool ConnectAllowed(CNetAddr ip); bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot void copyStats(CNodeStats &stats); }; diff --git a/src/netbase.cpp b/src/netbase.cpp index 95d6493..06ab06d 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -368,13 +368,15 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout); if (nRet == 0) { - printf("connection timeout\n"); + if (fDebugNet) + printf("connection timeout\n"); closesocket(hSocket); return false; } if (nRet == SOCKET_ERROR) { - printf("select() for connection failed: %i\n",WSAGetLastError()); + if (fDebugNet) + printf("select() for connection failed: %i\n",WSAGetLastError()); closesocket(hSocket); return false; } @@ -385,13 +387,15 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR) #endif { - printf("getsockopt() for connection failed: %i\n",WSAGetLastError()); + if (fDebugNet) + printf("getsockopt() for connection failed: %i\n",WSAGetLastError()); closesocket(hSocket); return false; } if (nRet != 0) { - printf("connect() failed after select(): %s\n",strerror(nRet)); + if (fDebugNet) + printf("connect() failed after select(): %s\n",strerror(nRet)); closesocket(hSocket); return false; } @@ -402,7 +406,8 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe else #endif { - printf("connect() failed: %i\n",WSAGetLastError()); + if (fDebugNet) + printf("connect() failed: %i\n",WSAGetLastError()); closesocket(hSocket); return false; } diff --git a/src/obj-test/.gitignore b/src/obj-test/.gitignore index d6b7ef3..dd3727e 100644 --- a/src/obj-test/.gitignore +++ b/src/obj-test/.gitignore @@ -1,2 +1,3 @@ +#Ignore all files except the .gitignore * !.gitignore diff --git a/src/obj/.gitignore b/src/obj/.gitignore new file mode 100644 index 0000000..dd3727e --- /dev/null +++ b/src/obj/.gitignore @@ -0,0 +1,3 @@ +#Ignore all files except the .gitignore +* +!.gitignore diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index ce96743..f4ab713 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -121,14 +121,26 @@ void AddressBookPage::setModel(AddressTableModel *model) // Receive filter proxyModel->setFilterRole(AddressTableModel::TypeRole); proxyModel->setFilterFixedString(AddressTableModel::Receive); + + // Set this slightly earlier so that we can adjust our columns by tab. + ui->tableView->setModel(proxyModel); + // Only display the scrape address for receiving tab (cheap hack). + ui->tableView->horizontalHeader()->resizeSection( + AddressTableModel::ScrapeAddress, 320); break; case SendingTab: // Send filter proxyModel->setFilterRole(AddressTableModel::TypeRole); proxyModel->setFilterFixedString(AddressTableModel::Send); + + // Set this slightly earlier so that we can adjust our columns by tab. + ui->tableView->setModel(proxyModel); + // Do not display the scrape address for the send tab (cheap hack). + ui->tableView->horizontalHeader()->resizeSection( + AddressTableModel::ScrapeAddress, 0); + break; } - ui->tableView->setModel(proxyModel); ui->tableView->sortByColumn(0, Qt::AscendingOrder); // Set column widths diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index e65d391..64de9fb 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -21,8 +21,11 @@ struct AddressTableEntry Type type; QString label; QString address; + QString scrape_address; AddressTableEntry() {} + AddressTableEntry(Type type, const QString &label, const QString &address, const QString &scrape_address): + type(type), label(label), address(address), scrape_address(scrape_address) {} AddressTableEntry(Type type, const QString &label, const QString &address): type(type), label(label), address(address) {} }; @@ -64,9 +67,21 @@ class AddressTablePriv const CBitcoinAddress& address = item.first; const std::string& strName = item.second; bool fMine = IsMine(*wallet, address.Get()); - cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending, - QString::fromStdString(strName), - QString::fromStdString(address.ToString()))); + + if (fMine) { + std::string addr; + if (wallet->HasScrapeAddress(address.ToString())) + wallet->ReadScrapeAddress(address.ToString(), addr); + + cachedAddressTable.append(AddressTableEntry(AddressTableEntry::Receiving, + QString::fromStdString(strName), + QString::fromStdString(address.ToString()), + QString::fromStdString(addr))); + } else { + cachedAddressTable.append(AddressTableEntry(AddressTableEntry::Sending, + QString::fromStdString(strName), + QString::fromStdString(address.ToString()))); + } } } } @@ -81,6 +96,7 @@ class AddressTablePriv int lowerIndex = (lower - cachedAddressTable.begin()); int upperIndex = (upper - cachedAddressTable.begin()); bool inModel = (lower != upper); + AddressTableEntry::Type newEntryType = isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending; switch(status) @@ -139,7 +155,7 @@ class AddressTablePriv AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) : QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0) { - columns << tr("Label") << tr("Address"); + columns << tr("Label") << tr("Address") << tr("Scrape Address"); priv = new AddressTablePriv(wallet, this); priv->refreshAddressTable(); } @@ -183,6 +199,17 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const } case Address: return rec->address; + + case ScrapeAddress: + // No scrape address for sending tab. + if (rec->type == AddressTableEntry::Sending) + break; + + if (rec->scrape_address.isEmpty() && role == Qt::DisplayRole) { + return tr("(no scrape address)"); + } else { + return rec->scrape_address; + } } } else if (role == Qt::FontRole) @@ -243,6 +270,35 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu } } break; + case ScrapeAddress: + // No scrape address for sending tab. + if (rec->type == AddressTableEntry::Sending) + break; + + /* If passing an empty string delete the current scrape address if + * one exists. */ + if (value.toString().toStdString().empty()) { + if (wallet->HasScrapeAddress(rec->address.toStdString())) + if (!wallet->EraseScrapeAddress(rec->address.toStdString())) + return false; + } else { + // Confirm the scrape address is a valid address before setting it + if (!walletModel->validateAddress(value.toString())) { + editStatus = INVALID_ADDRESS; + return false; + } + + /* Confirm we are not setting the scrape address to the same + * address as the staking address. */ + if (rec->address == value.toString()) + return false; + + if (!wallet->WriteScrapeAddress(rec->address.toStdString(), value.toString().toStdString())) + return false; + } + + rec->scrape_address = value.toString(); + break; } return true; @@ -270,9 +326,10 @@ Qt::ItemFlags AddressTableModel::flags(const QModelIndex & index) const Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled; // Can edit address and label for sending addresses, - // and only label for receiving addresses. + // label and scrapeadddress for receiving addresses. if(rec->type == AddressTableEntry::Sending || - (rec->type == AddressTableEntry::Receiving && index.column()==Label)) + (rec->type == AddressTableEntry::Receiving && index.column()==Label) || + (rec->type == AddressTableEntry::Receiving && index.column()==ScrapeAddress)) { retval |= Qt::ItemIsEditable; } diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 42974e3..0803ffe 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -20,7 +20,8 @@ class AddressTableModel : public QAbstractTableModel enum ColumnIndex { Label = 0, /**< User specified label */ - Address = 1 /**< Bitcoin address */ + Address = 1, /**< Bitcoin address */ + ScrapeAddress = 2 /**< Diamond scrape address (receiving tab only) */ }; enum RoleIndex { diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index 5ad1e78..def9959 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -4,6 +4,8 @@ #include "guiconstants.h" #include "walletmodel.h" +#include "util.h" + #include #include #include @@ -33,6 +35,10 @@ AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.
Please use a passphrase of 10 or more random characters, or eight or more words.")); setWindowTitle(tr("Encrypt wallet")); break; + case(UnlockStaking): + ui->mintingCheckBox->setChecked(true); + ui->mintingCheckBox->show(); + // fallthru case Unlock: // Ask passphrase ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet.")); ui->passLabel2->hide(); @@ -138,6 +144,7 @@ void AskPassphraseDialog::accept() QDialog::reject(); // Cancelled } } break; + case(UnlockStaking): case Unlock: if(!model->setWalletLocked(false, oldpass)) { @@ -146,6 +153,7 @@ void AskPassphraseDialog::accept() } else { + fStakingOnly = ui->mintingCheckBox->isChecked(); QDialog::accept(); // Success } break; @@ -193,6 +201,7 @@ void AskPassphraseDialog::textChanged() case Encrypt: // New passphrase x2 acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); break; + case(UnlockStaking): case Unlock: // Old passphrase x1 case Decrypt: acceptable = !ui->passEdit1->text().isEmpty(); diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h index b500ff4..0ddb24f 100644 --- a/src/qt/askpassphrasedialog.h +++ b/src/qt/askpassphrasedialog.h @@ -19,6 +19,7 @@ class AskPassphraseDialog : public QDialog enum Mode { Encrypt, /**< Ask passphrase twice and encrypt */ Unlock, /**< Ask passphrase and unlock */ + UnlockStaking, /* Ask passphrase and unlock for staking only */ ChangePass, /**< Ask old passphrase + new passphrase twice */ Decrypt /**< Ask passphrase and decrypt wallet */ }; diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index d9e11c5..c7c2dcd 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -22,7 +22,7 @@ res/icons/editpaste.png res/icons/editcopy.png res/icons/add.png - res/icons/Diamond-128.png + res/icons/Diamond-128.png res/icons/Diamond-16.png res/icons/edit.png res/icons/history.png @@ -31,11 +31,13 @@ res/icons/synced.png res/icons/remove.png res/icons/tx_mined.png + res/icons/tx_minted.png res/icons/tx_input.png res/icons/tx_output.png res/icons/tx_inout.png res/icons/lock_closed.png res/icons/lock_open.png + res/icons/minting.png res/icons/key.png res/icons/filesave.png res/icons/qrcode.png @@ -45,7 +47,7 @@ res/images/about.png res/images/diamond-splash.jpg res/images/background.png - res/images/wallet.png + res/images/diamond.png res/movies/update_spinner.mng diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 2a78694..2c5651b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -25,6 +25,8 @@ #include "notificator.h" #include "guiutil.h" #include "rpcconsole.h" +#include "wallet.h" +#include "version.h" #ifdef Q_OS_MAC #include "macdockiconhandler.h" @@ -63,20 +65,25 @@ #include +extern CWallet *pwalletMain; +extern int64 nLastCoinStakeSearchInterval; +extern unsigned int nStakeTargetSpacing; + BitcoinGUI::BitcoinGUI(QWidget *parent): QMainWindow(parent), clientModel(0), walletModel(0), encryptWalletAction(0), changePassphraseAction(0), + lockWalletToggleAction(0), aboutQtAction(0), trayIcon(0), notificator(0), rpcConsole(0) { resize(850, 550); - setWindowTitle(tr("Diamond") + " - " + tr("Wallet")); - setStyleSheet("QMainWindow{border-image: url(:/images/main_backg)} QToolButton:!hover {color:white;} QToolButton:hover {background-color:rgb(59, 88, 104);color:white;} QToolTip {background-color:#1f333e; color:white;}"); + setWindowTitle(tr("Diamond") + " - " + tr("Wallet") + " " + QString::fromStdString(CLIENT_BUILD)); + setStyleSheet("QMainWindow{border-image: url(:/images/main_backg)} QToolButton:!hover {color:white; border-style: none;} QToolButton:hover {background-color:rgb(59, 88, 104);color:white; border-style: none;} QToolTip {background-color:#1f333e; color:white; border-style: none;}"); #ifndef Q_OS_MAC qApp->setWindowIcon(QIcon(":icons/bitcoin")); @@ -131,22 +138,38 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Status bar notification icons QFrame *frameBlocks = new QFrame(); frameBlocks->setContentsMargins(0,0,0,0); - frameBlocks->setMinimumWidth(56); - frameBlocks->setMaximumWidth(56); + frameBlocks->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); frameBlocksLayout->setContentsMargins(3,0,3,0); frameBlocksLayout->setSpacing(3); labelEncryptionIcon = new QLabel(); + labelMintingIcon = new QLabel(); labelConnectionsIcon = new QLabel(); labelBlocksIcon = new QLabel(); frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelEncryptionIcon); frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelMintingIcon); + frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelConnectionsIcon); frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelBlocksIcon); frameBlocksLayout->addStretch(); + // Set minting pixmap + labelMintingIcon->setPixmap(QIcon(":/icons/minting").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelMintingIcon->setEnabled(false); + // Add timer to update minting icon + QTimer *timerMintingIcon = new QTimer(labelMintingIcon); + timerMintingIcon->start(MODEL_UPDATE_DELAY); + connect(timerMintingIcon, SIGNAL(timeout()), this, SLOT(updateMintingIcon())); + // Add timer to update minting weights + QTimer *timerMintingWeights = new QTimer(labelMintingIcon); + timerMintingWeights->start(30 * 1000); + connect(timerMintingWeights, SIGNAL(timeout()), this, SLOT(updateMintingWeights())); + // Set initial values for user and network weights + nWeight, nNetworkWeight = 0; + // Progress bar and label for blocks download progressBarLabel = new QLabel(); progressBarLabel->setVisible(false); @@ -168,7 +191,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): statusBar()->addPermanentWidget(frameBlocks); syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this); - // this->setStyleSheet("background-color: #effbef;"); + // this->setStyleSheet("background-color: #effbef;"); // Clicking on a transaction on the overview page simply sends you to transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage())); @@ -263,6 +286,7 @@ void BitcoinGUI::createActions() backupWalletAction->setToolTip(tr("Backup wallet to another location")); changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase..."), this); changePassphraseAction->setToolTip(tr("Change the passphrase used for wallet encryption")); + lockWalletToggleAction = new QAction(this); signMessageAction = new QAction(QIcon(":/icons/edit"), tr("Sign &message..."), this); verifyMessageAction = new QAction(QIcon(":/icons/transaction_0"), tr("&Verify message..."), this); @@ -279,6 +303,7 @@ void BitcoinGUI::createActions() connect(encryptWalletAction, SIGNAL(triggered(bool)), this, SLOT(encryptWallet(bool))); connect(backupWalletAction, SIGNAL(triggered()), this, SLOT(backupWallet())); connect(changePassphraseAction, SIGNAL(triggered()), this, SLOT(changePassphrase())); + connect(lockWalletToggleAction, SIGNAL(triggered()), this, SLOT(lockWalletToggle())); connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab())); connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab())); } @@ -305,6 +330,7 @@ void BitcoinGUI::createMenuBar() QMenu *settings = appMenuBar->addMenu(tr("&Settings")); settings->addAction(encryptWalletAction); settings->addAction(changePassphraseAction); + settings->addAction(lockWalletToggleAction); settings->addSeparator(); settings->addAction(optionsAction); @@ -314,7 +340,7 @@ void BitcoinGUI::createMenuBar() help->addAction(aboutAction); help->addAction(aboutQtAction); - // QString ss("QMenuBar::item { background-color: #effbef; color: black }"); + // QString ss("QMenuBar::item { background-color: #effbef; color: black }"); // appMenuBar->setStyleSheet(ss); } @@ -330,6 +356,7 @@ void BitcoinGUI::createToolBars() QToolBar *toolbar2 = addToolBar(tr("Actions toolbar")); toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolbar2->addAction(lockWalletToggleAction); toolbar2->addAction(exportAction); } @@ -343,10 +370,10 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) { setWindowTitle(windowTitle() + QString(" ") + tr("[testnet]")); #ifndef Q_OS_MAC - qApp->setWindowIcon(QIcon(":icons/bitcoin_testnet")); - setWindowIcon(QIcon(":icons/bitcoin_testnet")); + qApp->setWindowIcon(QIcon(":icons/Diamond_testnet")); + setWindowIcon(QIcon(":icons/Diamond_testnet")); #else - MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); + MacDockIconHandler::instance()->setIcon(QIcon(":icons/Diamond_testnet")); #endif if(trayIcon) { @@ -396,7 +423,7 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) // Balloon pop-up for new transaction connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(incomingTransaction(QModelIndex,int,int))); + this, SLOT(incomingTransaction(QModelIndex,int,int))); // Ask for passphrase if needed connect(walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet())); @@ -413,13 +440,13 @@ void BitcoinGUI::createTrayIcon() trayIcon->setToolTip(tr("Diamond client")); trayIcon->setIcon(QIcon(":/icons/toolbar")); connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), - this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); trayIcon->show(); #else // Note: On Mac, the dock icon is used to provide the tray's functionality. MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance(); trayIconMenu = dockIconHandler->dockMenu(); - dockIconHandler->setMainWindow((QMainWindow*)this); + dockIconHandler->setMainWindow((QMainWindow*)this); #endif // Configuration of the tray icon (or dock icon) icon menu @@ -531,7 +558,7 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) progressBar->setVisible(false); } - tooltip = tr("Current difficulty is %1.").arg(clientModel->GetDifficulty()) + QString("
") + tooltip; + tooltip = tr("Current difficulty is %1.").arg(clientModel->GetDifficulty()) + QString("
") + tooltip; QDateTime lastBlockDate = clientModel->getLastBlockDate(); int secs = lastBlockDate.secsTo(QDateTime::currentDateTime()); @@ -639,12 +666,12 @@ void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) { QString strMessage = tr("This transaction is over the size limit. You can still send it for a fee of %1, " - "which goes to the nodes that process your transaction and helps to support the network. " - "Do you want to pay the fee?").arg( - BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired)); + "which goes to the nodes that process your transaction and helps to support the network. " + "Do you want to pay the fee?").arg( + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired)); QMessageBox::StandardButton retval = QMessageBox::question( - this, tr("Confirm transaction fee"), strMessage, - QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); + this, tr("Confirm transaction fee"), strMessage, + QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); *payFee = (retval == QMessageBox::Yes); } @@ -654,32 +681,32 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int return; TransactionTableModel *ttm = walletModel->getTransactionTableModel(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent) - .data(Qt::EditRole).toULongLong(); + .data(Qt::EditRole).toULongLong(); if(!clientModel->inInitialBlockDownload()) { // On new transaction, make an info balloon // Unless the initial block download is in progress, to prevent balloon-spam QString date = ttm->index(start, TransactionTableModel::Date, parent) - .data().toString(); + .data().toString(); QString type = ttm->index(start, TransactionTableModel::Type, parent) - .data().toString(); + .data().toString(); QString address = ttm->index(start, TransactionTableModel::ToAddress, parent) - .data().toString(); + .data().toString(); QIcon icon = qvariant_cast(ttm->index(start, - TransactionTableModel::ToAddress, parent) - .data(Qt::DecorationRole)); + TransactionTableModel::ToAddress, parent) + .data(Qt::DecorationRole)); notificator->notify(Notificator::Information, - (amount)<0 ? tr("Sent transaction") : - tr("Incoming transaction"), - tr("Date: %1\n" - "Amount: %2\n" - "Type: %3\n" - "Address: %4\n") - .arg(date) - .arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true)) - .arg(type) - .arg(address), icon); + (amount)<0 ? tr("Sent transaction") : + tr("Incoming transaction"), + tr("Date: %1\n" + "Amount: %2\n" + "Type: %3\n" + "Address: %4\n") + .arg(date) + .arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true)) + .arg(type) + .arg(address), icon); } } @@ -797,24 +824,33 @@ void BitcoinGUI::setEncryptionStatus(int status) case WalletModel::Unencrypted: labelEncryptionIcon->hide(); encryptWalletAction->setChecked(false); - changePassphraseAction->setEnabled(false); encryptWalletAction->setEnabled(true); + changePassphraseAction->setEnabled(false); + lockWalletToggleAction->setVisible(false); break; case WalletModel::Unlocked: labelEncryptionIcon->show(); labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); - labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently unlocked")); + labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently unlocked.")); encryptWalletAction->setChecked(true); - changePassphraseAction->setEnabled(true); encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported + changePassphraseAction->setEnabled(true); + lockWalletToggleAction->setVisible(true); + lockWalletToggleAction->setIcon(QIcon(":/icons/lock_closed")); + lockWalletToggleAction->setText(tr("&Lock Wallet")); + lockWalletToggleAction->setToolTip(tr("Lock wallet")); break; case WalletModel::Locked: labelEncryptionIcon->show(); labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); - labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently locked")); + labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently locked.")); encryptWalletAction->setChecked(true); - changePassphraseAction->setEnabled(true); encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported + changePassphraseAction->setEnabled(true); + lockWalletToggleAction->setVisible(true); + lockWalletToggleAction->setIcon(QIcon(":/icons/lock_open")); + lockWalletToggleAction->setText(tr("&Unlock Wallet...")); + lockWalletToggleAction->setToolTip(tr("Unlock wallet")); break; } } @@ -824,7 +860,7 @@ void BitcoinGUI::encryptWallet(bool status) if(!walletModel) return; AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt: - AskPassphraseDialog::Decrypt, this); + AskPassphraseDialog::Decrypt, this); dlg.setModel(walletModel); dlg.exec(); @@ -857,12 +893,28 @@ void BitcoinGUI::unlockWallet() { if(!walletModel) return; + // Unlock wallet when requested by wallet model if(walletModel->getEncryptionStatus() == WalletModel::Locked) { - AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this); + AskPassphraseDialog::Mode mode = AskPassphraseDialog::Unlock; + AskPassphraseDialog dlg(mode, this); + dlg.setModel(walletModel); + dlg.exec(); + } +} + +void BitcoinGUI::lockWalletToggle() +{ + if(!walletModel) + return; + + if(walletModel->getEncryptionStatus() == WalletModel::Locked) { + AskPassphraseDialog dlg(AskPassphraseDialog::UnlockStaking, this); dlg.setModel(walletModel); dlg.exec(); + } else { + walletModel->setWalletLocked(true); } } @@ -892,3 +944,51 @@ void BitcoinGUI::toggleHidden() { showNormalIfMinimized(true); } + +void BitcoinGUI::updateMintingIcon() +{ + if (pwalletMain && pwalletMain->IsLocked()) + { + labelMintingIcon->setToolTip(tr("Not minting because wallet is locked.")); + labelMintingIcon->setEnabled(false); + } + else if (vNodes.empty()) + { + labelMintingIcon->setToolTip(tr("Not minting because wallet is offline.")); + labelMintingIcon->setEnabled(false); + } + else if (IsInitialBlockDownload()) + { + labelMintingIcon->setToolTip(tr("Not minting because wallet is syncing.")); + labelMintingIcon->setEnabled(false); + } + else if (!nWeight) + { + labelMintingIcon->setToolTip(tr("Not minting because you don't have mature coins.")); + labelMintingIcon->setEnabled(false); + } + else if (nLastCoinStakeSearchInterval) + { + labelMintingIcon->setEnabled(true); + labelMintingIcon->setToolTip(tr("Minting.
Your weight is %1.
Network weight is %2.").arg(nWeight).arg(nNetworkWeight)); + } + else + { + labelMintingIcon->setToolTip(tr("Not minting.")); + labelMintingIcon->setEnabled(false); + } +} + +void BitcoinGUI::updateMintingWeights() +{ + // Only update if we have the network's current number of blocks, or weight(s) are zero (fixes lagging GUI) + if ((clientModel && clientModel->getNumBlocks() == clientModel->getNumBlocksOfPeers()) || !nWeight || !nNetworkWeight) + { + nWeight = 0; + + if (pwalletMain) + pwalletMain->GetStakeWeight(*pwalletMain, nMinMax, nMinMax, nWeight); + + nNetworkWeight = GetPoSKernelPS(); + } +} diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index c67e887..c6306f5 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -67,6 +67,7 @@ class BitcoinGUI : public QMainWindow SignVerifyMessageDialog *signVerifyMessageDialog; QLabel *labelEncryptionIcon; + QLabel *labelMintingIcon; QLabel *labelConnectionsIcon; QLabel *labelBlocksIcon; QLabel *progressBarLabel; @@ -88,6 +89,7 @@ class BitcoinGUI : public QMainWindow QAction *encryptWalletAction; QAction *backupWalletAction; QAction *changePassphraseAction; + QAction *lockWalletToggleAction; QAction *aboutQtAction; QAction *openRPCConsoleAction; @@ -98,6 +100,10 @@ class BitcoinGUI : public QMainWindow QMovie *syncIconMovie; + unsigned long long nMinMax; + unsigned long long nWeight; + unsigned long long nNetworkWeight; + /** Create the main UI actions. */ void createActions(); /** Create the menu bar and sub-menus. */ @@ -169,11 +175,18 @@ private slots: void changePassphrase(); /** Ask for passphrase to unlock wallet temporarily */ void unlockWallet(); + /** Toggle unlocking wallet */ + void lockWalletToggle(); /** Show window if hidden, unminimize when minimized, rise when obscured or show if hidden and fToggleHidden is true */ void showNormalIfMinimized(bool fToggleHidden = false); /** simply calls showNormalIfMinimized(true) for use in SLOT() macro */ void toggleHidden(); + + /** Update info about minting */ + void updateMintingIcon(); + /** Update minting weight info */ + void updateMintingWeights(); }; #endif diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 469e1ee..c386c83 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -10,7 +10,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "%s, you must set a rpcpassword in the configuration file:\n" " %s\n" "It is recommended you use the following random password:\n" -"rpcuser=bitcoinrpc\n" +"rpcuser=diamondrpc\n" "rpcpassword=%s\n" "(you do not need to remember this password)\n" "If the file does not exist, create it with owner-readable-only file " @@ -44,7 +44,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Execute command when the best block changes (%s in cmd is replaced by block " "hash)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Listen for JSON-RPC connections on (default: 8344 or testnet: 18344)"), +"Listen for JSON-RPC connections on (default: 17772 or testnet: 27772)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Number of seconds to keep misbehaving peers from reconnecting (default: " "86400)"), @@ -116,7 +116,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=: '%s'") QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -reservebalance="), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount"), QT_TRANSLATE_NOOP("bitcoin-core", "List commands"), -QT_TRANSLATE_NOOP("bitcoin-core", "Listen for connections on (default: 27772 or testnet: 17772)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Listen for connections on (default: 17771 or testnet: 27771)"), QT_TRANSLATE_NOOP("bitcoin-core", "Loading addresses..."), QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."), QT_TRANSLATE_NOOP("bitcoin-core", "Loading wallet..."), @@ -174,4 +174,4 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart Diamon QT_TRANSLATE_NOOP("bitcoin-core", "Warning: Disk space is low!"), QT_TRANSLATE_NOOP("bitcoin-core", "Warning: This version is obsolete, upgrade required!"), QT_TRANSLATE_NOOP("bitcoin-core", "wallet.dat corrupt, salvage failed"), -}; \ No newline at end of file +}; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp new file mode 100644 index 0000000..b2aadeb --- /dev/null +++ b/src/qt/coincontroldialog.cpp @@ -0,0 +1,742 @@ +#include "coincontroldialog.h" +#include "ui_coincontroldialog.h" + +#include "init.h" +#include "bitcoinunits.h" +#include "walletmodel.h" +#include "addresstablemodel.h" +#include "optionsmodel.h" +#include "coincontrol.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +QList CoinControlDialog::payAmounts; +CCoinControl* CoinControlDialog::coinControl = new CCoinControl(); + +CoinControlDialog::CoinControlDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::CoinControlDialog), + model(0) +{ + ui->setupUi(this); + + // context menu actions + QAction *copyAddressAction = new QAction(tr("Copy address"), this); + QAction *copyLabelAction = new QAction(tr("Copy label"), this); + QAction *copyAmountAction = new QAction(tr("Copy amount"), this); + copyTransactionHashAction = new QAction(tr("Copy transaction ID"), this); // we need to enable/disable this + //lockAction = new QAction(tr("Lock unspent"), this); // we need to enable/disable this + //unlockAction = new QAction(tr("Unlock unspent"), this); // we need to enable/disable this + + // context menu + contextMenu = new QMenu(); + contextMenu->addAction(copyAddressAction); + contextMenu->addAction(copyLabelAction); + contextMenu->addAction(copyAmountAction); + contextMenu->addAction(copyTransactionHashAction); + //contextMenu->addSeparator(); + //contextMenu->addAction(lockAction); + //contextMenu->addAction(unlockAction); + + // context menu signals + connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint))); + connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress())); + connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel())); + connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount())); + connect(copyTransactionHashAction, SIGNAL(triggered()), this, SLOT(copyTransactionHash())); + //connect(lockAction, SIGNAL(triggered()), this, SLOT(lockCoin())); + //connect(unlockAction, SIGNAL(triggered()), this, SLOT(unlockCoin())); + + // clipboard actions + QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this); + QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this); + QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this); + QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this); + QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this); + QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this); + QAction *clipboardLowOutputAction = new QAction(tr("Copy low output"), this); + QAction *clipboardChangeAction = new QAction(tr("Copy change"), this); + + connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(clipboardQuantity())); + connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(clipboardAmount())); + connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(clipboardFee())); + connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(clipboardAfterFee())); + connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(clipboardBytes())); + connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(clipboardPriority())); + connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(clipboardLowOutput())); + connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(clipboardChange())); + + ui->labelCoinControlQuantity->addAction(clipboardQuantityAction); + ui->labelCoinControlAmount->addAction(clipboardAmountAction); + ui->labelCoinControlFee->addAction(clipboardFeeAction); + ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction); + ui->labelCoinControlBytes->addAction(clipboardBytesAction); + ui->labelCoinControlPriority->addAction(clipboardPriorityAction); + ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); + ui->labelCoinControlChange->addAction(clipboardChangeAction); + + // toggle tree/list mode + connect(ui->radioTreeMode, SIGNAL(toggled(bool)), this, SLOT(radioTreeMode(bool))); + connect(ui->radioListMode, SIGNAL(toggled(bool)), this, SLOT(radioListMode(bool))); + + // click on checkbox + connect(ui->treeWidget, SIGNAL(itemChanged( QTreeWidgetItem*, int)), this, SLOT(viewItemChanged( QTreeWidgetItem*, int))); + + // click on header +#if QT_VERSION < 0x050000 + ui->treeWidget->header()->setClickable(true); +#else + ui->treeWidget->header()->setSectionsClickable(true); +#endif + connect(ui->treeWidget->header(), SIGNAL(sectionClicked(int)), this, SLOT(headerSectionClicked(int))); + + // ok button + connect(ui->buttonBox, SIGNAL(clicked( QAbstractButton*)), this, SLOT(buttonBoxClicked(QAbstractButton*))); + + // (un)select all + connect(ui->pushButtonSelectAll, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked())); + + ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84); + ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 100); + ui->treeWidget->setColumnWidth(COLUMN_LABEL, 170); + ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 290); + ui->treeWidget->setColumnWidth(COLUMN_DATE, 110); + ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 100); + ui->treeWidget->setColumnWidth(COLUMN_PRIORITY, 100); + ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transacton hash in this column, but dont show it + ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but dont show it + ui->treeWidget->setColumnHidden(COLUMN_AMOUNT_INT64, true); // store amount int64 in this column, but dont show it + ui->treeWidget->setColumnHidden(COLUMN_PRIORITY_INT64, true); // store priority int64 in this column, but dont show it + + // default view is sorted by amount desc + sortView(COLUMN_AMOUNT_INT64, Qt::DescendingOrder); +} + +CoinControlDialog::~CoinControlDialog() +{ + delete ui; +} + +void CoinControlDialog::setModel(WalletModel *model) +{ + this->model = model; + + if(model && model->getOptionsModel() && model->getAddressTableModel()) + { + updateView(); + //updateLabelLocked(); + CoinControlDialog::updateLabels(model, this); + } +} + +// helper function str_pad +QString CoinControlDialog::strPad(QString s, int nPadLength, QString sPadding) +{ + while (s.length() < nPadLength) + s = sPadding + s; + + return s; +} + +// ok button +void CoinControlDialog::buttonBoxClicked(QAbstractButton* button) +{ + if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) + done(QDialog::Accepted); // closes the dialog +} + +// (un)select all +void CoinControlDialog::buttonSelectAllClicked() +{ + Qt::CheckState state = Qt::Checked; + for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + { + if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != Qt::Unchecked) + { + state = Qt::Unchecked; + break; + } + } + ui->treeWidget->setEnabled(false); + for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != state) + ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state); + ui->treeWidget->setEnabled(true); + CoinControlDialog::updateLabels(model, this); +} + +// context menu +void CoinControlDialog::showMenu(const QPoint &point) +{ + QTreeWidgetItem *item = ui->treeWidget->itemAt(point); + if(item) + { + contextMenuItem = item; + + // disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu + if (item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode) + { + copyTransactionHashAction->setEnabled(true); + //if (model->isLockedCoin(uint256(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt())) + //{ + // lockAction->setEnabled(false); + // unlockAction->setEnabled(true); + //} + //else + //{ + // lockAction->setEnabled(true); + // unlockAction->setEnabled(false); + //} + } + else // this means click on parent node in tree mode -> disable all + { + copyTransactionHashAction->setEnabled(false); + //lockAction->setEnabled(false); + //unlockAction->setEnabled(false); + } + + // show context menu + contextMenu->exec(QCursor::pos()); + } +} + +// context menu action: copy amount +void CoinControlDialog::copyAmount() +{ + QApplication::clipboard()->setText(contextMenuItem->text(COLUMN_AMOUNT)); +} + +// context menu action: copy label +void CoinControlDialog::copyLabel() +{ + if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_LABEL).length() == 0 && contextMenuItem->parent()) + QApplication::clipboard()->setText(contextMenuItem->parent()->text(COLUMN_LABEL)); + else + QApplication::clipboard()->setText(contextMenuItem->text(COLUMN_LABEL)); +} + +// context menu action: copy address +void CoinControlDialog::copyAddress() +{ + if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_ADDRESS).length() == 0 && contextMenuItem->parent()) + QApplication::clipboard()->setText(contextMenuItem->parent()->text(COLUMN_ADDRESS)); + else + QApplication::clipboard()->setText(contextMenuItem->text(COLUMN_ADDRESS)); +} + +// context menu action: copy transaction id +void CoinControlDialog::copyTransactionHash() +{ + QApplication::clipboard()->setText(contextMenuItem->text(COLUMN_TXHASH)); +} + +// context menu action: lock coin +/*void CoinControlDialog::lockCoin() +{ + if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked) + contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + + COutPoint outpt(uint256(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); + model->lockCoin(outpt); + contextMenuItem->setDisabled(true); + contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed")); + updateLabelLocked(); +}*/ + +// context menu action: unlock coin +/*void CoinControlDialog::unlockCoin() +{ + COutPoint outpt(uint256(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); + model->unlockCoin(outpt); + contextMenuItem->setDisabled(false); + contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon()); + updateLabelLocked(); +}*/ + +// copy label "Quantity" to clipboard +void CoinControlDialog::clipboardQuantity() +{ + QApplication::clipboard()->setText(ui->labelCoinControlQuantity->text()); +} + +// copy label "Amount" to clipboard +void CoinControlDialog::clipboardAmount() +{ + QApplication::clipboard()->setText(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" "))); +} + +// copy label "Fee" to clipboard +void CoinControlDialog::clipboardFee() +{ + QApplication::clipboard()->setText(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" "))); +} + +// copy label "After fee" to clipboard +void CoinControlDialog::clipboardAfterFee() +{ + QApplication::clipboard()->setText(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" "))); +} + +// copy label "Bytes" to clipboard +void CoinControlDialog::clipboardBytes() +{ + QApplication::clipboard()->setText(ui->labelCoinControlBytes->text()); +} + +// copy label "Priority" to clipboard +void CoinControlDialog::clipboardPriority() +{ + QApplication::clipboard()->setText(ui->labelCoinControlPriority->text()); +} + +// copy label "Low output" to clipboard +void CoinControlDialog::clipboardLowOutput() +{ + QApplication::clipboard()->setText(ui->labelCoinControlLowOutput->text()); +} + +// copy label "Change" to clipboard +void CoinControlDialog::clipboardChange() +{ + QApplication::clipboard()->setText(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" "))); +} + +// treeview: sort +void CoinControlDialog::sortView(int column, Qt::SortOrder order) +{ + sortColumn = column; + sortOrder = order; + ui->treeWidget->sortItems(column, order); + ui->treeWidget->header()->setSortIndicator((sortColumn == COLUMN_AMOUNT_INT64 ? COLUMN_AMOUNT : (sortColumn == COLUMN_PRIORITY_INT64 ? COLUMN_PRIORITY : sortColumn)), sortOrder); +} + +// treeview: clicked on header +void CoinControlDialog::headerSectionClicked(int logicalIndex) +{ + if (logicalIndex == COLUMN_CHECKBOX) // click on most left column -> do nothing + { + ui->treeWidget->header()->setSortIndicator((sortColumn == COLUMN_AMOUNT_INT64 ? COLUMN_AMOUNT : (sortColumn == COLUMN_PRIORITY_INT64 ? COLUMN_PRIORITY : sortColumn)), sortOrder); + } + else + { + if (logicalIndex == COLUMN_AMOUNT) // sort by amount + logicalIndex = COLUMN_AMOUNT_INT64; + + if (logicalIndex == COLUMN_PRIORITY) // sort by priority + logicalIndex = COLUMN_PRIORITY_INT64; + + if (sortColumn == logicalIndex) + sortOrder = ((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder); + else + { + sortColumn = logicalIndex; + sortOrder = ((sortColumn == COLUMN_AMOUNT_INT64 || sortColumn == COLUMN_PRIORITY_INT64 || sortColumn == COLUMN_DATE || sortColumn == COLUMN_CONFIRMATIONS) ? Qt::DescendingOrder : Qt::AscendingOrder); // if amount,date,conf,priority then default => desc, else default => asc + } + + sortView(sortColumn, sortOrder); + } +} + +// toggle tree mode +void CoinControlDialog::radioTreeMode(bool checked) +{ + if (checked && model) + updateView(); +} + +// toggle list mode +void CoinControlDialog::radioListMode(bool checked) +{ + if (checked && model) + updateView(); +} + +// checkbox clicked by user +void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) +{ + if (column == COLUMN_CHECKBOX && item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode) + { + COutPoint outpt(uint256(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt()); + + if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked) + coinControl->UnSelect(outpt); + else if (item->isDisabled()) // locked (this happens if "check all" through parent node) + item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + else + coinControl->Select(outpt); + + // selection changed -> update labels + if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all + CoinControlDialog::updateLabels(model, this); + } +} + +// helper function, return human readable label for priority number +QString CoinControlDialog::getPriorityLabel(double dPriority) +{ + if (dPriority > 576000ULL) // at least medium, this number is from AllowFree(), the other thresholds are kinda random + { + if (dPriority > 5760000000ULL) return tr("highest"); + else if (dPriority > 576000000ULL) return tr("high"); + else if (dPriority > 57600000ULL) return tr("medium-high"); + else return tr("medium"); + } + else + { + if (dPriority > 5760ULL) return tr("low-medium"); + else if (dPriority > 58ULL) return tr("low"); + else return tr("lowest"); + } +} + +// shows count of locked unspent outputs +/*void CoinControlDialog::updateLabelLocked() +{ + vector vOutpts; + model->listLockedCoins(vOutpts); + if (vOutpts.size() > 0) + { + ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size())); + ui->labelLocked->setVisible(true); + } + else ui->labelLocked->setVisible(false); +}*/ + +void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) +{ + if (!model) return; + + // nPayAmount + qint64 nPayAmount = 0; + bool fLowOutput = false; + bool fDust = false; + CTransaction txDummy; + foreach(const qint64 &amount, CoinControlDialog::payAmounts) + { + nPayAmount += amount; + + if (amount > 0) + { + if (amount < CENT) + fLowOutput = true; + + CTxOut txout(amount, (CScript)vector(24, 0)); + txDummy.vout.push_back(txout); + } + } + + QString sPriorityLabel = ""; + int64 nAmount = 0; + int64 nPayFee = 0; + int64 nAfterFee = 0; + int64 nChange = 0; + unsigned int nBytes = 0; + unsigned int nBytesInputs = 0; + double dPriority = 0; + double dPriorityInputs = 0; + unsigned int nQuantity = 0; + + vector vCoinControl; + vector vOutputs; + coinControl->ListSelected(vCoinControl); + model->getOutputs(vCoinControl, vOutputs); + + BOOST_FOREACH(const COutput& out, vOutputs) + { + // Quantity + nQuantity++; + + // Amount + nAmount += out.tx->vout[out.i].nValue; + + // Priority + dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); + + // Bytes + CTxDestination address; + if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + { + CPubKey pubkey; + CKeyID *keyid = boost::get< CKeyID >(&address); + if (keyid && model->getPubKey(*keyid, pubkey)) + nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); + else + nBytesInputs += 148; // in all error cases, simply assume 148 here + } + else nBytesInputs += 148; + } + + // calculation + if (nQuantity > 0) + { + // Bytes + nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here + + // Priority + dPriority = dPriorityInputs / nBytes; + sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority); + + // Fee + int64 nFee = nTransactionFee * (1 + (int64)nBytes / 1000); + + // Min Fee + int64 nMinFee = txDummy.GetMinFee(1, false, GMF_SEND); + + nPayFee = max(nFee, nMinFee); + + if (nPayAmount > 0) + { + nChange = nAmount - nPayFee - nPayAmount; + + // if sub-cent change is required, the fee must be raised to at least CTransaction::nMinTxFee + if (nPayFee < CENT && nChange > 0 && nChange < CENT) + { + if (nChange < CENT) // change < 0.01 => simply move all change to fees + { + nPayFee = nChange; + nChange = 0; + } + else + { + nChange = nChange + nPayFee - CENT; + nPayFee = CENT; + } + } + + if (nChange == 0) + nBytes -= 34; + } + + // after fee + nAfterFee = nAmount - nPayFee; + if (nAfterFee < 0) + nAfterFee = 0; + } + + // actually update labels + int nDisplayUnit = BitcoinUnits::BTC; + if (model && model->getOptionsModel()) + nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); + + QLabel *l1 = dialog->findChild("labelCoinControlQuantity"); + QLabel *l2 = dialog->findChild("labelCoinControlAmount"); + QLabel *l3 = dialog->findChild("labelCoinControlFee"); + QLabel *l4 = dialog->findChild("labelCoinControlAfterFee"); + QLabel *l5 = dialog->findChild("labelCoinControlBytes"); + QLabel *l6 = dialog->findChild("labelCoinControlPriority"); + QLabel *l7 = dialog->findChild("labelCoinControlLowOutput"); + QLabel *l8 = dialog->findChild("labelCoinControlChange"); + + // enable/disable "low output" and "change" + dialog->findChild("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0); + dialog->findChild("labelCoinControlLowOutput") ->setEnabled(nPayAmount > 0); + dialog->findChild("labelCoinControlChangeText") ->setEnabled(nPayAmount > 0); + dialog->findChild("labelCoinControlChange") ->setEnabled(nPayAmount > 0); + + // stats + l1->setText(QString::number(nQuantity)); // Quantity + l2->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAmount)); // Amount + l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee + l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee + l5->setText(((nBytes > 0) ? "~" : "") + QString::number(nBytes)); // Bytes + l6->setText(sPriorityLabel); // Priority + l7->setText((fLowOutput ? (fDust ? tr("DUST") : tr("yes")) : tr("no"))); // Low Output / Dust + l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change + + // turn labels "red" + l5->setStyleSheet((nBytes >= 10000) ? "color:red;" : ""); // Bytes >= 10000 + l6->setStyleSheet((dPriority <= 576000) ? "color:red;" : ""); // Priority < "medium" + l7->setStyleSheet((fLowOutput) ? "color:red;" : ""); // Low Output = "yes" + l8->setStyleSheet((nChange > 0 && nChange < CENT) ? "color:red;" : ""); // Change < 0.01BTC + + // tool tips + l5->setToolTip(tr("This label turns red, if the transaction size is bigger than 10000 bytes.\n\n This means a fee of at least %1 per kb is required.\n\n Can vary +/- 1 Byte per input.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT))); + l6->setToolTip(tr("Transactions with higher priority get more likely into a block.\n\nThis label turns red, if the priority is smaller than \"medium\".\n\n This means a fee of at least %1 per kb is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT))); + l7->setToolTip(tr("This label turns red, if any recipient receives an amount smaller than %1.\n\n This means a fee of at least %2 is required. \n\n Amounts below 0.546 times the minimum relay fee are shown as DUST.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT))); + l8->setToolTip(tr("This label turns red, if the change is smaller than %1.\n\n This means a fee of at least %2 is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT))); + dialog->findChild("labelCoinControlBytesText") ->setToolTip(l5->toolTip()); + dialog->findChild("labelCoinControlPriorityText") ->setToolTip(l6->toolTip()); + dialog->findChild("labelCoinControlLowOutputText")->setToolTip(l7->toolTip()); + dialog->findChild("labelCoinControlChangeText") ->setToolTip(l8->toolTip()); + + // Insufficient funds + QLabel *label = dialog->findChild("labelCoinControlInsuffFunds"); + if (label) + label->setVisible(nChange < 0); +} + +void CoinControlDialog::updateView() +{ + bool treeMode = ui->radioTreeMode->isChecked(); + + ui->treeWidget->clear(); + ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox + ui->treeWidget->setAlternatingRowColors(!treeMode); + QFlags flgCheckbox=Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; + QFlags flgTristate=Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate; + + int nDisplayUnit = BitcoinUnits::BTC; + if (model && model->getOptionsModel()) + nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); + + map > mapCoins; + model->listCoins(mapCoins); + + BOOST_FOREACH(PAIRTYPE(QString, vector) coins, mapCoins) + { + QTreeWidgetItem *itemWalletAddress = new QTreeWidgetItem(); + QString sWalletAddress = coins.first; + QString sWalletLabel = ""; + if (model->getAddressTableModel()) + sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); + if (sWalletLabel.length() == 0) + sWalletLabel = tr("(no label)"); + + if (treeMode) + { + // wallet address + ui->treeWidget->addTopLevelItem(itemWalletAddress); + + itemWalletAddress->setFlags(flgTristate); + itemWalletAddress->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked); + + for (int i = 0; i < ui->treeWidget->columnCount(); i++) + itemWalletAddress->setBackground(i, QColor(248, 247, 246)); + + // label + itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel); + + // address + itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress); + } + + int64 nSum = 0; + double dPrioritySum = 0; + int nChildren = 0; + int nInputSum = 0; + BOOST_FOREACH(const COutput& out, coins.second) + { + int nInputSize = 148; // 180 if uncompressed public key + nSum += out.tx->vout[out.i].nValue; + nChildren++; + + QTreeWidgetItem *itemOutput; + if (treeMode) itemOutput = new QTreeWidgetItem(itemWalletAddress); + else itemOutput = new QTreeWidgetItem(ui->treeWidget); + itemOutput->setFlags(flgCheckbox); + itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked); + + // address + CTxDestination outputAddress; + QString sAddress = ""; + if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress)) + { + sAddress = CBitcoinAddress(outputAddress).ToString().c_str(); + + // if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs + if (!treeMode || (!(sAddress == sWalletAddress))) + itemOutput->setText(COLUMN_ADDRESS, sAddress); + + CPubKey pubkey; + CKeyID *keyid = boost::get< CKeyID >(&outputAddress); + if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed()) + nInputSize = 180; + } + + // label + if (!(sAddress == sWalletAddress)) // change + { + // tooltip from where the change comes from + itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress)); + itemOutput->setText(COLUMN_LABEL, tr("(change)")); + } + else if (!treeMode) + { + QString sLabel = ""; + if (model->getAddressTableModel()) + sLabel = model->getAddressTableModel()->labelForAddress(sAddress); + if (sLabel.length() == 0) + sLabel = tr("(no label)"); + itemOutput->setText(COLUMN_LABEL, sLabel); + } + + // amount + itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue)); + itemOutput->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(out.tx->vout[out.i].nValue), 15, " ")); // padding so that sorting works correctly + + // date + itemOutput->setText(COLUMN_DATE, QDateTime::fromTime_t(out.tx->GetTxTime()).toUTC().toString("yy-MM-dd hh:mm")); + + // immature PoS reward + if (out.tx->IsCoinStake() && out.tx->GetBlocksToMaturity() > 0 && out.tx->GetDepthInMainChain() > 0) { + itemOutput->setBackground(COLUMN_CONFIRMATIONS, Qt::red); + itemOutput->setDisabled(true); + } + + // confirmations + itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " ")); + + // priority + double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10 + itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority)); + itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64)dPriority), 20, " ")); + dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); + nInputSum += nInputSize; + + // transaction hash + uint256 txhash = out.tx->GetHash(); + itemOutput->setText(COLUMN_TXHASH, txhash.GetHex().c_str()); + + // vout index + itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i)); + + // disable locked coins + /*if (model->isLockedCoin(txhash, out.i)) + { + COutPoint outpt(txhash, out.i); + coinControl->UnSelect(outpt); // just to be sure + itemOutput->setDisabled(true); + itemOutput->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed")); + }*/ + + // set checkbox + if (coinControl->IsSelected(txhash, out.i)) + itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Checked); + } + + // amount + if (treeMode) + { + dPrioritySum = dPrioritySum / (nInputSum + 78); + itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")"); + itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum)); + itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " ")); + itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum)); + itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64)dPrioritySum), 20, " ")); + } + } + + // expand all partially selected + if (treeMode) + { + for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked) + ui->treeWidget->topLevelItem(i)->setExpanded(true); + } + + // sort view + sortView(sortColumn, sortOrder); + ui->treeWidget->setEnabled(true); +} diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h new file mode 100644 index 0000000..5d0a90b --- /dev/null +++ b/src/qt/coincontroldialog.h @@ -0,0 +1,92 @@ +#ifndef COINCONTROLDIALOG_H +#define COINCONTROLDIALOG_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { + class CoinControlDialog; +} +class WalletModel; +class CCoinControl; + +class CoinControlDialog : public QDialog +{ + Q_OBJECT + +public: + explicit CoinControlDialog(QWidget *parent = 0); + ~CoinControlDialog(); + + void setModel(WalletModel *model); + + // static because also called from sendcoinsdialog + static void updateLabels(WalletModel*, QDialog*); + static QString getPriorityLabel(double); + + static QList payAmounts; + static CCoinControl *coinControl; + +private: + Ui::CoinControlDialog *ui; + WalletModel *model; + int sortColumn; + Qt::SortOrder sortOrder; + + QMenu *contextMenu; + QTreeWidgetItem *contextMenuItem; + QAction *copyTransactionHashAction; + //QAction *lockAction; + //QAction *unlockAction; + + QString strPad(QString, int, QString); + void sortView(int, Qt::SortOrder); + void updateView(); + + enum + { + COLUMN_CHECKBOX, + COLUMN_AMOUNT, + COLUMN_LABEL, + COLUMN_ADDRESS, + COLUMN_DATE, + COLUMN_CONFIRMATIONS, + COLUMN_PRIORITY, + COLUMN_TXHASH, + COLUMN_VOUT_INDEX, + COLUMN_AMOUNT_INT64, + COLUMN_PRIORITY_INT64 + }; + +private slots: + void showMenu(const QPoint &); + void copyAmount(); + void copyLabel(); + void copyAddress(); + void copyTransactionHash(); + //void lockCoin(); + //void unlockCoin(); + void clipboardQuantity(); + void clipboardAmount(); + void clipboardFee(); + void clipboardAfterFee(); + void clipboardBytes(); + void clipboardPriority(); + void clipboardLowOutput(); + void clipboardChange(); + void radioTreeMode(bool); + void radioListMode(bool); + void viewItemChanged(QTreeWidgetItem*, int); + void headerSectionClicked(int); + void buttonBoxClicked(QAbstractButton*); + void buttonSelectAllClicked(); + //void updateLabelLocked(); +}; + +#endif // COINCONTROLDIALOG_H diff --git a/src/qt/coincontroltreewidget.cpp b/src/qt/coincontroltreewidget.cpp new file mode 100644 index 0000000..0c1b514 --- /dev/null +++ b/src/qt/coincontroltreewidget.cpp @@ -0,0 +1,28 @@ +#include "coincontroltreewidget.h" +#include "coincontroldialog.h" + +CoinControlTreeWidget::CoinControlTreeWidget(QWidget *parent) : + QTreeWidget(parent) +{ + +} + +void CoinControlTreeWidget::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Space) // press spacebar -> select checkbox + { + event->ignore(); + int COLUMN_CHECKBOX = 0; + this->currentItem()->setCheckState(COLUMN_CHECKBOX, ((this->currentItem()->checkState(COLUMN_CHECKBOX) == Qt::Checked) ? Qt::Unchecked : Qt::Checked)); + } + else if (event->key() == Qt::Key_Escape) // press esc -> close dialog + { + event->ignore(); + CoinControlDialog *coinControlDialog = (CoinControlDialog*)this->parentWidget(); + coinControlDialog->done(QDialog::Accepted); + } + else + { + this->QTreeWidget::keyPressEvent(event); + } +} diff --git a/src/qt/coincontroltreewidget.h b/src/qt/coincontroltreewidget.h new file mode 100644 index 0000000..d981f72 --- /dev/null +++ b/src/qt/coincontroltreewidget.h @@ -0,0 +1,17 @@ +#ifndef COINCONTROLTREEWIDGET_H +#define COINCONTROLTREEWIDGET_H + +#include +#include + +class CoinControlTreeWidget : public QTreeWidget { +Q_OBJECT + +public: + explicit CoinControlTreeWidget(QWidget *parent = 0); + +protected: + virtual void keyPressEvent(QKeyEvent *event); +}; + +#endif // COINCONTROLTREEWIDGET_H \ No newline at end of file diff --git a/src/qt/forms/aboutdialog.ui b/src/qt/forms/aboutdialog.ui index ad188b9..af5428b 100644 --- a/src/qt/forms/aboutdialog.ui +++ b/src/qt/forms/aboutdialog.ui @@ -11,7 +11,7 @@ - About DiamondCoin + About Diamond @@ -50,7 +50,7 @@ IBeamCursor - <b>DMDCoin</b> version + <b>Diamond</b> version Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse @@ -63,7 +63,7 @@ IBeamCursor - 2.0.1 + 2.1.0.0 Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse @@ -94,7 +94,7 @@ Copyright © 2009-2014 Bitcoin Developers Copyright © 2012-2014 PPCoin Developers -Copyright © 2014 DiamondCoin Developers +Copyright © 2013-2016 Diamond Foundation diff --git a/src/qt/forms/addressbookpage.ui b/src/qt/forms/addressbookpage.ui index 69597e8..8dacba1 100644 --- a/src/qt/forms/addressbookpage.ui +++ b/src/qt/forms/addressbookpage.ui @@ -14,14 +14,16 @@ Address Book - AddressBookPage{background:transparent; color: white;} + + AddressBookPage{background:transparent; color: white;} + QTableView {background-color:rgb(69, 87, 96); color:white;} + QToolTip {background-color:#1f333e; color:white; border-style: none;} + QPushButton {background-color:rgb(69, 87, 96); color: white;} + - - color:white; - These are your Diamond addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. @@ -483,10 +485,6 @@ Double-click to edit address or label - - QTableView {background-color:rgb(69, 87, 96); color:white;} -QToolTip {background-color:#1f333e; color:white;} - false @@ -514,9 +512,6 @@ QToolTip {background-color:#1f333e; color:white;}
Create a new address - - background-color:rgb(69, 87, 96); color: white; - &New Address @@ -531,9 +526,6 @@ QToolTip {background-color:#1f333e; color:white;}
Copy the currently selected address to the system clipboard - - background-color:rgb(69, 87, 96); color: white; - &Copy Address @@ -545,9 +537,6 @@ QToolTip {background-color:#1f333e; color:white;} - - background-color:rgb(69, 87, 96); color: white; - Show &QR Code @@ -562,9 +551,6 @@ QToolTip {background-color:#1f333e; color:white;} Sign a message to prove you own a Diamond address - - background-color:rgb(69, 87, 96); color: white; - Sign &Message @@ -579,9 +565,6 @@ QToolTip {background-color:#1f333e; color:white;} Verify a message to ensure it was signed with a specified Diamond address - - background-color:rgb(69, 87, 96); color: white; - &Verify Message @@ -596,9 +579,6 @@ QToolTip {background-color:#1f333e; color:white;} Delete the currently selected address from the list - - background-color:rgb(69, 87, 96); color: white; - &Delete diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui index 2516904..204d282 100644 --- a/src/qt/forms/askpassphrasedialog.ui +++ b/src/qt/forms/askpassphrasedialog.ui @@ -99,6 +99,22 @@ + + + + true + + + Serves to disable trivial use of sendmoney when the OS account is compromised. Provides no real security. + + + For minting only + + + false + + + diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui new file mode 100644 index 0000000..29ff1fd --- /dev/null +++ b/src/qt/forms/coincontroldialog.ui @@ -0,0 +1,554 @@ + + + CoinControlDialog + + + + 0 + 0 + 1000 + 500 + + + + Coin Control + + + QTableView {background-color:rgb(69, 87, 96); color:white;} +QToolTip {background-color:#1f333e; color:white; border-style: none;} + + + + + + + 0 + + + 10 + + + + + 10 + + + 10 + + + 6 + + + 6 + + + + + font-weight:bold; + + + Quantity: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Bytes: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 10 + + + 6 + + + 6 + + + + + font-weight:bold; + + + Amount: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 DMD + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Priority: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 10 + + + 6 + + + 6 + + + + + font-weight:bold; + + + Fee: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 DMD + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + false + + + font-weight:bold; + + + Low Output: + + + + + + + false + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + no + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 10 + + + 6 + + + 6 + + + + + font-weight:bold; + + + After Fee: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 DMD + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + false + + + font-weight:bold; + + + Change: + + + + + + + false + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 DMD + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + + 0 + 40 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + 10 + 0 + 781 + 41 + + + + + 14 + + + + + + 0 + 0 + + + + (un)select all + + + + + + + + 0 + 0 + + + + Tree mode + + + true + + + + + + + + 0 + 0 + + + + List mode + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::CustomContextMenu + + + false + + + 11 + + + true + + + false + + + + + + + + + Amount + + + + + Label + + + + + Address + + + + + Date + + + + + Confirmations + + + Confirmed + + + + + Priority + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + CoinControlTreeWidget + QTreeWidget +
coincontroltreewidget.h
+
+
+ + +
diff --git a/src/qt/forms/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui index 10577f4..d6ec6bb 100644 --- a/src/qt/forms/editaddressdialog.ui +++ b/src/qt/forms/editaddressdialog.ui @@ -38,7 +38,7 @@ The label associated with this address book entry - + background-color:#1f333e; color:white; border: none;
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 6402c2c..6bca13e 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -33,7 +33,7 @@ - Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. Fee 0.01 recommended. + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. Fee 0.001 recommended. Qt::PlainText @@ -343,6 +343,16 @@ + + + + Whether to show coin control features or not. + + + Display coin &control features (experts only!) + + + diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 5361d26..756d570 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -22,7 +22,7 @@ - background-color:rgba(255, 255, 255, 40); color: white; + background-color:transparent; color:white; QFrame::StyledPanel @@ -56,7 +56,7 @@ The displayed information may be out of date. Your wallet automatically synchronizes with the Diamond network after a connection is established, but this process has not completed yet. - QToolTip {background-color:#1f333e; color:white;} + QToolTip {background-color:#1f333e; color:white; border-style: none;} QLabel { color: red; background:transparent;} @@ -119,7 +119,7 @@ QLabel { color: red; background:transparent;} QLabel {background:transparent;} -QToolTip {background-color:#1f333e; color:white;} +QToolTip {background-color:#1f333e; color:white; border-style: none;} 0 DMD @@ -155,7 +155,7 @@ QToolTip {background-color:#1f333e; color:white;} QLabel {background:transparent;} -QToolTip {background-color:#1f333e; color:white;} +QToolTip {background-color:#1f333e; color:white; border-style: none;} 0 DMD @@ -191,7 +191,7 @@ QToolTip {background-color:#1f333e; color:white;} QLabel {background:transparent;} -QToolTip {background-color:#1f333e; color:white;} +QToolTip {background-color:#1f333e; color:white; border-style: none;} 0 DMD @@ -224,7 +224,7 @@ QToolTip {background-color:#1f333e; color:white;} QLabel {background:transparent;} -QToolTip {background-color:#1f333e; color:white;} +QToolTip {background-color:#1f333e; color:white; border-style: none;} 0 DMD @@ -251,7 +251,7 @@ QToolTip {background-color:#1f333e; color:white;} QLabel {background:transparent;} -QToolTip {background-color:#1f333e; color:white;} +QToolTip {background-color:#1f333e; color:white; border-style: none;} 0 @@ -308,7 +308,7 @@ QToolTip {background-color:#1f333e; color:white;} - background-color:rgba(255, 255, 255, 40); color: white; + background-color:transparent; color:white; QFrame::StyledPanel @@ -325,7 +325,7 @@ QToolTip {background-color:#1f333e; color:white;} background:transparent; - <html><head/><body><p><span style=" font-weight:600; color:#ffffff;">Recent transactions</span></p></body></html> + <b>Recent transactions</b> @@ -335,7 +335,7 @@ QToolTip {background-color:#1f333e; color:white;} The displayed information may be out of date. Your wallet automatically synchronizes with the Diamond network after a connection is established, but this process has not completed yet. - QToolTip {background-color:#1f333e; color:white;} + QToolTip {background-color:#1f333e; color:white; border-style: none;} QLabel { color: red; background:transparent; } @@ -365,7 +365,7 @@ QLabel { color: red; background:transparent; } QListView { background: transparent; } -QToolTip {background-color:#1f333e; color:white;} +QToolTip {background-color:#1f333e; color:white; border-style: none;} QFrame::NoFrame @@ -381,31 +381,21 @@ QToolTip {background-color:#1f333e; color:white;} - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - false + + + Qt::Vertical - - Unlock wallet + + + 20 + 40 + - + diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 715d244..6cb282d 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -6,24 +6,601 @@ 0 0 - 686 - 234 + 831 + 390 Send Coins - SendCoinsDialog {background:transparent; color:white;} -QToolTip {background-color:#1f333e; color:white;} - + + SendCoinsDialog {background:transparent; color:white;} + QPushButton {background-color:rgb(69, 87, 96); color:white;} + QLabel {background-color:transparent; color:white;} + QFrame {background-color:transparent; color:white;} + QCheckBox {background-color:transparent; color:white;} + QLineEdit {background-color:#1f333e; color:white; border: none;} + QToolTip {background-color:#1f333e; color:white; border-style: none;} + + + 8 + - - - background-color:rgb(69, 87, 96) + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + 6 + + + 0 + + + 0 + + + 0 + + + 6 + + + + + 0 + + + 10 + + + 10 + + + + + 15 + + + + + + 0 + 0 + + + + + 75 + true + + + + Coin Control Features + + + + + + + + + 8 + + + 10 + + + + + Inputs... + + + + + + + automatically selected + + + 5 + + + + + + + + 75 + true + + + + Insufficient funds! + + + 5 + + + + + + + Qt::Horizontal + + + + 40 + 1 + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 20 + + + 0 + + + 10 + + + + + 10 + + + 14 + + + 10 + + + 4 + + + 6 + + + + + Quantity: + + + 0 + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Bytes: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 14 + + + 6 + + + 4 + + + 6 + + + + + Amount: + + + 0 + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 DMD + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Priority: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + medium + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 14 + + + 6 + + + 4 + + + 6 + + + + + Fee: + + + 0 + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 DMD + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Low Output: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + no + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 14 + + + 6 + + + 4 + + + 6 + + + + + After Fee: + + + 0 + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 DMD + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Change + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 DMD + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + + + + 12 + + + QLayout::SetDefaultConstraint + + + 5 + + + 5 + + + + + custom change address + + + + + + + false + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + 3 + + + + + + + + + Qt::Vertical + + + + 800 + 1 + + + + + + + + + + + QFrame::StyledPanel @@ -34,15 +611,12 @@ QToolTip {background-color:#1f333e; color:white;} - <html><head/><body><p><span style=" color:#ffffff;">Transaction comment:</span></p></body></html> + Transaction comment: - - color:white; - @@ -50,10 +624,6 @@ QToolTip {background-color:#1f333e; color:white;} - - -background-color:rgba(255, 255, 255, 40); color: white; - true @@ -67,7 +637,7 @@ background-color:rgba(255, 255, 255, 40); color: white; - background:transparent; + background-color:rgb(59, 88, 104); @@ -113,10 +683,6 @@ background-color:rgba(255, 255, 255, 40); color: white; Send to multiple recipients at once - - QPushButton {background-color:rgb(69, 87, 96); color: white;} -QToolTip {background-color:#1f333e; color:white;} - Add &Recipient @@ -143,10 +709,6 @@ QToolTip {background-color:#1f333e; color:white;} Remove all transaction fields - - QPushButton {background-color:rgb(69, 87, 96); color: white;} -QToolTip {background-color:#1f333e; color:white;} - Clear &All @@ -169,6 +731,12 @@ QToolTip {background-color:#1f333e; color:white;} + + + 0 + 0 + + Balance: @@ -176,6 +744,12 @@ QToolTip {background-color:#1f333e; color:white;} + + + 0 + 0 + + IBeamCursor @@ -213,10 +787,6 @@ QToolTip {background-color:#1f333e; color:white;} Confirm the send action - - QPushButton {background-color:rgb(69, 87, 96); color: white;} -QToolTip {background-color:#1f333e; color:white;} - S&end diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 3fcb25a..516fe4f 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -14,9 +14,10 @@ Form - background-color:rgb(69, 87, 96); -QPushButton{background-color:rgba(97, 147, 174, 255);color:white;} -QToolTip {background-color:#1f333e !important; color:white !important;} + background-color:transparent; + QLabel {background-color:transparent; color:white;} + QToolTip {background-color:#1f333e !important; color:white !important; border-style: none;} + QFrame::StyledPanel @@ -31,7 +32,7 @@ QToolTip {background-color:#1f333e !important; color:white !important;} - <html><head/><body><p><span style=" color:#ffffff;">A&amp;mount:</span></p></body></html> + A&mount: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -44,7 +45,7 @@ QToolTip {background-color:#1f333e !important; color:white !important;} - <html><head/><body><p><span style=" color:#ffffff;">Pay &amp;To:</span></p></body></html> + Pay &To: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -58,7 +59,8 @@ QToolTip {background-color:#1f333e !important; color:white !important;} color:white; -QToolTip {background-color:#1f333e; color:white;} + QToolTip {background-color:#1f333e; color:white; border-style: none;} + @@ -75,9 +77,6 @@ QToolTip {background-color:#1f333e; color:white;} Enter a label for this address to add it to your address book - - QToolTip {background-color:#1f333e; color:white;} - @@ -85,7 +84,7 @@ QToolTip {background-color:#1f333e; color:white;} - <html><head/><body><p><span style=" color:#ffffff;">&amp;Label:</span></p></body></html> + &Label: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -105,9 +104,6 @@ QToolTip {background-color:#1f333e; color:white;} The address to send the payment to (e.g. dc8hQjs9oy9AHPCSF5aTvkcRVECojV3joC) - - QToolTip {background-color:#1f333e; color:white;} - 34 @@ -118,9 +114,6 @@ QToolTip {background-color:#1f333e; color:white;} Choose address from address book - - QToolTip {background-color:#1f333e; color:white;} - @@ -138,9 +131,6 @@ QToolTip {background-color:#1f333e; color:white;} Paste address from clipboard - - QToolTip {background-color:#1f333e; color:white;} - @@ -158,9 +148,6 @@ QToolTip {background-color:#1f333e; color:white;} Remove this recipient - - QToolTip {background-color:#1f333e; color:white;} - diff --git a/src/qt/forms/transactiondescdialog.ui b/src/qt/forms/transactiondescdialog.ui index 2203e6a..f3300c1 100644 --- a/src/qt/forms/transactiondescdialog.ui +++ b/src/qt/forms/transactiondescdialog.ui @@ -14,7 +14,7 @@ Transaction details - TransactionDescDialog{background: transparent;} + TransactionDescDialog{background: transparent; color:white;} @@ -22,9 +22,6 @@ This pane shows a detailed description of the transaction - - - true diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index e5494de..add7991 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -22,6 +22,7 @@ #include #include #include + #include #include diff --git a/src/qt/locale/bitcoin_en.qm b/src/qt/locale/bitcoin_en.qm deleted file mode 100644 index 78bebd4..0000000 Binary files a/src/qt/locale/bitcoin_en.qm and /dev/null differ diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index a4eb463..6f12bd5 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -2708,7 +2708,7 @@ Address: %4 %s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: -rpcuser=bitcoinrpc +rpcuser=diamondrpc rpcpassword=%s (you do not need to remember this password) If the file does not exist, create it with owner-readable-only file permissions. @@ -2716,7 +2716,7 @@ If the file does not exist, create it with owner-readable-only file permissions. %s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: -rpcuser=bitcoinrpc +rpcuser=diamondrpc rpcpassword=%s (you do not need to remember this password) If the file does not exist, create it with owner-readable-only file permissions. diff --git a/src/qt/locale/bitcoin_ru.qm b/src/qt/locale/bitcoin_ru.qm deleted file mode 100644 index afb610b..0000000 Binary files a/src/qt/locale/bitcoin_ru.qm and /dev/null differ diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts index 56abb9a..f75445e 100644 --- a/src/qt/locale/bitcoin_ru.ts +++ b/src/qt/locale/bitcoin_ru.ts @@ -1406,8 +1406,8 @@ Address: %4 - The address to send the payment to (e.g. dZo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) - Адрес получателя платежа (например dZo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + The address to send the payment to (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Адрес получателя платежа (например 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) @@ -1436,8 +1436,8 @@ Address: %4 - Enter a BottleCaps address (e.g. dZo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) - Введите BottleCaps-адрес (например dZo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Enter a BottleCaps address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Введите BottleCaps-адрес (например 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) @@ -1460,8 +1460,8 @@ Address: %4 - The address to sign the message with (e.g. dZo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) - Адрес, которым вы хотите подписать сообщение (напр. dZo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + The address to sign the message with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Адрес, которым вы хотите подписать сообщение (напр. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) @@ -1524,8 +1524,8 @@ Address: %4 - The address the message was signed with (e.g. dZo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) - Адрес, которым было подписано сообщение (напр. dZo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + The address the message was signed with (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Адрес, которым было подписано сообщение (напр. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) @@ -1540,8 +1540,8 @@ Address: %4 - Enter a BottleCaps address (e.g. dZo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) - Введите адрес BottleCaps (напр. dZo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Enter a BottleCaps address (e.g. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) + Введите адрес BottleCaps (напр. 4Zo1ga6xuKuQ7JV7M9rGDoxdbYwV5zgQJ5) @@ -2721,7 +2721,7 @@ Address: %4 %s, you must set a rpcpassword in the configuration file: %s It is recommended you use the following random password: -rpcuser=bitcoinrpc +rpcuser=diamondrpc rpcpassword=%s (you do not need to remember this password) If the file does not exist, create it with owner-readable-only file permissions. @@ -2729,7 +2729,7 @@ If the file does not exist, create it with owner-readable-only file permissions. %s, вы должны установить опцию rpcpassword в конфигурационном файле: %s Рекомендуется использовать следующий случайный пароль: -rpcuser=bitcoinrpc +rpcuser=diamondrpc rpcpassword=%s (вам не нужно запоминать этот пароль) Если файл не существует, создайте его и установите права доступа только для владельца. diff --git a/src/qt/macdockiconhandler.h b/src/qt/macdockiconhandler.h index 2092fb2..08a8110 100644 --- a/src/qt/macdockiconhandler.h +++ b/src/qt/macdockiconhandler.h @@ -1,7 +1,8 @@ #ifndef MACDOCKICONHANDLER_H #define MACDOCKICONHANDLER_H -#include +#include +#include class QMenu; class QIcon; @@ -24,6 +25,7 @@ class MacDockIconHandler : public QObject QMenu *dockMenu(); void setIcon(const QIcon &icon); + void setMainWindow(QMainWindow *window); static MacDockIconHandler *instance(); void handleDockIconClickEvent(); @@ -39,6 +41,7 @@ public slots: DockIconClickEventHandler *m_dockIconClickEventHandler; QWidget *m_dummyWidget; QMenu *m_dockMenu; + QMainWindow *mainWindow; }; #endif // MACDOCKICONCLICKHANDLER_H diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm index 6d1cd64..0c3ee5f 100644 --- a/src/qt/macdockiconhandler.mm +++ b/src/qt/macdockiconhandler.mm @@ -4,7 +4,6 @@ #include #include -//extern void qt_mac_set_dock_menu(QMenu*); #include #include @@ -53,13 +52,19 @@ - (void)handleDockClickEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAp this->m_dummyWidget = new QWidget(); this->m_dockMenu = new QMenu(this->m_dummyWidget); + this->setMainWindow(NULL); [pool release]; } +void MacDockIconHandler::setMainWindow(QMainWindow *window) { + this->mainWindow = window; +} + MacDockIconHandler::~MacDockIconHandler() { [this->m_dockIconClickEventHandler release]; delete this->m_dummyWidget; + this->setMainWindow(NULL); } QMenu *MacDockIconHandler::dockMenu() @@ -109,5 +114,10 @@ - (void)handleDockClickEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAp void MacDockIconHandler::handleDockIconClickEvent() { + if (this->mainWindow) + { + this->mainWindow->activateWindow(); + this->mainWindow->show(); + } emit this->dockIconClicked(); } diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index 8028190..3a92be0 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -18,8 +18,10 @@ #ifdef Q_OS_MAC #include +#if QT_VERSION < 0x050000 extern bool qt_mac_execute_apple_script(const QString &script, AEDesc *ret); #endif +#endif // https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128 const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128; @@ -269,7 +271,9 @@ void Notificator::notifyGrowl(Class cls, const QString &title, const QString &te quotedTitle.replace("\\", "\\\\").replace("\"", "\\"); quotedText.replace("\\", "\\\\").replace("\"", "\\"); QString growlApp(this->mode == Notificator::Growl13 ? "Growl" : "GrowlHelperApp"); +#if QT_VERSION < 0x050000 qt_mac_execute_apple_script(script.arg(notificationApp, quotedTitle, quotedText, notificationIcon, growlApp), 0); +#endif } #endif diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 1a438cb..e13bc24 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -148,6 +148,7 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->lang, OptionsModel::Language); mapper->addMapping(ui->unit, OptionsModel::DisplayUnit); mapper->addMapping(ui->displayAddresses, OptionsModel::DisplayAddresses); + mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures); } void OptionsDialog::enableApplyButton() diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 756fe61..abc09a0 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -45,6 +45,7 @@ void OptionsModel::Init() bDisplayAddresses = settings.value("bDisplayAddresses", false).toBool(); fMinimizeToTray = settings.value("fMinimizeToTray", false).toBool(); fMinimizeOnClose = settings.value("fMinimizeOnClose", false).toBool(); + fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool(); nTransactionFee = settings.value("nTransactionFee").toLongLong(); language = settings.value("language", "").toString(); @@ -170,6 +171,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return QVariant(bitdb.GetDetach()); case Language: return settings.value("language", ""); + case CoinControlFeatures: + return QVariant(fCoinControlFeatures); default: return QVariant(); } @@ -239,6 +242,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in case Fee: nTransactionFee = value.toLongLong(); settings.setValue("nTransactionFee", nTransactionFee); + emit transactionFeeChanged(nTransactionFee); break; case DisplayUnit: nDisplayUnit = value.toInt(); @@ -258,6 +262,12 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in case Language: settings.setValue("language", value); break; + case CoinControlFeatures: { + fCoinControlFeatures = value.toBool(); + settings.setValue("fCoinControlFeatures", fCoinControlFeatures); + emit coinControlFeaturesChanged(fCoinControlFeatures); + } + break; default: break; } @@ -272,6 +282,11 @@ qint64 OptionsModel::getTransactionFee() return nTransactionFee; } +bool OptionsModel::getCoinControlFeatures() +{ + return fCoinControlFeatures; +} + bool OptionsModel::getMinimizeToTray() { return fMinimizeToTray; diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 34724ad..263b95e 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -30,6 +30,7 @@ class OptionsModel : public QAbstractListModel DisplayAddresses, // bool DetachDatabases, // bool Language, // QString + CoinControlFeatures, // bool OptionIDRowCount, }; @@ -48,6 +49,7 @@ class OptionsModel : public QAbstractListModel bool getMinimizeOnClose(); int getDisplayUnit(); bool getDisplayAddresses(); + bool getCoinControlFeatures(); QString getLanguage() { return language; } private: @@ -55,10 +57,13 @@ class OptionsModel : public QAbstractListModel bool bDisplayAddresses; bool fMinimizeToTray; bool fMinimizeOnClose; + bool fCoinControlFeatures; QString language; signals: void displayUnitChanged(int unit); + void transactionFeeChanged(qint64); + void coinControlFeaturesChanged(bool); }; #endif // OPTIONSMODEL_H diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 5b455f9..716e1f5 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -154,24 +154,6 @@ void OverviewPage::setNumTransactions(int count) ui->labelNumTransactions->setText(QLocale::system().toString(count)); } -void OverviewPage::unlockWallet() -{ - if(model->getEncryptionStatus() == WalletModel::Locked) - { - AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this); - dlg.setModel(model); - if(dlg.exec() == QDialog::Accepted) - { - ui->unlockWalletButton->setText(QString("Lock wallet")); - } - } - else - { - model->setWalletLocked(true); - ui->unlockWalletButton->setText(QString("Unlock wallet")); - } -} - void OverviewPage::setModel(WalletModel *model) { this->model = model; @@ -183,7 +165,7 @@ void OverviewPage::setModel(WalletModel *model) filter->setLimit(NUM_ITEMS); filter->setDynamicSortFilter(true); filter->setSortRole(Qt::EditRole); - filter->sort(TransactionTableModel::Status, Qt::DescendingOrder); + filter->sort(TransactionTableModel::Date, Qt::DescendingOrder); ui->listTransactions->setModel(filter); ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); @@ -196,14 +178,6 @@ void OverviewPage::setModel(WalletModel *model) connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); - - // Unlock wallet button - WalletModel::EncryptionStatus status = model->getEncryptionStatus(); - if(status == WalletModel::Unencrypted) - { - ui->unlockWalletButton->setDisabled(true); - } - connect(ui->unlockWalletButton, SIGNAL(clicked()), this, SLOT(unlockWallet())); } // update the display unit, to not use the default ("DMD") diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 2ed43b7..59fbb1d 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -29,7 +29,6 @@ class OverviewPage : public QWidget public slots: void setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance); void setNumTransactions(int count); - void unlockWallet(); signals: void transactionClicked(const QModelIndex &index); diff --git a/src/qt/res/bitcoin-qt.rc b/src/qt/res/bitcoin-qt.rc index b0ea7a9..26d5d1c 100644 --- a/src/qt/res/bitcoin-qt.rc +++ b/src/qt/res/bitcoin-qt.rc @@ -22,7 +22,7 @@ BEGIN VALUE "FileDescription", "Diamond-Qt (OSS GUI client for Diamond)" VALUE "FileVersion", VER_FILEVERSION_STR VALUE "InternalName", "Diamond-qt" - VALUE "LegalCopyright", "2009-2014 The Bitcoin developers, 2012-2014 The Diamond & PPCoin developers" + VALUE "LegalCopyright", "2009-2014 The Bitcoin developers, 2012-2013 PPCoin developers, 2013-2015 Diamond Foundation" VALUE "LegalTrademarks1", "Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php." VALUE "OriginalFilename", "Diamond-qt.exe" VALUE "ProductName", "Diamond-Qt" diff --git a/src/qt/res/icons/Diamond.icns b/src/qt/res/icons/Diamond.icns index 83a5c43..71d5376 100644 Binary files a/src/qt/res/icons/Diamond.icns and b/src/qt/res/icons/Diamond.icns differ diff --git a/src/qt/res/icons/bitcoin_testnet.png b/src/qt/res/icons/Diamond_testnet.png similarity index 100% rename from src/qt/res/icons/bitcoin_testnet.png rename to src/qt/res/icons/Diamond_testnet.png diff --git a/src/qt/res/icons/New folder/mining.png b/src/qt/res/icons/New folder/mining.png deleted file mode 100644 index e1c4ebc..0000000 Binary files a/src/qt/res/icons/New folder/mining.png and /dev/null differ diff --git a/src/qt/res/icons/New folder/mining_active.png b/src/qt/res/icons/New folder/mining_active.png deleted file mode 100644 index 8be8766..0000000 Binary files a/src/qt/res/icons/New folder/mining_active.png and /dev/null differ diff --git a/src/qt/res/icons/New folder/mining_inactive.png b/src/qt/res/icons/New folder/mining_inactive.png deleted file mode 100644 index cee171f..0000000 Binary files a/src/qt/res/icons/New folder/mining_inactive.png and /dev/null differ diff --git a/src/qt/res/icons/mining.png b/src/qt/res/icons/mining.png new file mode 100644 index 0000000..8217870 Binary files /dev/null and b/src/qt/res/icons/mining.png differ diff --git a/src/qt/res/icons/minting.png b/src/qt/res/icons/minting.png new file mode 100644 index 0000000..18bb7d6 Binary files /dev/null and b/src/qt/res/icons/minting.png differ diff --git a/src/qt/res/icons/tx_minted.png b/src/qt/res/icons/tx_minted.png new file mode 100644 index 0000000..6fd6afe Binary files /dev/null and b/src/qt/res/icons/tx_minted.png differ diff --git a/src/qt/res/images/diamond-splash.jpg b/src/qt/res/images/diamond-splash.jpg index ff47832..6d02745 100644 Binary files a/src/qt/res/images/diamond-splash.jpg and b/src/qt/res/images/diamond-splash.jpg differ diff --git a/src/qt/res/images/diamond.png b/src/qt/res/images/diamond.png new file mode 100644 index 0000000..882d645 Binary files /dev/null and b/src/qt/res/images/diamond.png differ diff --git a/src/qt/res/images/wallet.png b/src/qt/res/images/wallet.png deleted file mode 100644 index ad7d975..0000000 Binary files a/src/qt/res/images/wallet.png and /dev/null differ diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 0aa82d1..b7f72da 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -2,9 +2,12 @@ #include "ui_rpcconsole.h" #include "clientmodel.h" -#include "bitcoinrpc.h" #include "guiutil.h" +#ifndef Q_MOC_RUN +#include "bitcoinrpc.h" +#endif + #include #include #include @@ -20,7 +23,7 @@ // TODO: make it possible to filter out categories (esp debug messages when implemented) // TODO: receive errors and debug messages through ClientModel -const int CONSOLE_SCROLLBACK = 50; +//const int CONSOLE_SCROLLBACK = 50; const int CONSOLE_HISTORY = 50; const QSize ICON_SIZE(24, 24); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index efbaed4..dfce5b3 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1,18 +1,23 @@ #include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" +#include "init.h" #include "walletmodel.h" +#include "addresstablemodel.h" +#include "addressbookpage.h" #include "bitcoinunits.h" #include "addressbookpage.h" #include "optionsmodel.h" #include "sendcoinsentry.h" #include "guiutil.h" #include "askpassphrasedialog.h" -#include "base58.h" +#include "coincontrol.h" +#include "coincontroldialog.h" #include #include #include #include +#include SendCoinsDialog::SendCoinsDialog(QWidget *parent) : QDialog(parent), @@ -31,7 +36,39 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) : connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry())); connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); - + + // Coin Control + ui->lineEditCoinControlChange->setFont(GUIUtil::bitcoinAddressFont()); + connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked())); + connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int))); + connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &))); + + // Coin Control: clipboard actions + QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this); + QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this); + QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this); + QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this); + QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this); + QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this); + QAction *clipboardLowOutputAction = new QAction(tr("Copy low output"), this); + QAction *clipboardChangeAction = new QAction(tr("Copy change"), this); + connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity())); + connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAmount())); + connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee())); + connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee())); + connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes())); + connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardPriority())); + connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput())); + connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange())); + ui->labelCoinControlQuantity->addAction(clipboardQuantityAction); + ui->labelCoinControlAmount->addAction(clipboardAmountAction); + ui->labelCoinControlFee->addAction(clipboardFeeAction); + ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction); + ui->labelCoinControlBytes->addAction(clipboardBytesAction); + ui->labelCoinControlPriority->addAction(clipboardPriorityAction); + ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); + ui->labelCoinControlChange->addAction(clipboardChangeAction); + fNewRecipientAllowed = true; } @@ -52,6 +89,12 @@ void SendCoinsDialog::setModel(WalletModel *model) setBalance(model->getBalance(), model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance()); connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + // Coin Control + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels())); + connect(model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool))); + connect(model->getOptionsModel(), SIGNAL(transactionFeeChanged(qint64)), this, SLOT(coinControlUpdateLabels())); + ui->frameCoinControl->setVisible(model->getOptionsModel()->getCoinControlFeatures()); + coinControlUpdateLabels(); } } @@ -124,7 +167,13 @@ void SendCoinsDialog::on_sendButton_clicked() return; } - WalletModel::SendCoinsReturn sendstatus = model->sendCoins(txcomment, recipients); + WalletModel::SendCoinsReturn sendstatus; + + if (!model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures()) + sendstatus = model->sendCoins(txcomment,recipients); + else + sendstatus = model->sendCoins(txcomment,recipients, CoinControlDialog::coinControl); + switch(sendstatus.status) { case WalletModel::InvalidAddress: @@ -167,6 +216,8 @@ void SendCoinsDialog::on_sendButton_clicked() break; case WalletModel::OK: accept(); + CoinControlDialog::coinControl->UnSelectAll(); + coinControlUpdateLabels(); break; } fNewRecipientAllowed = true; @@ -202,6 +253,7 @@ SendCoinsEntry *SendCoinsDialog::addEntry() entry->setModel(model); ui->entries->addWidget(entry); connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*))); + connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels())); updateRemoveEnabled(); @@ -229,6 +281,7 @@ void SendCoinsDialog::updateRemoveEnabled() } } setupTabChain(0); + coinControlUpdateLabels(); } void SendCoinsDialog::removeEntry(SendCoinsEntry* entry) @@ -311,3 +364,152 @@ void SendCoinsDialog::updateDisplayUnit() ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->getBalance())); } } + + // Coin Control: copy label "Quantity" to clipboard + void SendCoinsDialog::coinControlClipboardQuantity() + { + QApplication::clipboard()->setText(ui->labelCoinControlQuantity->text()); + } + + // Coin Control: copy label "Amount" to clipboard + void SendCoinsDialog::coinControlClipboardAmount() + { + QApplication::clipboard()->setText(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" "))); + } + + // Coin Control: copy label "Fee" to clipboard + void SendCoinsDialog::coinControlClipboardFee() + { + QApplication::clipboard()->setText(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" "))); + } + + // Coin Control: copy label "After fee" to clipboard + void SendCoinsDialog::coinControlClipboardAfterFee() + { + QApplication::clipboard()->setText(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" "))); + } + + // Coin Control: copy label "Bytes" to clipboard + void SendCoinsDialog::coinControlClipboardBytes() + { + QApplication::clipboard()->setText(ui->labelCoinControlBytes->text()); + } + + // Coin Control: copy label "Priority" to clipboard + void SendCoinsDialog::coinControlClipboardPriority() + { + QApplication::clipboard()->setText(ui->labelCoinControlPriority->text()); + } + + // Coin Control: copy label "Low output" to clipboard + void SendCoinsDialog::coinControlClipboardLowOutput() + { + QApplication::clipboard()->setText(ui->labelCoinControlLowOutput->text()); + } + + // Coin Control: copy label "Change" to clipboard + void SendCoinsDialog::coinControlClipboardChange() + { + QApplication::clipboard()->setText(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" "))); + } + + // Coin Control: settings menu - coin control enabled/disabled by user + void SendCoinsDialog::coinControlFeatureChanged(bool checked) + { + ui->frameCoinControl->setVisible(checked); + + if (!checked && model) // coin control features disabled + CoinControlDialog::coinControl->SetNull(); + } + + // Coin Control: button inputs -> show actual coin control dialog + void SendCoinsDialog::coinControlButtonClicked() + { + CoinControlDialog dlg; + dlg.setModel(model); + dlg.exec(); + coinControlUpdateLabels(); + } + + // Coin Control: checkbox custom change address + void SendCoinsDialog::coinControlChangeChecked(int state) + { + if (model) + { + if (state == Qt::Checked) + CoinControlDialog::coinControl->destChange = CBitcoinAddress(ui->lineEditCoinControlChange->text().toStdString()).Get(); + else + CoinControlDialog::coinControl->destChange = CNoDestination(); + } + + ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked)); + ui->labelCoinControlChangeLabel->setEnabled((state == Qt::Checked)); + } + + // Coin Control: custom change address changed + void SendCoinsDialog::coinControlChangeEdited(const QString & text) + { + if (model) + { + CoinControlDialog::coinControl->destChange = CBitcoinAddress(text.toStdString()).Get(); + + // label for the change address + ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}"); + if (text.isEmpty()) + ui->labelCoinControlChangeLabel->setText(""); + else if (!CBitcoinAddress(text.toStdString()).IsValid()) + { + ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); + ui->labelCoinControlChangeLabel->setText(tr("WARNING: Invalid Bitcoin address")); + } + else + { + QString associatedLabel = model->getAddressTableModel()->labelForAddress(text); + if (!associatedLabel.isEmpty()) + ui->labelCoinControlChangeLabel->setText(associatedLabel); + else + { + CPubKey pubkey; + CKeyID keyid; + CBitcoinAddress(text.toStdString()).GetKeyID(keyid); + if (model->getPubKey(keyid, pubkey)) + ui->labelCoinControlChangeLabel->setText(tr("(no label)")); + else + { + ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); + ui->labelCoinControlChangeLabel->setText(tr("WARNING: unknown change address")); + } + } + } + } + } + + // Coin Control: update labels + void SendCoinsDialog::coinControlUpdateLabels() + { + if (!model || !model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures()) + return; + // set pay amounts + CoinControlDialog::payAmounts.clear(); + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + CoinControlDialog::payAmounts.append(entry->getValue().amount); + } + if (CoinControlDialog::coinControl->HasSelected()) + { + // actual coin control calculation + CoinControlDialog::updateLabels(model, this); + // show coin control stats + ui->labelCoinControlAutomaticallySelected->hide(); + ui->widgetCoinControl->show(); + } + else + { + // hide coin control stats + ui->labelCoinControlAutomaticallySelected->show(); + ui->widgetCoinControl->hide(); + ui->labelCoinControlInsuffFunds->hide(); + } +} diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 32bb61a..68424bb 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -2,6 +2,7 @@ #define SENDCOINSDIALOG_H #include +#include namespace Ui { class SendCoinsDialog; @@ -49,6 +50,19 @@ private slots: void on_sendButton_clicked(); void removeEntry(SendCoinsEntry* entry); void updateDisplayUnit(); + void coinControlFeatureChanged(bool); + void coinControlButtonClicked(); + void coinControlChangeChecked(int); + void coinControlChangeEdited(const QString &); + void coinControlUpdateLabels(); + void coinControlClipboardQuantity(); + void coinControlClipboardAmount(); + void coinControlClipboardFee(); + void coinControlClipboardAfterFee(); + void coinControlClipboardBytes(); + void coinControlClipboardPriority(); + void coinControlClipboardLowOutput(); + void coinControlClipboardChange(); }; #endif // SENDCOINSDIALOG_H diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index f00cf1b..2bed6cb 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -23,7 +23,10 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) : #if QT_VERSION >= 0x040700 /* Do not move this to the XML file, Qt before 4.7 will choke on it */ ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); + ui->addAsLabel->setStyleSheet("background-color:#1f333e; color:white; border:none;"); ui->payTo->setPlaceholderText(tr("Enter a valid Diamond address")); + ui->payTo->setStyleSheet("background-color:#1f333e; color:white; border:none;"); + ui->payAmount->setStyleSheet("background-color:#1f333e; color:white; border:none;"); #endif setFocusPolicy(Qt::TabFocus); setFocusProxy(ui->payTo); @@ -72,6 +75,7 @@ void SendCoinsEntry::setModel(WalletModel *model) if(model && model->getOptionsModel()) connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + connect(ui->payAmount, SIGNAL(textChanged()), this, SIGNAL(payAmountChanged())); clear(); } diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 0ac14c1..4148128 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -39,6 +39,7 @@ public slots: signals: void removeEntry(SendCoinsEntry *entry); + void payAmountChanged(); private slots: void on_deleteButton_clicked(); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 361e691..ae10db6 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -220,7 +220,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += "" + tr("Transaction ID") + ": " + wtx.GetHash().ToString().c_str() + "
"; if (wtx.IsCoinBase() || wtx.IsCoinStake()) - strHTML += "
" + tr("Generated coins must mature 50 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.") + "
"; + strHTML += "
" + tr("Generated coins must mature 200 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.") + "
"; // // Debug view diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 18858e2..3e453d0 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -34,7 +34,42 @@ QList TransactionRecord::decomposeTransaction(const CWallet * if (wtx.IsCoinStake()) { // Stake generation - parts.append(TransactionRecord(hash, nTime, TransactionRecord::StakeMint, "", -nDebit, wtx.GetValueOut())); + TransactionRecord sub(hash, nTime, TransactionRecord::StakeMint, "", -nDebit, wtx.GetValueOut()); + CTxDestination stakingAddress, rewardAddress; + /* vout[0] is blank, just marks the transaction as stake + * vout[1] is the first stake output and therefore always related to + * the staking address. */ + if (ExtractDestination(wtx.vout[1].scriptPubKey, stakingAddress)) { + if (ExtractDestination(wtx.vout[wtx.vout.size() - 1].scriptPubKey, rewardAddress)) { + /* If the staking address isn't in the wallet than this is an + * external scrape received from another wallet. */ + if (!IsMine(*wallet, stakingAddress)) { + sub.type = TransactionRecord::ExternalScrape; + // In this instance the reward is always in the last output. + sub.credit = wtx.vout[wtx.vout.size() - 1].nValue; + sub.address = CBitcoinAddress(rewardAddress).ToString(); + /* The reward address is not in the wallet but the address is + * so the reward went to a scrape, treat it like a normal mint + * but display the scrape address. */ + } else if (!IsMine(*wallet, rewardAddress)) { + sub.type = TransactionRecord::ScrapeToExternal; + sub.address = CBitcoinAddress(rewardAddress).ToString(); + /* The address is in the wallet but it's different than the staking + * address, display the reward address and the stake amount. */ + } else if (CBitcoinAddress(stakingAddress).ToString() != CBitcoinAddress(rewardAddress).ToString()) { + sub.type = TransactionRecord::LocalScrape; + sub.address = CBitcoinAddress(rewardAddress).ToString(); + // The reweard went to the same address as the staking address (not a scrape) + } else { + sub.address = CBitcoinAddress(stakingAddress).ToString(); + } + // No destination in the last output (this should not happen) + } else { + sub.address = CBitcoinAddress(stakingAddress).ToString(); + } + } + + parts.append(sub); } else if (nNet > 0 || wtx.IsCoinBase()) { @@ -197,7 +232,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) } // For generated transactions, determine maturity - if(type == TransactionRecord::Generated || type == TransactionRecord::StakeMint) + if(type == TransactionRecord::Generated || type == TransactionRecord::StakeMint || type == TransactionRecord::ExternalScrape || type == TransactionRecord::LocalScrape || type == TransactionRecord::ScrapeToExternal) { int64 nCredit = wtx.GetCredit(true); if (nCredit == 0) @@ -233,4 +268,3 @@ std::string TransactionRecord::getTxID() { return hash.ToString() + strprintf("-%03d", idx); } - diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index 8d4ab03..0b60931 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -69,7 +69,10 @@ class TransactionRecord RecvWithAddress, RecvFromOther, SendToSelf, - StakeMint + StakeMint, + ExternalScrape, + LocalScrape, + ScrapeToExternal }; /** Number of confirmation needed for transaction */ diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 0638a8b..c302f6b 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -295,14 +295,19 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth); break; } - if(wtx->type == TransactionRecord::Generated || wtx->type == TransactionRecord::StakeMint) + if(wtx->type == TransactionRecord::Generated || wtx->type == TransactionRecord::StakeMint || wtx->type == TransactionRecord::ExternalScrape || wtx->type == TransactionRecord::LocalScrape || wtx->type == TransactionRecord::ScrapeToExternal) { switch(wtx->status.maturity) { case TransactionStatus::Immature: - status += "\n" + tr("Mined balance will be available when it matures in %n more block(s)", "", wtx->status.matures_in); + wtx->type == TransactionRecord::ScrapeToExternal ? status += "\n" + tr("Minted balance will be available at reward address in %n more blocks", "", + wtx->status.matures_in) : + status += "\n" + tr("Mined balance will be available in %n more blocks", "", + wtx->status.matures_in); break; case TransactionStatus::Mature: + if (wtx->type == TransactionRecord::ScrapeToExternal) + status += "\n" + tr("Minted balance is available at the reward address not found in this wallet."); break; case TransactionStatus::MaturesWarning: status += "\n" + tr("This block was not received by any other nodes and will probably not be accepted!"); @@ -360,9 +365,15 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const case TransactionRecord::SendToSelf: return tr("Payment to yourself"); case TransactionRecord::StakeMint: - return tr("Mint"); + return tr("Minted"); case TransactionRecord::Generated: return tr("Mined"); + case TransactionRecord::ExternalScrape: + return tr("External scrape"); + case TransactionRecord::LocalScrape: + return tr("Local scrape"); + case TransactionRecord::ScrapeToExternal: + return tr("Scraped to external"); default: return QString(); } @@ -373,12 +384,15 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx switch(wtx->type) { case TransactionRecord::Generated: + { + QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit); + return QIcon(":/icons/tx_mined"); + } case TransactionRecord::StakeMint: - { - QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit); - float dd = str.toFloat(); - return QIcon(":/icons/tx_mined"); - } + { + QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit); + return QIcon(":/icons/tx_minted"); + } case TransactionRecord::RecvWithAddress: case TransactionRecord::RecvFromOther: return QIcon(":/icons/tx_input"); @@ -400,6 +414,9 @@ QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, b case TransactionRecord::RecvWithAddress: case TransactionRecord::SendToAddress: case TransactionRecord::Generated: + case TransactionRecord::ExternalScrape: + case TransactionRecord::LocalScrape: + case TransactionRecord::ScrapeToExternal: return lookupAddress(wtx->address, tooltip); case TransactionRecord::SendToOther: return QString::fromStdString(wtx->address); @@ -417,6 +434,9 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const case TransactionRecord::RecvWithAddress: case TransactionRecord::SendToAddress: case TransactionRecord::Generated: + case TransactionRecord::ExternalScrape: + case TransactionRecord::LocalScrape: + case TransactionRecord::ScrapeToExternal: { QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(wtx->address)); if(label.isEmpty()) @@ -435,7 +455,9 @@ QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit); if(showUnconfirmed) { - if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) + /* Always display ScrapeToExternal transactions as if the coins are + * immature because they are not and will never be in this wallet. */ + if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature || wtx->type == TransactionRecord::ScrapeToExternal) { str = QString("[") + str + QString("]"); } @@ -445,7 +467,7 @@ QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const { - if(wtx->type == TransactionRecord::Generated || wtx->type == TransactionRecord::StakeMint) + if(wtx->type == TransactionRecord::Generated || wtx->type == TransactionRecord::StakeMint || wtx->type == TransactionRecord::ExternalScrape || wtx->type == TransactionRecord::LocalScrape) { switch(wtx->status.maturity) { diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 564be3f..427fdb0 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -35,8 +35,7 @@ TransactionView::TransactionView(QWidget *parent) : { // Build filter row setContentsMargins(0,0,0,0); - setStyleSheet("background-color:rgba(255, 255, 255, 40); color: white;"); - + setStyleSheet("background-color:#1f333e; color: white;"); QHBoxLayout *hlayout = new QHBoxLayout(); hlayout->setContentsMargins(0,0,0,0); @@ -76,7 +75,7 @@ TransactionView::TransactionView(QWidget *parent) : typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) | TransactionFilterProxy::TYPE(TransactionRecord::SendToOther)); typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf)); - typeWidget->addItem(tr("Mint"), TransactionFilterProxy::TYPE(TransactionRecord::StakeMint)); + typeWidget->addItem(tr("Minted"), TransactionFilterProxy::TYPE(TransactionRecord::StakeMint)); typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated)); typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other)); @@ -86,6 +85,7 @@ TransactionView::TransactionView(QWidget *parent) : #if QT_VERSION >= 0x040700 /* Do not move this to the XML file, Qt before 4.7 will choke on it */ addressWidget->setPlaceholderText(tr("Enter address or label to search")); + addressWidget->setStyleSheet("background-color:#1f333e; color:white; border:none;"); #endif hlayout->addWidget(addressWidget); @@ -170,12 +170,12 @@ void TransactionView::setModel(WalletModel *model) transactionView->setModel(transactionProxyModel); transactionView->setAlternatingRowColors(true); - transactionView->setStyleSheet("QTableView{alternate-background-color: rgb(37,59,72); background-color: rgb(60,93,112); selection-background-color: rgb(97,147,174);} QToolTip{background-color:#1f333e; color:white}"); + transactionView->setStyleSheet("QTableView{alternate-background-color: rgb(37,59,72); background-color: rgb(60,93,112); selection-background-color: rgb(97,147,174);} QToolTip{background-color:#1f333e; color:white; border-style: none;}"); transactionView->setSelectionBehavior(QAbstractItemView::SelectRows); transactionView->setSelectionMode(QAbstractItemView::ExtendedSelection); transactionView->setSortingEnabled(true); - transactionView->sortByColumn(TransactionTableModel::Status, Qt::DescendingOrder); + transactionView->sortByColumn(TransactionTableModel::Date, Qt::DescendingOrder); transactionView->verticalHeader()->hide(); transactionView->horizontalHeader()->resizeSection( diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 34850d8..5e2b28f 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -129,7 +129,7 @@ bool WalletModel::validateAddress(const QString &address) return addressParsed.IsValid(); } -WalletModel::SendCoinsReturn WalletModel::sendCoins(const QString &txcomment, const QList &recipients) +WalletModel::SendCoinsReturn WalletModel::sendCoins(const QString &txcomment, const QList &recipients, const CCoinControl *coinControl) { qint64 total = 0; QSet setAddress; @@ -161,12 +161,18 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QString &txcomment, co return DuplicateAddress; } - if(total > getBalance()) + int64 nBalance = 0; + std::vector vCoins; + wallet->AvailableCoins(vCoins, true, coinControl); + + BOOST_FOREACH(const COutput& out, vCoins) + nBalance += out.tx->vout[out.i].nValue; + if(total > nBalance) { return AmountExceedsBalance; } - if((total + nTransactionFee) > getBalance()) + if((total + nTransactionFee) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee); } @@ -189,11 +195,11 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QString &txcomment, co std::string strTxComment = txcomment.toStdString(); if (!strTxComment.empty()) strTxComment = "text:" + strTxComment; - bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strTxComment); + bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strTxComment, coinControl); if(!fCreated) { - if((total + nFeeRequired) > wallet->GetBalance()) + if((total + nFeeRequired) > nBalance) // FIXME: could cause collisions in the future { return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); } @@ -247,20 +253,18 @@ TransactionTableModel *WalletModel::getTransactionTableModel() return transactionTableModel; } -WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const -{ +WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const { + if(!wallet->IsCrypted()) - { - return Unencrypted; - } - else if(wallet->IsLocked()) - { - return Locked; - } + return(Unencrypted); + + if(wallet->IsLocked()) + return(Locked); + + if(fStakingOnly) + return(UnlockedStaking); else - { - return Unlocked; - } + return(Unlocked); } bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase) @@ -352,15 +356,22 @@ void WalletModel::unsubscribeFromCoreSignals() WalletModel::UnlockContext WalletModel::requestUnlock() { bool was_locked = getEncryptionStatus() == Locked; + + if((!was_locked) && fStakingOnly) { + setWalletLocked(true); + was_locked = getEncryptionStatus() == Locked; + } + if(was_locked) { // Request UI to unlock wallet emit requireUnlock(); } + // If wallet is still locked, unlock was failed or cancelled, mark context as invalid bool valid = getEncryptionStatus() != Locked; - return UnlockContext(this, valid, was_locked); + return(UnlockContext(this, valid, was_locked && !fStakingOnly)); } WalletModel::UnlockContext::UnlockContext(WalletModel *wallet, bool valid, bool relock): @@ -384,3 +395,70 @@ void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs) *this = rhs; rhs.relock = false; } + + bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const + { + return wallet->GetPubKey(address, vchPubKeyOut); + } + + // returns a list of COutputs from COutPoints + void WalletModel::getOutputs(const std::vector& vOutpoints, std::vector& vOutputs) + { + BOOST_FOREACH(const COutPoint& outpoint, vOutpoints) + { + if (!wallet->mapWallet.count(outpoint.hash)) continue; + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain()); + vOutputs.push_back(out); + } + } + + // AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address) + void WalletModel::listCoins(std::map >& mapCoins) const + { + std::vector vCoins; + wallet->AvailableCoins(vCoins); + std::vector vLockedCoins; + + // add locked coins + BOOST_FOREACH(const COutPoint& outpoint, vLockedCoins) + { + if (!wallet->mapWallet.count(outpoint.hash)) continue; + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain()); + vCoins.push_back(out); + } + + BOOST_FOREACH(const COutput& out, vCoins) + { + COutput cout = out; + + while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0])) + { + if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break; + cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0); + } + + CTxDestination address; + if(!ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address)) continue; + mapCoins[CBitcoinAddress(address).ToString().c_str()].push_back(out); + } + } + + bool WalletModel::isLockedCoin(uint256 hash, unsigned int n) const + { + return false; + } + + void WalletModel::lockCoin(COutPoint& output) + { + return; + } + + void WalletModel::unlockCoin(COutPoint& output) + { + return; + } + + void WalletModel::listLockedCoins(std::vector& vOutpts) + { + return; + } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 5890b42..ef50f29 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -2,6 +2,8 @@ #define WALLETMODEL_H #include +#include +#include #include "allocators.h" /* for SecureString */ @@ -9,6 +11,12 @@ class OptionsModel; class AddressTableModel; class TransactionTableModel; class CWallet; +class CKeyID; +class CPubKey; +class COutput; +class COutPoint; +class uint256; +class CCoinControl; QT_BEGIN_NAMESPACE class QTimer; @@ -48,7 +56,8 @@ class WalletModel : public QObject { Unencrypted, // !wallet->IsCrypted() Locked, // wallet->IsCrypted() && wallet->IsLocked() - Unlocked // wallet->IsCrypted() && !wallet->IsLocked() + Unlocked, /* wallet->IsCrypted() && !wallet->IsLocked() && !fStakingOnly */ + UnlockedStaking /* wallet->IsCrypted() && !wallet->IsLocked() && fStakingOnly */ }; OptionsModel *getOptionsModel(); @@ -68,7 +77,7 @@ class WalletModel : public QObject // Return status record for SendCoins, contains error id + information struct SendCoinsReturn { - SendCoinsReturn(StatusCode status, + SendCoinsReturn(StatusCode status=Aborted, qint64 fee=0, QString hex=QString()): status(status), fee(fee), hex(hex) {} @@ -78,7 +87,7 @@ class WalletModel : public QObject }; // Send coins to a list of recipients - SendCoinsReturn sendCoins(const QString &txcomment, const QList &recipients); + SendCoinsReturn sendCoins(const QString &txcomment, const QList &recipients, const CCoinControl *coinControl=NULL); // Wallet encryption bool setWalletEncrypted(bool encrypted, const SecureString &passphrase); @@ -109,6 +118,14 @@ class WalletModel : public QObject }; UnlockContext requestUnlock(); + + bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; + void getOutputs(const std::vector& vOutpoints, std::vector& vOutputs); + void listCoins(std::map >& mapCoins) const; + bool isLockedCoin(uint256 hash, unsigned int n) const; + void lockCoin(COutPoint& output); + void unlockCoin(COutPoint& output); + void listLockedCoins(std::vector& vOutpts); private: CWallet *wallet; diff --git a/src/reactorlist.cpp b/src/reactorlist.cpp new file mode 100644 index 0000000..6c983ae --- /dev/null +++ b/src/reactorlist.cpp @@ -0,0 +1,498 @@ +// Copyright (c) 2015-2016 Nathan Bass "IngCr3at1on" +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "reactors.h" + +unsigned int REACTOR_START_TIME = 1463313600; // Tue 15 May 2016 12:00:00 UTC +unsigned int REACTOR_TEST_START_TIME = 1453075200; // Sun Jan 17 18:00:00 CST 2016 + +/* If changing anything here make sure to update the CURRENT_REACTOR_VERSION to + * force old databases to be rewritten and update the first and last keys in + * InitReactors */ + +void CReactorDB::WriteReactorDB() { + LOCK(cs); + + WriteReactorDBVersion(CURRENT_REACTOR_VERSION); + + // Legendaries, no shutoff time. + + /* The Hearth of Eternity. + * Every great dream begins with a dreamer. Within him lies the strength, + * the patience, and the passion to reach for the stars to change the world. + */ + WriteReactorAddr(std::string("dasHERZmwgtvWGNRxs55GPrXsAKwY7bX85"), REACTOR_START_TIME, -1, 15000); + /* The Cullinan. + * Cowards die many times before their deaths; The valiant never taste of + * death but once. + */ + WriteReactorAddr(std::string("dEB799gGhnG89b8bkcPUitgXiJWc7ggNCM"), REACTOR_START_TIME, -1, 15000); + /* Koh-I-Noor. + * Rough diamonds may sometimes be mistaken for worthless pebbles, just like + * the true value of a human being can be hidden beneath a hard surface. + */ + WriteReactorAddr(std::string("dMdjuF7xcsUvkYy2ikaQkx5z5viqNvhzxz"), REACTOR_START_TIME, -1, 15000); + /* The Allnatt. + * Watch your thoughts, thoughts become words and words become actions. + * Actions build character and character defines your destiny. AreTimes.com + */ + WriteReactorAddr(std::string("dTKsf6qD7BeVN7bNZpBM2M1iXfbrt5vvak"), REACTOR_START_TIME, -1, 15000); + /* The Steinmetz Pink. + * Kishore Now That You Are One Of The Legends, May You Be Immortalized In + * History. And Bring Hope To, Those Who Have Lost Hope. + */ + WriteReactorAddr(std::string("dW4mnJLQZwFExTBFTBdGSNte71TfvqC8LX"), REACTOR_START_TIME, -1, 15000); + /* De Beers Centenary. + * Violence: It is not any once race. The victems of the violence are black + * and white. Most importantly they were human beings who loved others. BK + */ + WriteReactorAddr(std::string("dTuJJvYoDcerBaHwoK8cK2oEEpYEWr8Tdh"), REACTOR_START_TIME, -1, 15000); + /* The Moussaieff Red. + * Dear son Andrei, we love you and wish for you the happiest of days and + * a bright, healthy future! + */ + WriteReactorAddr(std::string("dMbc7KM7cDy487C9FrWwvyaquprTgtCWZ8"), REACTOR_START_TIME, -1, 3000); + /* The Sancy. + * To grow a Diamond is a challenge but a good Diamond gains its value over + * time. For every Diamond that makes my life a Bliss, a grateful THANKS! + */ + WriteReactorAddr(std::string("dJWVbYhykxPwJQ1PxxjtQjBoGc1abhfQh7"), REACTOR_START_TIME, -1, 3000); + /* Wittelsbach. + * Leonidas pros Perses - Molon Lave - Apo ta kokkala bgalmenh twn Ellhnwn + * ta Iera. Kai san prwta andreiwmenh. Xaire w Xaire Elefteria. Annoula. + */ + WriteReactorAddr(std::string("dFKNwDWexXFapBBxRh5FsYtj3GkG8TyS75"), REACTOR_START_TIME, -1, 3000); + /* The Hope. + * In retrospect all will see this impulsive decision helped change the + * world. Only things impossible are things not yet figured out. + */ + WriteReactorAddr(std::string("dZTEMvLjwWjtiH3k1LCCX2soLXhQcUDLyZ"), REACTOR_START_TIME, -1, 3000); + + // Foundation, variant 1 (disabled). + WriteReactorAddr(std::string("VARIANTDISABLED"), -1, -1, 10000); + + // Foundation, variant 2. + WriteReactorAddr(std::string("dRwXJFujnkqLVCE1He4Se8TzBUGfTNZKVJ"), REACTOR_START_TIME, -1, 100); + + // Year 1 group 1 + WriteReactorAddr(std::string("dMd36o2f1xpRqjbhtJzzMQKFCnrPmkY35W"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oizJFn3SmAkb6joFxeGdkxJfD5hma"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oy5gCoTBpzcDow7zBVQzkjXkEK4zg"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36ofwR2BKWvDHE9R2PMTZgcpqCXMCSh"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36op6aXpdqrrguid183dFokfNgWj2LE"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36orZs9Tt9aVQMe3zwwPSDu4V3Y3gSx"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oYfiD1H2dAA6MKyvVypX6t1vzgyn7"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oC3cJ1tq3paRoudv67FzjWAUzUmxk"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oWVvkFWVcDwn9rLrv9ho7hHe8e2Aj"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36odLYXup2aeHu1F8peZ2DLuru8sR4a"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36o4JMpMsFdx5WhWheNmMsJ6RxuQGoi"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oGrLK8EFHxYvjFr3cHKaFaganBc3T"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36ovsMxr7XTwUWTvkKBMNmZqxdAq2Qf"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oNNJu54XVfpp22GQNC52QWbwEkZBu"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oQ9Mo518CZDZYyWpq6NXHBTGdEuz7"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36obh51hvr94tZMYcaaS71ESW7rR4wp"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oiMbbonehWLTk8fSRPvLerKWEuxq9"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oaFt14PGrLKtGjsQCYuk11RWoQseB"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oSPP6eBK3UyFJTQ1gXc2ATi2PptpB"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oJ6tKMi64xNwhGRuZ7tGKkTA2w91L"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36obGd94CMTchB8Ej8Uywb3TXCLABi8"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36ogK8wGoemauLuwxYuzm9ZCaTq8Unv"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oMramh7TjUMuSrbz8XandDKh7sG4L"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oZGt7x4jcWYrvsi3mmUCcJYwj5ycJ"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oG39FLCTXYixrPeM2P1zbnCLyuuWt"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oN4KZvixno5JwpafaUKSN2atbHhJ7"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36od2grzbCZrFbzukcvQSckngZ8P9FT"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oYg1bXP25GpDVPWpWZUkDqbMZeVfT"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36o7kSGthLtqsTnL5SSifSTFzoKqs2Z"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36o8aSPwAN8N7nX3sN7T7B3nxPoNpJU"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36ozBZ3s7Xxt7jHpaf23ypURWC3MtzE"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oCxTaNdWW1L3EzwQfNkg99LZZ399z"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oxvXtSjkLoASTTbYEWLhevb1x83wJ"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oUFiEUdMSn1B2QXhgu3Z3h2TuP9gs"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oLh44w8X1WhRvp4jyVA6vwucBuS2S"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oAuLE9VDuAZF15zQKy43XyRGygdLZ"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oXeTJWGk8yV22jsamGqaUBwn56aKa"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oQrHRKnKmYFKwELP2qGw8hPtSHtSd"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36o4ntQ3aSBS6ZbseCA5qe4TWuuNT61"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oRXmj3hb7oiw1MapLpmdKDdDstMkh"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36osq1Vg9tHk7dyLamkqgsNjMcTRADS"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36ojhQS4mEffzv6rZkNZtpufsW8WFJV"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36ooJ27wqGTbbcaFP46FGwD2BNc6W7D"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36onmRjUtfzkKmNrHpxtA7ugtJgRBEK"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oz6Wa3Jm4Yk82nxwbexW385XgLjer"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36o4NWrvwE2156mpFEkUKFLa9c39MnN"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oiEsJEsu5JXRCNi6Vy5HQRs89kbvJ"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36o2LrskBknTtXBw6ry5aRqLrAizFby"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36ojMQ7wMuYdyJ6PNhpjAva2dkXaByK"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + WriteReactorAddr(std::string("dMd36oVDG9Z3VPaCVC8ZFvfMsai8aJ36VR"), REACTOR_START_TIME, REACTOR_START_TIME + 60 * 60 * 24 * 395, 1000); + + // Year 1 group 2 + WriteReactorAddr(std::string("dMd36oZL8CnJKVbsUxtaGLzMzyofKSFqMn"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oM5h4BFXZ8D9ZAQN8vSRMtKdb4Jnq"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oz34cbiyhrcRKpfmhXKDmsVMKfJ1V"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36o1AtREdscY8HoX42dUCkvhwxsPKoc"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oFgNikhdiv5haAWhcjji3VmHeMz8V"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36o9vbH32MaU2KmMDkNo1JRkdPigLck"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oJ9oxQ8m2i9Jhmab9NtfUe1H7c8yV"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36obgAMMEGckQb1R88kpPpd5EWYwda1"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oknqBxpwNq8TLuCKbDHg7eY8URFaf"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oonaU9gmQZ1CEAhfsUFQ9tPidb8P5"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oW1AFSJAiDXr67r7CCj8E87xuSgGS"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36o7PoLjERuej3zVY6DMoLXVmo3UxCs"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36ogopQ56XyPLNaqccx6g7DwyZHdEgX"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36omJmSu4opY1nTy1yznPUtSgNLZ4PP"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oZmgCiSxXgCjb5QEWrZmG9CupYL5p"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oGEXTyuKc8pF1dTggT3o6xS389Hwm"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36odLauWP47k6VpNMxQha5VGE56Qyhn"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36ooK4j4rQdB81EVhvPWN1yzYHcnpcJ"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oR8hGxPWv398cyao8R5gYz35GxLaf"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oFKAne7u1zJZoEEt8Lx87BoqrBqfx"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36onvuwthQ1Dg6tQiCYpN87jmA7WEpB"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oPKg8uss6FLZuxv2iXQS4DrF3FsPa"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oxP8XjGRtumZ3im6rSkZ42nZuY3A2"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36ofmiUmHHcmwgiwxJterCd9mEZQmRP"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oJjyHf9y842JnoqtQuKpcoYErNzMV"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36o47zSy7bmERdLFdsvkSUaV8VgKkK6"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oGHmnrW5HJnHVnHkWwZMPC4GGdfSQ"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36o3ceJMD3pXJq1yXp1sQhB3dmkTyb6"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oxPUiFraQHEeg9iYB8cL8h7pKqQHm"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + WriteReactorAddr(std::string("dMd36oov9SH3bNG3qB6kr1pz2W77odn7GS"), REACTOR_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), 1000); + + // Year 2 group 1 + WriteReactorAddr(std::string("dMd36oMMnScuNL6qiK9UU6whWWDjYbY4Md"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36orfHpvyqd5mMVWg7pzXidfZhMHAni"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36oRU3itkvwVZaxsnJG7ePkRFTGrZAp"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36oJ2m1tkJdqrmuXHppmfuRJuMFy2QS"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36oxmibF2dCv2yWpR3vjxfudUdUH7m1"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36ou7SXuR4PXATDyatajHF4Uk4zKGTW"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36ozvLn9jxh9kLzyVMce63Rcj8mzKGm"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36oh8Wf7GksZ8byqLYETKx935WjW3cY"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36oiiF4q5UEx38Azmi1yx85y5RTVC6R"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36oCYt7uyDUwQwNSDRsQR3cPPZp68Z8"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36omxRb9FvNVaAmyuqWKubigsqVQVjv"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36opdXiyCg6RHcQMnpAwZr7nW6PnRSF"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36oTczE1XAK4Gy3NE3tEeh9PjzgsK2L"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36ozivzmpCFCZnBy1ZxWvzjajN2AqAf"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36omSPXMQbW21zgbqTQJ8AcuRKN6uzk"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36oux17dbgBHDqJ161L2S11i34FhTtD"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36oLEoziV5B4jspBPTwkcVzwXte1C7k"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36oaZNyvrzczfwGZ6ju6c39PB3LBRmW"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36ovnydN3MjEYE8GrohomUpxZUnJyMB"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36ohd1D7kY1TqmdvVGK2PAG1aTE9DPJ"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36ow47Q29WNcd5X1oT1tTqfhmBCvWLS"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36ovgqEX9pEpYiGTNp9TRtKqvgekMc6"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36ogmCu5SCoveeSTPJwEEaohUPZzH9v"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36o4pyYrTFVmMGamrGDD6hwkYedfNdT"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36oUwLtFviPeZsrZjGoorKT3XFTaSAg"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36oUzfNoqMMnnTKoH4yGRE8346BKGVy"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36o48DmwrzopGBsw99cPUN81TK3CC5D"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36ob7NE9ZHQXfGaQtwusy7f2YkBtsz5"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36oSfWPBZy3T3Xgapi2po28UDj7JRD3"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + WriteReactorAddr(std::string("dMd36o1yiGk56Nv2XCnCJLXwRkAngosWKG"), REACTOR_START_TIME + 60 * 60 * 24 * 365, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + + // Year 2 group 2 + WriteReactorAddr(std::string("dMd36oheiFcrK4Fi1S6YQGyzms5kGQNM6m"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o89VvVB93fZKtdNKJ4ViXMBYbDwYC"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oQR2f6RETor8FcyZwuhTahLDfN1db"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36okQ8hCuTjNHV8XcxEtbW1GoCKwvoT"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oWnuiMcr4X69zKqThs3kqfZyVT9wA"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oqQkXpvbRLJiXQtqirQzmGH5ooZLw"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oY7tsmsaUi2q98LiUt5tQqtqq6n41"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oamoqhXFWpGxsKgrNSiiZ5G1cThoE"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oMWeEPbqMc4fMby82cjf2CcCi2sy1"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oHxBudoHY1vVWn39hYBQgHP6VoT7X"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oS8CGtqkdeFQu2RrexD4tWck7cfC9"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oVHPhNxb4pX9jME7vMQt9UB85WEEf"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ocrqUwJeR3SkfxsVzFQ6RUtVCmzJL"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o2UaDUgmXSmRWRLorfjLR7DekMdTP"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ohPiMqcz4nopVUz36ehj5udyUfLjx"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o5a1jZ9ohgVZDGXxBhLfPCr74T5iA"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ooiDaGpmRBeVSgK1bpi13XqFBsc5p"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oMN7k57YTJcBrnCHHCbpYroe6bk64"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oZRNRaz9SnPWxAusURi1t1RaHBeb7"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36of6RGUmqnceCXiHsEAsjLG7PNJFBY"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oz7bnZYNo9se7SyHW9rczV4KPGVvp"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ox7AXap7SvT55e5bbmTdB6RpcvdXh"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36onaJDAyvLqBcYWjk5DDuGwoqymyAV"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oDVqTUrpQiRh6qxQPNcgNLtCGh22E"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oFgtStDgM78VHcnAvPpuqTRLL7xEj"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o3cuHpUJphfGTAjEyHHu68CuNt5C5"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oKrJiBCVTMtkHhifRTR38zC2A1Nfc"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oFjhgxdDYryhZiqAS3Uh3dcSJhtU8"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o2Lvkwhi4UFhfhiZqH7xakHhiLJQo"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o7V9qmJoruQW5jyhbM3ev5wUYjupg"), REACTOR_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + + // Year 3 group 1 + WriteReactorAddr(std::string("dMd36o2inYGNQsjrbviPTmQHBbGLFd5Dwx"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36o9cQ5LyH5KyzKMip4pEFMkx45F4TF"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36oPXSooCF8WMEovBAAxxPFsM8XCMBy"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36o9Ye7xQFQ7puB218vXwVt6Awvva3r"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36owy3kitfTy5bzM92KtY4WELjKtvTJ"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36o5V9yB8YJtJW5n85WFUw6gbRzZTBZ"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36ouaJLZtEVgcAKEhVHwAYcMAHmVHXy"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36oM5kK86Qp12xZ6MvDowfdJZZqZQ2p"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36oFaK8iJuruezuXUZJDsciTxdmcyGt"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36o8DhzQ8dDMWaGX3oeB4WQKr51mR4U"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36oV6MyiPoy5DCxadXfJyJhff18QrJs"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36o36FwLSR8goPBcUaCG5YpRUZnXdZQ"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36oSd7RcfdYiDhAZoLRhgvHbXdzXyMv"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36oFRUmZ99tx4UBqo49qsSzaVkhi7dz"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36oL83sZtKjGhAn6zhyWxZNfAD5vJxC"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36oUV8QvnZ6cPjigKjZS7F26QVavSYp"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36otpin5u94ZyPCoPqA2gbDvkEFg6iH"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36oEvLPXaKxhYhjxxQMu77NjpkanHnf"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36otyxXs7kt8J27ZmXYpvqVFy22JYBY"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36o3nokGTUfKQm3PQZwqx7D41iWCEwK"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36omMNpP8Lj715tQrfHRdGkFh9QPndE"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36oeEC7ub5sHD6gqmFwg5RmZN9waQ3u"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36oNVVW8UDdZ5qy9JsJkPmFd7gTuhHX"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36o7HGByuyk4emA5E4cGkTtVP1hsRTc"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36oPZYkZtmCZoPSJMo5Dk57mCiiS2DC"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36ofRRYSYywcUSzwaVMSdKkzBHmzLSL"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36o9H6r6m2phZ4uDkk8e9Kxm7okcRvH"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36oh451uBqjfDWxiJNCC8GzUugy4CfK"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36o6AkB9wbJ4VDN4EBZ4q3Q8nkY6Nay"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + WriteReactorAddr(std::string("dMd36ombweBLcCTMweFBWKxLHsk9W4WrAx"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + + // Year 3 group 2 + WriteReactorAddr(std::string("dMd36oZ1uhFvdzP8TULR7b2ETvBZyD16LM"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oAh8GRCPzrgRWC4ykFT4oyrZP3Nks"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o667bZtfxckXP3ocFb1sByKnwAYRa"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oS6CdmBV5dcBHS2csodE7aiN5eJyn"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o13cajnt8gtq7jxejU75KBJrQSqCS"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oW7kaisdjC1ewdmhTqR9wRfMhKRd8"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ogpFLKSrv1tMajgUURatoY9Zsj9M2"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oMJKcSEewgMr3iqH7QtLZwPUvkLfi"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oBbjQMyZNx8s673sFSk1MGCntVJ2P"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oVCrpwDt3Saa4AryTvMDVUeQVFVJ3"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ovZkS4FVg1e4L4hQex6um8Q2qY5oH"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36omJF33uQ9SHY6wjJM1JmqcHzHAj2T"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36opnHw3LwbhssqocSBCYsUqCdfjufk"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oEMBCTA7S3WbfcYrZwwtQx5AtSirw"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36opdZiz8oEV7EA98ANEWjb2hjABBEE"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oTputpQVdXUbbMVf9hBXiY7Da5ZeQ"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oQGdvrXdeXNzpdvj1bKjRngX1Suqw"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oCCKwprSg2Tjqgz1Gukn7ybJ38zUM"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oY4uaSmgMvnYtVzo6nDYVK3dy5of6"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ojibE7GxovWuwyB9f1DjSpH8zZTXP"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ohcPLYU8Dd7xM5FRTEaPmgjaNkLPp"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o3k561mQmQwYGhvdTGFtkK1ngM7ss"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oHX1XDkhepMj8yTLUbJCohBEA4AbH"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oUMSevHtgZSKNtQVwjJFC3dWG6DQs"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o6ZdRoQZAbJxUKdgJKFsnqjNX3ZyB"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oQ11TqmoWacR4nC7931quCfYeCw7W"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oCTVs2SmLhGwHY4bWaoNwbKbNVjm8"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oFoWYo93JMfDm2trtrrKCEbi7YMpF"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oxjuu9wgEZDEeWUciDKx1JM357jbm"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oHf9xxULEVC5R6pu2pKQGtEsRbtvY"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + + // Year 4 group 1 + WriteReactorAddr(std::string("dMd36oBakBHeJ1h7e6xwGjvB1RMAgDP8TT"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oWqbHRQmHKdgsJC6WM527MMrreNNM"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oe5pbTdPpnBptgds6Pf64x2iPcn3Z"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oJ3SByKPUsYAedqyCUVyq3zo8Fdiz"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36o6xDseQLYTpXfSpkjjFmvi6FZCib5"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oU12z2GcPd1rdmxKGtoqgtQdEFX5H"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oS1tNDKjqjFkHRZtLT3gGXpbUx2Ae"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oB1FcA6cdvR6PUymhG1BC9yoEe9s7"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oHPf8WjeinByMptbN68DJpJ5uGU2G"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oGbuZn6LTCGnXQbFcvgSRDsr3hwLE"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oGcvh6pmkEMVpnjan1priLBXV8cgh"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oJeDyA5aJUKtG1x6DEHSG8SWVN6pP"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oV8dZvsXjmtk4trGZRyMsTRxKbJhJ"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oMZaiVXFT2yXKQRbTCozZTSYFXDfs"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36o1qRsAijrzgzp5xQauTnrqpzrvz6q"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36orasqXxbuUQFjesvgedQgpNUJ5xg5"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36o697vqa3Y6veUUauPkcqkX1zYTo9i"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oW2gyNhw3MBzA3zY8PAkUv6oujn6w"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36o1tZnMqX9fsDnFUjFM1kEAFa8Wn1g"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oTJcLsiWHr2CESK3Ag8ybnjLa9Wv3"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oqwXqYU1AAJaYU8tt9fZBifLk551d"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oMZhzk9oEc8EHRsum74BcCg7FCfQJ"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oU44s5uSsPT3GzHupVjiJQwEPU2vk"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oUmzD9rUgGUNKpaBVKLWf4cjSw4GP"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oq3sK1W6hLSLarbnFJCyAVEgKiRTQ"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36o4PJvWJD6aUTfdPxuAF3J5uNnnGCq"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36o1GYFDTovyfQN7cgiZEj2stCZD8pr"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36oT9JZkJz2k5yQg6kqvp7qr7TDPCSu"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36obEHBp8sN2HQMeJFmDkFvTwRZwWxY"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + WriteReactorAddr(std::string("dMd36ooVCZa9VxJP9x1d4zy8S9FVRuCfKr"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + + // Year 4 group 2 + WriteReactorAddr(std::string("dMd36o7GFjHsQCqW1MjnHxyq2LspFGQjfA"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oq7fs93HFV9yXZ79gxcSf8VGJGy2B"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ohY48bw65dLaFVcReuMkmQtdJiqdx"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o3MttJEWJNpBFUH1LJ2wBe43Um8Xr"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oNgRDTYtZm99UWVSWihLEaPhXExKL"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oV7BUS312c6GSMArwMivkT6DSjZXg"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ovoEbC1qEm2xRrNCgANcHJrPcasrE"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oceKzCYQ54Uv7hdRWt6dH1seSz3VA"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36or46U46ZGytF8bSUbxANCZEsYSyUj"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ohMeGEEpvHGG7esB5eMNPQvJztJdN"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oC9bkJtcMFhYrtZQJSAECSkHdsY6z"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36odYhxwonp8LBnL6P7ZdM3qZrDThLK"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ozmEojszv35rkf1S6WYFZYUC6ze2P"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o2S9f8cuAW4k6FGhqqWzU7ZuGD9Qv"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oVuGfGe3ZazYwbNET5BtBpc1cGACN"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oe2j3pL8dH6dzKqbUuMSTnJezHNpP"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oX7LAnfNMPFVfvcJBcDPcJRYUvvRo"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o7pGGn6nf1vmmTvgRtjcwvJE9hCA7"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oDDkNyPFeeVq5N38PvvNry9qctST6"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36odmMdMKV5tRi4EWWGja9jLvmkw219"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o5qRWHb4GxVUj3tyy91N7xPG4PJ7U"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o8FErExgrKWMkUtMfJZmyYNUNvDA4"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36omecJLtDK6NofCrYuWvB4iwnDvBYq"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oBbwariDhR1RBQZndVdHcpoxDkZxT"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oeAVcno4R9u1aauvxef2WWNQX2pwT"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o7UtCYwSdbb5JjxD5x81sR4yJvCQb"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oXam37orGtFTA496LqrErVNWw1ao4"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oTP7mnM93nFnqyhsXa2xmwpo4utWM"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oZt9cqBUY8qEYtjT5STR4FWP3sA4d"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36okrNi5dUxoSYYduEpxTRB5rkoZjJs"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + + // Year 5 group 1 + WriteReactorAddr(std::string("dMd36o9umQgGZxF1PNhGDQGdh1PPig1VrK"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36opPwHvhzbTqZCg6nHsdbFxkKfCBYX"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oKX2jZzvNgCursjvpYd6fxEtryRrK"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oNpjzYvh672hsYkiAAG5XRZmeXq2x"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36ojborkoooUYfyYMasHRJYnPwTbsax"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36osjB34sKpSPD2yjxCSzhQzF1YX3MJ"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oqrgLYoQNj2Dygcxozna3tPu9C18R"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oa49jkBkb9P3Cb7pRLF2ejNNHEc5p"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oKHiG9PU2rWgXHcB5vuRg4WHwY1AB"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oVmSAag5uzT56CdL8KtJUja9ofLjc"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36obzmJqMKMYzLM4rqYPm9foYyupb9A"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36ohTf8FrgkTX9fEybDrD5XRFDZZ1sE"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36o2KEeh93YEZ8cPGkk7sfMXXEkN1as"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oqyyywoaZWDPKranEpWdHnhxApLNN"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oNaBPzwYPeK2bqH6DN6EimUavRV5x"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36ouAHJAQLxpKJ6qQ9ptUE6teAXd1QD"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oL2PDn2pXhc8EwFxDtQf7JqqT5FFB"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36ofwKq8XcMhH2iaQSdg23KXkQhV8Rx"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oW8HZSr4VamfbrD1XwfnQUVsJfVoH"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36o75BBMGSekBvUFKLCgXCFNfUXLFdD"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oWKYDHYvitBmjzYsnRJ2DmjiupfJ1"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36o6kU6Usx93SP42nka5bvr1cVyUV6B"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oYFbMhaB8aYE1WNnXKdXvx4QtN7fX"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36ozf4ngdgUEXjHbZFLEEtX3pi8uk3P"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oEF6YuS4oyi8tMVY3t31QXax3pTNR"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oKq8wizd6Bf57efJTanzC44wTzZcd"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36o4cm6yBeT1rLPKsX4ruv8m1NBzbM6"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36o5qFZW8ksMRX5v3rVZjyN1PtTfNWc"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oes7Y6ZrusSrZzjxRUfvVeDtkHubp"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + WriteReactorAddr(std::string("dMd36oTs7GZqQxBY6nRMffiMDhtVQF44Uj"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + + // Year 5 group 2 + WriteReactorAddr(std::string("dMd36oHTmMzUQEFkzUyrHYBq1ZhFQBov9w"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36onBPfWMvv69EpxNXfwAahBvudWJPJ"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ou7ov8513weh2RQsBNcuHMpbLaQVx"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oWD3yMKA113Fv3dNbtkD7yJH9xZYy"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ouVchHGt5iXdZv2H1fgCoZYb6DxpF"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oy62TV181FbbL9s8AXo4s6KsMNt4x"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oLxiRrCDkkpsuxYvYGbFFeqVod2vc"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oRdof2cqGzrFqCqBFgSaQ7W6em8Ep"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ozSRfjVSU89z5SUaSsAWgx2q4NiJF"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oVn826RT4YddFad1jTq5NtkgtSrwg"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oJeTrfJshm3mhCQ9feNvm2AjM316p"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o3oUqgcTHuoAMd2xhUVqPVcMsAUn1"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oqo6nNHyaUJfHtqu3ZdQRHJCNGAFB"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oH1LRWzkRjuHcf2NdspoacpYsbcmh"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oD314YtkWf6e5hpsnt9cwj6NfoGTB"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oMVhbyvkUbsbcShhuhPHfxNd43KtW"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oJ9KdqGqm1K9c6sx5VKPfScY1YKZC"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oBgAUK25m83sNAGGYmK5pYj7rCvaD"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ob9UQusMEF9dEXvGT3k2je9fXQox9"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36o8r9MC4QBgrLqHrUTpRoWxUhSLjDL"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oh9ttBHQxHrja7859cRqfEKvAkCeF"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ophXvDzifXBrk98D7pZAU6LbTBXcR"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oWR7wtjgaxB6mgWZEMf9wutM1BB8n"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oxScfgJMaHctWbBNZTTkRU75AUSoq"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oCbP7pQytLuDLxeEZu1DgHUwR3Udp"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36ohHhJFXyhSndcemVJmvdaAPmVWFUz"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oERDvd1Vb4qsfT7u6G5XsbV7VP6QY"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oChxkUxQL5GzWbqqsw6GBniEcJFUY"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36owM7A4HyH9cpsq7Xn8igA9pL9CKXJ"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + WriteReactorAddr(std::string("dMd36oq2qhwhYFK9Y62dp5zKFCeYqqgVje"), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + + // Year 6 (1 group) + WriteReactorAddr(std::string("dMd36oMNnpTfRC5BKL1i1uCrtiZb8Ynedn"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36ok1VajGGHaVUcms7dG2W7s5HFkQKe"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oNbX1k9bXvfdh7dpWWH2u5dAiGudb"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36orfGuHp1bgwfjFJWX6Ln1WcwkEx7X"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36of2YXpGR4LD2mENSvQ2WaiMaM2U8H"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36o7K968tsu8d6XyNR8ixbraTtbTm3f"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36okyYkJWJf3koJAtdghPfbCyHDJWqU"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oFer1etuhi8vS1qQweSCtwwekJ56B"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36okGNy7eyiRU7TJUzvcWqoeeWoR4qD"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36ohTnE9LK6aMVefobASfhUAtQG6z5Q"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oH9Vb1sEe5Ac3nEmZQRzWP2WnbsCJ"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oBbWy8a1ddLT8nZsFf4YmGyafGjKh"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36owYp5DYr7eBCdkYbNjSUCeVSCVVvw"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oJEBC68pvXC77rKKC3FcZuGRJPdUD"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oW4crZ8FCCnYsNz1qpSwfidaztRVv"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36o19QGANdBpFipB9dMfFhrfMND6ac5"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36ojooynBmXXSjKXZQhEc9vPoMYU9Qo"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oJZU4ynmrYaz8Pd9ZtMpW9PcALaZC"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36otXFLwz2iHDZ5kbWMFVWRb5qKcATs"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oy4euTaeKHVZW7fJZoCjH146XE2in"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36ohA44QM4pmTBeeA1S8JJ99FWhuAQw"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oib3EdFVavFXBmd8pwQGQWnWBPjuG"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36ofRxGFJw9kDzZym1AqS5Y2zKLuoNh"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oYZ1TAkvka1snB6yG7iNgrhgfqXh1"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oxpnHFCC2hYxiqauPyRYovyr84nZa"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oQPXhfPzTKN5vcQugrWyLea4Z95ct"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oTYh1CperuJBorfrCexRoDXojFuEu"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oT1Vb7HGsgfuRGqr2PRrBgF8Yn7tW"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36oaRy2Pfgxzp1HMYc9nnhN1r2kHkRD"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); + WriteReactorAddr(std::string("dMd36o4YfLwRy1WqoyPc9DXRKkviDsMj5h"), REACTOR_START_TIME + 60 * 60 * 24 * 365 * 5, REACTOR_START_TIME + 60 * 60 * 24 * 365 * 6, 1000); +} + +void CReactorDB::WriteTestReactorDB() { + LOCK(cs); + + WriteReactorDBVersion(CURRENT_REACTOR_VERSION); + + // Year 1 group 1 + WriteReactorAddr(std::string("muLhTBAfaS2ro2fhDFh6D7MArg6qGv1j1i"), REACTOR_TEST_START_TIME, REACTOR_TEST_START_TIME + 60 * 60 * 24 * 365, 1000); + WriteReactorAddr(std::string("n2hXsAStBuJbjyL3QWvW11HG9t4hwPDvNu"), REACTOR_TEST_START_TIME, REACTOR_TEST_START_TIME + 60 * 60 * 24 * 365, 1000); + WriteReactorAddr(std::string("mrCAtCNfhLPfmJt3V4SYjU3Eq7K4eL89hv"), REACTOR_TEST_START_TIME, REACTOR_TEST_START_TIME + 60 * 60 * 24 * 365, 1000); + WriteReactorAddr(std::string("n4j5bHDq7C81xqE5C8mVGapcxANXDp1weh"), REACTOR_TEST_START_TIME, REACTOR_TEST_START_TIME + 60 * 60 * 24 * 365, 1000); + WriteReactorAddr(std::string("mgJ68V5ftC62DhjPDn5UMrR1qrk1a2dNqa"), REACTOR_TEST_START_TIME, REACTOR_TEST_START_TIME + 60 * 60 * 24 * 365, 1000); + + // Year 1 group 2 + WriteReactorAddr(std::string("mpw6YDtRPFayKdnVXdHpqugoqWFeZvt4j8"), REACTOR_TEST_START_TIME + 60 * 60 * 24 * (365 / 2), REACTOR_TEST_START_TIME + 60 * 60 * 24 * (365 + 365 / 2) , 1000); + + // Year 2 group 1 + WriteReactorAddr(std::string("mrQDYWsroDVgjB2R3QtAc38Ka6YxrHQWui"), REACTOR_TEST_START_TIME + 60 * 60 * 24 * 365, REACTOR_TEST_START_TIME + 60 * 60 * 24 * 365 * 2, 1000); + + // Year 2 group 2 + WriteReactorAddr(std::string("mo7meRhKuC2NUCvK6Lg3U2vf2LVc7a61zn"), REACTOR_TEST_START_TIME + 60 * 60 * 24 * (365 + 365 / 2), REACTOR_TEST_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), 1000); + + // Year 3 group 1 + WriteReactorAddr(std::string("mrUemWu2UiKUbBVdfky7iYyivyaB7v8y8v"), REACTOR_TEST_START_TIME + 60 * 60 * 24 * 365 * 2, REACTOR_TEST_START_TIME + 60 * 60 * 24 * 365 * 3, 1000); + + // Year 3 group 2 + WriteReactorAddr(std::string("mzbPu8daUDK96iPcQNWp8aKvqRjyUYeXWG"), REACTOR_TEST_START_TIME + 60 * 60 * 24 * (365 * 2 + (365 / 2)), REACTOR_TEST_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), 1000); + + // Year 4 group 1 + WriteReactorAddr(std::string("moDY5fXrdpGpHramVMisq3PZMfkyvFVXJk"), REACTOR_TEST_START_TIME + 60 * 60 * 24 * 365 * 3, REACTOR_TEST_START_TIME + 60 * 60 * 24 * 365 * 4, 1000); + + // Year 4 group 2 + WriteReactorAddr(std::string("mne2paA6HQqNmbSSMn4MbhGobR9XukBx3S"), REACTOR_TEST_START_TIME + 60 * 60 * 24 * (365 * 3 + (365 / 2)), REACTOR_TEST_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), 1000); + + // Year 5 group 1 + WriteReactorAddr(std::string("n2vgS6NTvYXDLufLeuz4ZSXFCSwddCnEt2"), REACTOR_TEST_START_TIME + 60 * 60 * 24 * 365 * 4, REACTOR_TEST_START_TIME + 60 * 60 * 24 * 365 * 5, 1000); + + // Year 5 group 2 + WriteReactorAddr(std::string("n2ZGE24BDNMAe4yRwXSNvkV2d45HuW2PE2"), REACTOR_TEST_START_TIME + 60 * 60 * 24 * (365 * 4 + (365 / 2)), REACTOR_TEST_START_TIME + 60 * 60 * 24 * (365 * 5 + (365 / 2)), 1000); + + // Legendaries (only list max here), no shutoff time. + WriteReactorAddr(std::string("mkKzRqz4hQNM3re3wZrcGnsMRRJPudPsQu"), REACTOR_TEST_START_TIME, -1, 15000); + + // Foundation, variant 1 + WriteReactorAddr(std::string("mgCRA4ZXqaB8GBwb2AN2EaKL476caTdf26"), REACTOR_TEST_START_TIME, -1, 10000); + + // Foundation, variant 2 + WriteReactorAddr(std::string("mkB4XAhFZmG5rNuhs4mnnBFWN3jKD2rXw2"), REACTOR_TEST_START_TIME, -1, 100); +} diff --git a/src/reactors.cpp b/src/reactors.cpp new file mode 100644 index 0000000..384ff55 --- /dev/null +++ b/src/reactors.cpp @@ -0,0 +1,205 @@ +// Copyright (c) 2015-2016 Nathan Bass "IngCr3at1on" +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "base58.h" +#include "util.h" +#include "reactors.h" + +using namespace std; + +int CURRENT_REACTOR_VERSION = 1; +unsigned int REACTOR_FIX_TIME = 1466337600; // 19 Jun 2016 12:00:00 UTC +unsigned int REACTOR_TEST_FIX_TIME = 1464123019; // Tue, 24 May 2016 20:50:19 GMT + +bool maybeWipeReactorDB(string strFileName) { + int dbversion; + if (CReactorDB(strFileName).CheckReactorDBVersion(dbversion)) { + if (dbversion == CURRENT_REACTOR_VERSION) + return false; + } + + // No version set or version is invalid, wipe the DB. + printf("maybeWipeReactorDB() : reactor databse is outdated, removing for recreation\n"); + CReactorDB(strFileName).Close(); + bitdb.CloseDb("reactors.dat"); + boost::filesystem::remove(strFileName); + // Return true so we know to re-inflate the DB. + return true; +} + +/* This function should not be called by tests (is not currently required for + * them) as it will use the reactor.dat found in the datadir instead of the + * mockdb. */ +void InitReactors() { + bool inflatedb = false; + // Check if the reactor database exists and check the version if it does. + string reactordbfile = GetReactorDBFile(); + if (boost::filesystem::exists(boost::filesystem::path(reactordbfile))) { + if (maybeWipeReactorDB(reactordbfile)) + inflatedb = true; + + if (!inflatedb) { + // TODO Replace with correct first and last reactor addresses. + if (!fTestNet && (!CReactorDB(reactordbfile).CheckReactorAddr(string("dasHERZmwgtvWGNRxs55GPrXsAKwY7bX85")) + || !CReactorDB(reactordbfile).CheckReactorAddr(string("dMd36o4YfLwRy1WqoyPc9DXRKkviDsMj5h")))) + inflatedb = true; + else if (fTestNet && (!CReactorDB(reactordbfile).CheckReactorAddr(string("muLhTBAfaS2ro2fhDFh6D7MArg6qGv1j1i")) + || !CReactorDB(reactordbfile).CheckReactorAddr(string("mkB4XAhFZmG5rNuhs4mnnBFWN3jKD2rXw2")))) + inflatedb = true; + } + } else { + inflatedb = true; + } + + if (inflatedb) + InflateReactorDB(reactordbfile); +} + +bool CTransaction::IsReactorStake(string strFileName, CScript scriptPubKeyType, CScript scriptPubKeyAddress, unsigned int nTime, int64 nValueIn, int64 nValueOut, uint64 nCoinAge, unsigned int nBits, int nHeight) { + int64 nStakeReward = nValueOut - nValueIn; + + // Should never be anything else. + if (scriptPubKeyType[0] == OP_REACTOR) { + unsigned int valid_starting; + unsigned int valid_until; + int64 reactorStakeValue; + + /* Destination address is stored in the CScript for vout[1] of the input + * so extract it against our database. */ + if (!CReactorDB(strFileName).IsReactor(strFileName, scriptPubKeyAddress, reactorStakeValue, valid_starting, valid_until)) + return DoS(100, error("IsReactorStake() : invalid reactor address, HAXORS!!!")); + + if (nTime < valid_starting) + return DoS(100, error("IsReactorStake() : reactor is not valid yet")); + + if (nTime >= valid_until && valid_until != (unsigned int)-1) + return DoS(100, error("IsReactorStake() : reactor is expired")); + + /* If this is a reactor check that the size of the stake matches the + * requirements of the given reactor.*/ + if (nValueIn < (reactorStakeValue * COIN)) + return DoS(100, error("IsReactorStake() : credit doesn't meet requirement for reactor stake; credit %" PRI64d "; reactor size %" PRI64d "", nValueIn, reactorStakeValue)); + + if (nValueIn > ((reactorStakeValue * COIN * 2) - 1 * COIN)) + return DoS(100, error("IsReactorStake() : credit exceeds value of reactor; credit %" PRI64d "; reactor size %" PRI64d "", nValueIn, reactorStakeValue)); + + if (nStakeReward > GetProofOfStakeReward(nCoinAge, nBits, nTime, nHeight, GetReactorRate(reactorStakeValue, nValueIn)) - GetMinFee() + MIN_TX_FEE) + return DoS(100, error("IsReactorStake() : %s stake reward exceeded", GetHash().ToString().substr(0, 10).c_str())); + + return true; + } + + return false; +} + +// Inflate the reactor table in the reactorDB +void InflateReactorDB(string strFileName) { + printf("InflateReactorDB() : inflating reactor database.\n"); + + fTestNet ? CReactorDB(strFileName, "cw+").WriteTestReactorDB() : CReactorDB(strFileName, "cw+").WriteReactorDB(); +} + +float GetReactorRate(int64 reactorStakeValue, int64 nValueIn) { + unsigned int fix_time = REACTOR_FIX_TIME; + if (fTestNet) + fix_time = REACTOR_TEST_FIX_TIME; + +if(reactorStakeValue == 100) { + if(GetTime() > fix_time) return(1.20); + else return(1.05); +} + + if (reactorStakeValue == 1000 || reactorStakeValue == 10000) + return 2; + + /* Originally the spec for the legendaries made it sound like they could + * have one or the other value (instead of being set to one specifically), + * this segment of code was overlooked in previous updates... + * While this check is probably not necessary we'll do it anyway as a sanity + * check to ensure that we don't inadvertently cause mini-forks. */ + if (GetTime() < fix_time) { + if (reactorStakeValue == 15000 && nValueIn == 3000 * COIN) + return 2; + } else { + if (reactorStakeValue == 3000) + return 2; + } + + if (GetTime() < fix_time) { + if (reactorStakeValue == 15000 && nValueIn == 15000 * COIN) + return 1.25; + } else { + if (reactorStakeValue == 15000) + return 1.25; + } + + return 0; +} + +int64 GetAdjustedCoinYear(int64 nRewardCoinYear, unsigned int nTime, float reactorRate) { + unsigned int fix_time = REACTOR_FIX_TIME; + if (fTestNet) + fix_time = REACTOR_TEST_FIX_TIME; + /* For 30 days after the fix_time we want to boost the normal stake rate + * by 30%. */ + if (nTime >= fix_time && nTime < fix_time+(60*60*24*30)) { + nRewardCoinYear = 30 * CENT; + } + /* If the reactor rate is greater than 0 adjust the nRewardCoinYear by + * the given rate. */ + if (reactorRate > 0) { + // If prior to fix cast reactorRate to int to avoid mini-forks. + if (nTime < fix_time) { + nRewardCoinYear = nRewardCoinYear * (int)reactorRate; + } else { + nRewardCoinYear = nRewardCoinYear * reactorRate; + } + } + + return nRewardCoinYear; +} + +bool CReactorDB::WriteReactorDBVersion(int version) +{ + return Write(string("dbversion"), version); +} + +bool CReactorDB::WriteReactorAddr(const string address, unsigned int valid_starting, unsigned int valid_until, int64 reactorStakeValue) +{ + return Write(make_pair(string("reactor"), address), boost::make_tuple(valid_starting, valid_until, reactorStakeValue)); +} + +bool CReactorDB::CheckReactorDBVersion(int &version) +{ + if (!Exists(string("dbversion"))) + return false; + + Read(string("dbversion"), version); + return true; +} + +bool CReactorDB::CheckReactorAddr(const string address) +{ + return Exists(make_pair(string("reactor"), address)); +} + +bool CReactorDB::IsReactor(std::string strFileName, CScript scriptPubKeyAddress, int64 &reactorStakeValue, unsigned int &valid_starting, unsigned int &valid_until) +{ + CTxDestination address; + ExtractDestination(scriptPubKeyAddress, address); + CBitcoinAddress addr(address); + + // Check if the address exists in the reactor database. + if (!CReactorDB(strFileName).CheckReactorAddr(addr.ToString())) + return false; + + boost::tuple t; + Read(make_pair(string("reactor"), addr.ToString()), t); + + valid_starting = t.get<0>(); + valid_until = t.get<1>(); + + reactorStakeValue = t.get<2>(); + return true; +} diff --git a/src/reactors.h b/src/reactors.h new file mode 100644 index 0000000..dc2ade1 --- /dev/null +++ b/src/reactors.h @@ -0,0 +1,52 @@ +// Copyright (c) 2015-2016 Nathan Bass "IngCr3at1on" +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef REACTORS_H +#define REACTORS_H + +#include "db.h" +#include "script.h" + +extern int CURRENT_REACTOR_VERSION; +extern unsigned int REACTOR_START_TIME; + +void InitReactors(); + +class CReactorDB : public CDB +{ +public: + CReactorDB(std::string strFileName, const char *pszMode="r+") : CDB(strFileName.c_str(), pszMode) { } +private: + mutable CCriticalSection cs; + + CReactorDB(const CReactorDB&); + void operator=(const CReactorDB); + + bool WriteReactorAddr(const std::string /*address*/, unsigned int /*valid_starting*/, unsigned int /*valid_until*/, int64 /*reactorStakeValue*/); + bool WriteReactorDBVersion(int /*version*/); +public: + void WriteReactorDB(); + void WriteTestReactorDB(); + + bool CheckReactorDBVersion(int &/*version*/); + + bool IsReactor(std::string /*strFileName*/, CScript /*scriptPubKeyAddress*/, int64 &/*reactorStakeValue*/, unsigned int &/*valid_starting*/, unsigned int &/*valid_until*/); + bool CheckReactorAddr(const std::string /*address*/); +}; + +/* Pass the filename instead of getting it in the function so that we can + * properly run tests in a mock db instead of using the db in the datadir.*/ +void InflateReactorDB(std::string strFileName); + +// Get the reactorRate (see inline comments for why nValueIn is needed). +float GetReactorRate(int64 /*reactorStakeValue*/, int64 /*nValueIn*/); +/* Get the adjusted coin year based on the reactorRate (depending on the time + * in relation to REACTOR_FIX_TIME. */ +int64 GetAdjustedCoinYear(int64 /*nRewardCoinYear*/, unsigned int /*nTime*/, float /*reactorRate*/); + +inline std::string GetReactorDBFile() { + return (GetDataDir() / "reactors.dat").string(); +} + +#endif // REACTORS_H diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index c9f9b35..4f82ef9 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -5,6 +5,7 @@ #include "main.h" #include "bitcoinrpc.h" +#include "auxpow.h" using namespace json_spirit; using namespace std; @@ -42,17 +43,40 @@ double GetDifficulty(const CBlockIndex* blockindex) return dDiff; } - int64 GetTotalCoin() { return pindexBest->nMoneySupply / COIN; } +double GetPoSKernelPS() +{ + int nPoSInterval = 72; + double dStakeKernelsTriedAvg = 0; + int nStakesHandled = 0, nStakesTime = 0; + + CBlockIndex* pindex = pindexBest;; + CBlockIndex* pindexPrevStake = NULL; + + while (pindex && nStakesHandled < nPoSInterval) + { + if (pindex->IsProofOfStake()) + { + dStakeKernelsTriedAvg += GetDifficulty(pindex) * 4294967296.0; + nStakesTime += pindexPrevStake ? (pindexPrevStake->nTime - pindex->nTime) : 0; + pindexPrevStake = pindex; + nStakesHandled++; + } + + pindex = pindex->pprev; + } + + return nStakesTime ? dStakeKernelsTriedAvg / nStakesTime : 0; +} Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool fPrintTransactionDetail) { Object result; - result.push_back(Pair("hash", block.GetHash().GetHex())); + result.push_back(Pair("hash", block.GetHash(true).GetHex())); CMerkleTx txGen(block.vtx[0]); txGen.SetMerkleBranch(&block); result.push_back(Pair("confirmations", (int)txGen.GetDepthInMainChain())); diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index 89078bc..df5400e 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -47,8 +47,8 @@ Value importprivkey(const Array& params, bool fHelp) bool fGood = vchSecret.SetString(strSecret); if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); - if (fWalletUnlockMintOnly) // ppcoin: no importprivkey in mint-only mode - throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for minting only."); + + EnsureWalletIsUnlocked(); CKey key; bool fCompressed; @@ -82,8 +82,9 @@ Value dumpprivkey(const Array& params, bool fHelp) CBitcoinAddress address; if (!address.SetString(strAddress)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Diamond address"); - if (fWalletUnlockMintOnly) // ppcoin: no dumpprivkey in mint-only mode - throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for minting only."); + + EnsureWalletIsUnlocked(); + CKeyID keyID; if (!address.GetKeyID(keyID)) throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 1565e8d..4106536 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -7,6 +7,7 @@ #include "db.h" #include "init.h" #include "bitcoinrpc.h" +#include "auxpow.h" using namespace json_spirit; using namespace std; @@ -70,15 +71,16 @@ Value getmininginfo(const Array& params, bool fHelp) Object obj; obj.push_back(Pair("blocks", (int)nBestHeight)); - obj.push_back(Pair("currentblocksize",(uint64_t)nLastBlockSize)); - obj.push_back(Pair("currentblocktx",(uint64_t)nLastBlockTx)); + obj.push_back(Pair("currentblocksize",(boost::uint64_t)nLastBlockSize)); + obj.push_back(Pair("currentblocktx",(boost::uint64_t)nLastBlockTx)); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("errors", GetWarnings("statusbar"))); obj.push_back(Pair("generate", GetBoolArg("-gen"))); obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); obj.push_back(Pair("hashespersec", gethashespersec(params, false))); - obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); - obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); + obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); + obj.push_back(Pair("pooledtx", (boost::uint64_t)mempool.size())); + obj.push_back(Pair("stakepower", (boost::uint64_t)pwalletMain->GetStakeMintPower(*pwalletMain))); obj.push_back(Pair("testnet", fTestNet)); return obj; } @@ -118,22 +120,24 @@ Value getnetworkhashps(const Array& params, bool fHelp) } -Value getworkex(const Array& params, bool fHelp) +Value getwork(const Array& params, bool fHelp) { - if (fHelp || params.size() > 2) + if (fHelp || params.size() > 1) throw runtime_error( - "getworkex [data, coinbase]\n" - "If [data, coinbase] is not specified, returns extended work data.\n" - ); + "getwork [data]\n" + "If [data] is not specified, returns formatted hash data to work on:\n" + " \"data\" : block data\n" + " \"target\" : little endian hash target\n" + "If [data] is specified, tries to solve the block and returns true if it was successful."); if (vNodes.empty()) - throw JSONRPCError(-9, "DiamondCoin is not connected!"); + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Diamond is not connected!"); if (IsInitialBlockDownload()) - throw JSONRPCError(-10, "DiamondCoin is downloading blocks..."); + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Diamond is downloading blocks..."); typedef map > mapNewBlock_t; - static mapNewBlock_t mapNewBlock; + static mapNewBlock_t mapNewBlock; // FIXME: thread safety static vector vNewBlock; static CReserveKey reservekey(pwalletMain); @@ -155,19 +159,27 @@ Value getworkex(const Array& params, bool fHelp) delete pblock; vNewBlock.clear(); } + + // Clear pindexPrev so future getworks make a new block, despite any failures from here on + pindexPrev = NULL; + + // Store the pindexBest used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = nTransactionsUpdated; - pindexPrev = pindexBest; + CBlockIndex* pindexPrevNew = pindexBest; nStart = GetTime(); // Create new block pblock = CreateNewBlock(pwalletMain); if (!pblock) - throw JSONRPCError(-7, "Out of memory"); + throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); vNewBlock.push_back(pblock); + + // Need to update only after we know CreateNewBlock succeeded + pindexPrev = pindexPrevNew; } // Update nTime - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->UpdateTime(pindexPrev); pblock->nNonce = 0; // Update nExtraNonce @@ -177,48 +189,22 @@ Value getworkex(const Array& params, bool fHelp) // Save mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig); - // Prebuild hash buffers - char pmidstate[32]; char pdata[128]; - char phash1[64]; - FormatHashBuffers(pblock, pmidstate, pdata, phash1); + FormatDataBuffer(pblock, (uint *) pdata); uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - CTransaction coinbaseTx = pblock->vtx[0]; - std::vector merkle = pblock->GetMerkleBranch(0); - Object result; result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); - - CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - ssTx << coinbaseTx; - result.push_back(Pair("coinbase", HexStr(ssTx.begin(), ssTx.end()))); - - Array merkle_arr; - - BOOST_FOREACH(uint256 merkleh, merkle) { - merkle_arr.push_back(HexStr(BEGIN(merkleh), END(merkleh))); - } - - result.push_back(Pair("merkle", merkle_arr)); - - return result; } else { // Parse parameters vector vchData = ParseHex(params[0].get_str()); - vector coinbase; - - if(params.size() == 2) - coinbase = ParseHex(params[1].get_str()); - if (vchData.size() != 128) - throw JSONRPCError(-8, "Invalid parameter"); - + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); CBlock* pdata = (CBlock*)&vchData[0]; // Byte reverse @@ -232,12 +218,7 @@ Value getworkex(const Array& params, bool fHelp) pblock->nTime = pdata->nTime; pblock->nNonce = pdata->nNonce; - - if(coinbase.size() == 0) - pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; - else - CDataStream(coinbase, SER_NETWORK, PROTOCOL_VERSION) >> pblock->vtx[0]; // FIXME - HACK! - + pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; pblock->hashMerkleRoot = pblock->BuildMerkleTree(); if (!pblock->SignBlock(*pwalletMain)) @@ -247,39 +228,51 @@ Value getworkex(const Array& params, bool fHelp) } } +Value getworkaux(const Array& params, bool fHelp) { + unsigned int i; -Value getwork(const Array& params, bool fHelp) -{ - totalCoin = GetTotalCoin(); - if (fHelp || params.size() > 1) + if (fHelp || params.size() < 1) throw runtime_error( - "getwork [data]\n" - "If [data] is not specified, returns formatted hash data to work on:\n" - " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated + "getworkaux \n" + "getworkaux '' \n" + "getworkaux 'submit' \n" + "getworkaux '' *\n" + " get work with auxiliary data in coinbase, for multichain mining\n" + " is the merkle root of the auxiliary chain block hashes, concatenated with the aux chain merkle tree size and a nonce\n" + " is the aux chain index in the aux chain merkle tree\n" + " is the optional merkle branch of the aux chain\n" + "If is not specified, returns formatted hash data to work on:\n" " \"data\" : block data\n" - " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated " \"target\" : little endian hash target\n" - "If [data] is specified, tries to solve the block and returns true if it was successful."); + "If is specified and 'submit', tries to solve the block for this (parent) chain and returns true if it was successful." + "If is specified and empty first argument, returns the aux merkle root, with size and nonce." + "If and are specified, creates an auxiliary proof of work for the chain specified and returns:\n" + " \"aux\" : merkle root of auxiliary chain block hashes\n" + " \"auxpow\" : aux proof of work to submit to aux chain\n" + ); - if (vNodes.empty()) - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "DiamondCoin is not connected!"); + if(vNodes.empty()) + throw(JSONRPCError(-9, "Diamond is not connected!")); - if (IsInitialBlockDownload()) - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "DiamondCoin is downloading blocks..."); + if(IsInitialBlockDownload()) + throw(JSONRPCError(-10, "Diamond is downloading blocks...")); - typedef map > mapNewBlock_t; - static mapNewBlock_t mapNewBlock; // FIXME: thread safety + static map > mapNewBlock; static vector vNewBlock; static CReserveKey reservekey(pwalletMain); - if (params.size() == 0) + if (params.size() == 1) { + static vector vchAuxPrev; + vector vchAux = ParseHex(params[0].get_str()); + // Update block static unsigned int nTransactionsUpdatedLast; static CBlockIndex* pindexPrev; static int64 nStart; static CBlock* pblock; if (pindexPrev != pindexBest || + vchAux != vchAuxPrev || (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) { if (pindexPrev != pindexBest) @@ -290,23 +283,16 @@ Value getwork(const Array& params, bool fHelp) delete pblock; vNewBlock.clear(); } - - // Clear pindexPrev so future getworks make a new block, despite any failures from here on - pindexPrev = NULL; - - // Store the pindexBest used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; + pindexPrev = pindexBest; + vchAuxPrev = vchAux; nStart = GetTime(); // Create new block pblock = CreateNewBlock(pwalletMain); if (!pblock) - throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); + throw JSONRPCError(-7, "Out of memory"); vNewBlock.push_back(pblock); - - // Need to update only after we know CreateNewBlock succeeded - pindexPrev = pindexPrevNew; } // Update nTime @@ -315,55 +301,199 @@ Value getwork(const Array& params, bool fHelp) // Update nExtraNonce static unsigned int nExtraNonce = 0; - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + IncrementExtraNonceWithAux(pblock, pindexPrev, nExtraNonce, vchAux); // Save - mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig); + mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, nExtraNonce); - // Pre-build hash buffers - char pmidstate[32]; char pdata[128]; - char phash1[64]; - FormatHashBuffers(pblock, pmidstate, pdata, phash1); + FormatDataBuffer(pblock, (uint *) pdata); uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); Object result; - result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); - result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); return result; } else { + if (params[0].get_str() != "submit" && params[0].get_str() != "") + throw JSONRPCError(-8, " must be the empty string or 'submit' if work is being submitted"); // Parse parameters - vector vchData = ParseHex(params[0].get_str()); + vector vchData = ParseHex(params[1].get_str()); if (vchData.size() != 128) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); + throw JSONRPCError(-8, "Invalid parameter"); CBlock* pdata = (CBlock*)&vchData[0]; // Byte reverse - for (int i = 0; i < 128/4; i++) + for(i = 0; i < 128/4; i++) ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]); // Get saved block if (!mapNewBlock.count(pdata->hashMerkleRoot)) return false; CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; + unsigned int nExtraNonce = mapNewBlock[pdata->hashMerkleRoot].second; pblock->nTime = pdata->nTime; pblock->nNonce = pdata->nNonce; - pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; + + // Get the aux merkle root from the coinbase + CScript script = pblock->vtx[0].vin[0].scriptSig; + opcodetype opcode; + CScript::const_iterator pc = script.begin(); + script.GetOp(pc, opcode); + script.GetOp(pc, opcode); + script.GetOp(pc, opcode); + if (opcode != OP_2) + throw runtime_error("invalid aux pow script"); + vector vchAux; + script.GetOp(pc, opcode, vchAux); + + RemoveMergedMiningHeader(vchAux); + + static CBlockIndex* pindexPrev; + pblock->vtx[0].vin[0].scriptSig = MakeCoinbaseWithAux(pindexPrev->nHeight + 1, nExtraNonce, vchAux); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - if (!pblock->SignBlock(*pwalletMain)) - throw JSONRPCError(-100, "Unable to sign block, wallet locked?"); + if (params.size() > 2) + { + // Requested aux proof of work + int nChainIndex = params[2].get_int(); - return CheckWork(pblock, *pwalletMain, reservekey); + CAuxPow pow(pblock->vtx[0]); + + for(i = 3 ; i < params.size() ; i++) { + uint256 nHash; + nHash.SetHex(params[i].get_str()); + pow.vChainMerkleBranch.push_back(nHash); + } + + pow.SetMerkleBranch(pblock); + pow.nChainIndex = nChainIndex; + pow.parentBlock = *pblock; + CDataStream ss(SER_GETHASH | SER_BLOCKHEADERONLY, PROTOCOL_VERSION); + ss << pow; + Object result; + result.push_back(Pair("auxpow", HexStr(ss.begin(), ss.end()))); + return result; + } + else + { + if (params[0].get_str() == "submit") + { + return CheckWork(pblock, *pwalletMain, reservekey); + } + else + { + Object result; + result.push_back(Pair("aux", HexStr(vchAux.begin(), vchAux.end()))); + result.push_back(Pair("hash", pblock->GetHashGroestl().GetHex())); + return result; + } + } } } +Value getauxblock(const Array& params, bool fHelp) +{ + if (fHelp || (params.size() != 0 && params.size() != 2)) + throw runtime_error( + "getauxblock [ ]\n" + " create a new block" + "If , is not specified, returns a new block hash.\n" + "If , is specified, tries to solve the block based on " + "the aux proof of work and returns true if it was successful."); + + if (vNodes.empty()) + throw JSONRPCError(-9, "Diamond is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(-10, "Diamond is downloading blocks..."); + + static map mapNewBlock; + static vector vNewBlock; + static CReserveKey reservekey(pwalletMain); + + if (params.size() == 0) + { + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64 nStart; + static CBlock* pblock; + if (pindexPrev != pindexBest || + (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) + { + if (pindexPrev != pindexBest) + { + // Deallocate old blocks since they're obsolete now + mapNewBlock.clear(); + BOOST_FOREACH(CBlock* pblock, vNewBlock) + delete pblock; + vNewBlock.clear(); + } + nTransactionsUpdatedLast = nTransactionsUpdated; + pindexPrev = pindexBest; + nStart = GetTime(); + + // Create new block with nonce = 0 and extraNonce = 1 + pblock = CreateNewBlock(pwalletMain); + + // Update nTime + pblock->UpdateTime(pindexPrev); + pblock->nNonce = 0; + + // Update nExtraNonce + static unsigned int nExtraNonce = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + + // Sets the version + pblock->SetAuxPow(new CAuxPow()); + + // Save + mapNewBlock[pblock->GetHashGroestl()] = pblock; + + if (!pblock) + throw JSONRPCError(-7, "Out of memory"); + vNewBlock.push_back(pblock); + } + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + Object result; + result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); + result.push_back(Pair("hash", pblock->GetHashGroestl().GetHex())); + result.push_back(Pair("chainid", pblock->GetChainID())); + return result; + } + else + { + uint256 hash; + hash.SetHex(params[0].get_str()); + vector vchAuxPow = ParseHex(params[1].get_str()); + CDataStream ss(vchAuxPow, SER_GETHASH | SER_BLOCKHEADERONLY, PROTOCOL_VERSION); + CAuxPow* pow = new CAuxPow(); + try { + ss >> *pow; + } + catch(std::exception &e) { + throw(JSONRPCError(RPC_DESERIALIZATION_ERROR, "AuxPoW decode failed")); + } + + if (!mapNewBlock.count(hash)) + return ::error("getauxblock() : block not found"); + + CBlock* pblock = mapNewBlock[hash]; + pblock->SetAuxPow(pow); + + if(!CheckWork(pblock, *pwalletMain, reservekey)) + return(false); + + return(true); + } +} Value getblocktemplate(const Array& params, bool fHelp) { @@ -395,131 +525,149 @@ Value getblocktemplate(const Array& params, bool fHelp) if (modeval.type() == str_type) strMode = modeval.get_str(); else if (modeval.type() == null_type) - { - /* Do nothing */ - } + strMode = "template"; else throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); } - if (strMode != "template") - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); - - if (vNodes.empty()) - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "DiamondCoin is not connected!"); - - if (IsInitialBlockDownload()) - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "DiamondCoin is downloading blocks..."); - - static CReserveKey reservekey(pwalletMain); - - // Update block - static unsigned int nTransactionsUpdatedLast; - static CBlockIndex* pindexPrev; - static int64 nStart; - static CBlock* pblock; - if (pindexPrev != pindexBest || - (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) + if (strMode == "template") { - // Clear pindexPrev so future calls make a new block, despite any failures from here on - pindexPrev = NULL; - - // Store the pindexBest used before CreateNewBlock, to avoid races - nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; - nStart = GetTime(); - - // Create new block - if(pblock) - { - delete pblock; - pblock = NULL; + if (vNodes.empty()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Diamond is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Diamond is downloading blocks..."); + + static CReserveKey reservekey(pwalletMain); + + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64 nStart; + static CBlock* pblock; + if (pindexPrev != pindexBest || + (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) + { + // Clear pindexPrev so future calls make a new block, despite any failures from here on + pindexPrev = NULL; + + // Store the pindexBest used before CreateNewBlock, to avoid races + nTransactionsUpdatedLast = nTransactionsUpdated; + CBlockIndex* pindexPrevNew = pindexBest; + nStart = GetTime(); + + // Create new block + if(pblock) + { + delete pblock; + pblock = NULL; + } + pblock = CreateNewBlock(pwalletMain); + if (!pblock) + throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); + + // Need to update only after we know CreateNewBlock succeeded + pindexPrev = pindexPrevNew; + } + + // Update nTime + pblock->UpdateTime(pindexPrev); + pblock->nNonce = 0; + + Array transactions; + map setTxIndex; + int i = 0; + CTxDB txdb("r"); + BOOST_FOREACH (CTransaction& tx, pblock->vtx) + { + uint256 txHash = tx.GetHash(); + setTxIndex[txHash] = i++; + + if (tx.IsCoinBase() || tx.IsCoinStake()) + continue; + + Object entry; + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << tx; + entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end()))); + + entry.push_back(Pair("hash", txHash.GetHex())); + + MapPrevTx mapInputs; + map mapUnused; + bool fInvalid = false; + if (tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) + { + entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut()))); + + Array deps; + BOOST_FOREACH (MapPrevTx::value_type& inp, mapInputs) + { + if (setTxIndex.count(inp.first)) + deps.push_back(setTxIndex[inp.first]); + } + entry.push_back(Pair("depends", deps)); + + int64_t nSigOps = tx.GetLegacySigOpCount(); + nSigOps += tx.GetP2SHSigOpCount(mapInputs); + entry.push_back(Pair("sigops", nSigOps)); + } + + transactions.push_back(entry); + } + + Object aux; + aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + static Array aMutable; + if (aMutable.empty()) + { + aMutable.push_back("time"); + aMutable.push_back("transactions"); + aMutable.push_back("prevblock"); + } + + Object result; + result.push_back(Pair("version", pblock->nVersion)); + result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); + result.push_back(Pair("transactions", transactions)); + result.push_back(Pair("coinbaseaux", aux)); + if((fTestNet && (pindexPrev->nHeight + 1 < nTestStage1)) || + (!fTestNet && ((totalCoin > VALUE_CHANGE) && (pindexPrev->nHeight + 1 < nLiveFork1)))) { + result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue + + (int64_t)pblock->vtx[0].vout[1].nValue)); + } else { + result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); } - pblock = CreateNewBlock(pwalletMain); - if (!pblock) - throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); - - // Need to update only after we know CreateNewBlock succeeded - pindexPrev = pindexPrevNew; + result.push_back(Pair("target", hashTarget.GetHex())); + result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); + result.push_back(Pair("mutable", aMutable)); + result.push_back(Pair("noncerange", "00000000ffffffff")); + result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); + result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); + result.push_back(Pair("curtime", (int64_t)pblock->nTime)); + result.push_back(Pair("bits", HexBits(pblock->nBits))); + result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); + + return result; } - - // Update nTime - pblock->UpdateTime(pindexPrev); - pblock->nNonce = 0; - - Array transactions; - map setTxIndex; - int i = 0; - CTxDB txdb("r"); - BOOST_FOREACH (CTransaction& tx, pblock->vtx) + else + if (strMode == "submit") { - uint256 txHash = tx.GetHash(); - setTxIndex[txHash] = i++; - - if (tx.IsCoinBase() || tx.IsCoinStake()) - continue; - - Object entry; - - CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - ssTx << tx; - entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end()))); - - entry.push_back(Pair("hash", txHash.GetHex())); - - MapPrevTx mapInputs; - map mapUnused; - bool fInvalid = false; - if (tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) - { - entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut()))); - - Array deps; - BOOST_FOREACH (MapPrevTx::value_type& inp, mapInputs) - { - if (setTxIndex.count(inp.first)) - deps.push_back(setTxIndex[inp.first]); - } - entry.push_back(Pair("depends", deps)); - - int64_t nSigOps = tx.GetLegacySigOpCount(); - nSigOps += tx.GetP2SHSigOpCount(mapInputs); - entry.push_back(Pair("sigops", nSigOps)); - } - - transactions.push_back(entry); - } - - Object aux; - aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); + // Parse parameters + const Object& oparam = params[0].get_obj(); + CDataStream ssBlock(ParseHex(find_value(oparam, "data").get_str()), SER_NETWORK, PROTOCOL_VERSION); + CBlock pblock; + ssBlock >> pblock; - uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + bool fAccepted = ProcessBlock(NULL, &pblock); - static Array aMutable; - if (aMutable.empty()) - { - aMutable.push_back("time"); - aMutable.push_back("transactions"); - aMutable.push_back("prevblock"); + return fAccepted ? Value::null : "rejected"; } - - Object result; - result.push_back(Pair("version", pblock->nVersion)); - result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); - result.push_back(Pair("transactions", transactions)); - result.push_back(Pair("coinbaseaux", aux)); - result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); - result.push_back(Pair("target", hashTarget.GetHex())); - result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); - result.push_back(Pair("mutable", aMutable)); - result.push_back(Pair("noncerange", "00000000ffffffff")); - result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); - result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); - result.push_back(Pair("curtime", (int64_t)pblock->nTime)); - result.push_back(Pair("bits", HexBits(pblock->nBits))); - result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); - - return result; + throw JSONRPCError(-8, "Invalid mode"); } Value submitblock(const Array& params, bool fHelp) diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 0813849..813cede 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2015-2016 Nathan Bass "IngCr3at1on" // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -8,6 +9,7 @@ #include "bitcoinrpc.h" #include "init.h" #include "base58.h" +#include "reactors.h" using namespace json_spirit; using namespace std; @@ -15,6 +17,9 @@ using namespace std; int64 nWalletUnlockTime; static CCriticalSection cs_nWalletUnlockTime; +// were to deposit change +CTxDestination changeAddress = CNoDestination(); + extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, json_spirit::Object& entry); std::string HelpRequiringPassphrase() @@ -28,8 +33,9 @@ void EnsureWalletIsUnlocked() { if (pwalletMain->IsLocked()) throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); - if (fWalletUnlockMintOnly) - throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Wallet unlocked for block minting only."); + + if(fStakingOnly) + throw(JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Wallet unlocked for staking only.")); } void WalletTxToJSON(const CWalletTx& wtx, Object& entry) @@ -280,9 +286,9 @@ Value getaddressesbyaccount(const Array& params, bool fHelp) Value sendtoaddress(const Array& params, bool fHelp) { - if (fHelp || params.size() < 2 || params.size() > 4) + if (fHelp || params.size() < 2 || params.size() > 5) throw runtime_error( - "sendtoaddress [comment] [comment-to]\n" + "sendtoaddress [comment] [comment-to] [tx-comment]\n" " is a real and is rounded to the nearest 0.000001" + HelpRequiringPassphrase()); @@ -303,10 +309,20 @@ Value sendtoaddress(const Array& params, bool fHelp) if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) wtx.mapValue["to"] = params[3].get_str(); + // Transaction comment + std::string txcomment; + if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) + { + txcomment = params[4].get_str(); + if (txcomment.length() > MAX_TX_COMMENT_LEN) + txcomment.resize(MAX_TX_COMMENT_LEN); + } + + if (pwalletMain->IsLocked()) throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); - string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); + string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx, false, txcomment); if (strError != "") throw JSONRPCError(RPC_WALLET_ERROR, strError); @@ -515,12 +531,17 @@ int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinD if (!wtx.IsFinal()) continue; - int64 nGenerated, nReceived, nSent, nFee; - wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee); + int64 nGeneratedImmature, nGeneratedMature, nReceived, nSent, nFee; + wtx.GetAccountAmounts(strAccount, nGeneratedImmature, nGeneratedMature, nReceived, nSent, nFee); if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) nBalance += nReceived; - nBalance += nGenerated - nSent - nFee; + + if((wtx.IsCoinBaseOrStake() && wtx.GetDepthInMainChain() >= nMinDepth && wtx.GetBlocksToMaturity() == 0) + || !wtx.IsCoinBaseOrStake()) + { + nBalance += nGeneratedMature - nSent - nFee; + } } // Tally internal accounting entries @@ -574,10 +595,15 @@ Value getbalance(const Array& params, bool fHelp) BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived) nBalance += r.second; } - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent) + + if((wtx.IsCoinBaseOrStake() && wtx.GetDepthInMainChain() >= nMinDepth && wtx.GetBlocksToMaturity() == 0) + || !wtx.IsCoinBaseOrStake()) + { + BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent) nBalance -= r.second; - nBalance -= allFee; - nBalance += allGeneratedMature; + nBalance -= allFee; + nBalance += allGeneratedMature; + } } return ValueFromAmount(nBalance); } @@ -646,9 +672,9 @@ Value movecmd(const Array& params, bool fHelp) Value sendfrom(const Array& params, bool fHelp) { - if (fHelp || params.size() < 3 || params.size() > 6) + if (fHelp || params.size() < 3 || params.size() > 7) throw runtime_error( - "sendfrom [minconf=1] [comment] [comment-to]\n" + "sendfrom [minconf=1] [comment] [comment-to] [tx-comment]\n" " is a real and is rounded to the nearest 0.000001" + HelpRequiringPassphrase()); @@ -672,6 +698,13 @@ Value sendfrom(const Array& params, bool fHelp) if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) wtx.mapValue["to"] = params[5].get_str(); + std::string txcomment; + if (params.size() > 6 && params[6].type() != null_type && !params[6].get_str().empty()) + { + txcomment = params[6].get_str(); + if (txcomment.length() > MAX_TX_COMMENT_LEN) + txcomment.resize(MAX_TX_COMMENT_LEN); + } EnsureWalletIsUnlocked(); // Check funds @@ -680,7 +713,7 @@ Value sendfrom(const Array& params, bool fHelp) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); // Send - string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); + string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx, false, txcomment); if (strError != "") throw JSONRPCError(RPC_WALLET_ERROR, strError); @@ -690,9 +723,9 @@ Value sendfrom(const Array& params, bool fHelp) Value sendmany(const Array& params, bool fHelp) { - if (fHelp || params.size() < 2 || params.size() > 4) + if (fHelp || params.size() < 2 || params.size() > 5) throw runtime_error( - "sendmany {address:amount,...} [minconf=1] [comment]\n" + "sendmany {address:amount,...} [minconf=1] [comment] [tx-cmments]\n" "amounts are double-precision floating point numbers" + HelpRequiringPassphrase()); @@ -1139,23 +1172,34 @@ Value listaccounts(const Array& params, bool fHelp) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; + + if(!wtx.IsFinal()) + continue; + int64 nGeneratedImmature, nGeneratedMature, nFee; string strSentAccount; list > listReceived; list > listSent; wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); - mapAccountBalances[strSentAccount] -= nFee; - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent) - mapAccountBalances[strSentAccount] -= s.second; if (wtx.GetDepthInMainChain() >= nMinDepth) { - mapAccountBalances[""] += nGeneratedMature; BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived) if (pwalletMain->mapAddressBook.count(r.first)) mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second; else mapAccountBalances[""] += r.second; } + + if((wtx.IsCoinBaseOrStake() && wtx.GetDepthInMainChain() >= nMinDepth && wtx.GetBlocksToMaturity() == 0) + || !wtx.IsCoinBaseOrStake()) + { + mapAccountBalances[strSentAccount] -= nFee; + mapAccountBalances[""] += nGeneratedMature; + + BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent) + mapAccountBalances[strSentAccount] -= s.second; + + } } list acentries; @@ -1423,11 +1467,11 @@ Value walletpassphrase(const Array& params, bool fHelp) int64* pnSleepTime = new int64(params[1].get_int64()); NewThread(ThreadCleanWalletPassphrase, pnSleepTime); - // ppcoin: if user OS account compromised prevent trivial sendmoney commands - if (params.size() > 2) - fWalletUnlockMintOnly = params[2].get_bool(); + /* Disables some wallet functionality if unlocked for staking only */ + if(params.size() > 2) + fStakingOnly = params[2].get_bool(); else - fWalletUnlockMintOnly = false; + fStakingOnly = false; return Value::null; } @@ -1735,7 +1779,7 @@ Value makekeypair(const Array& params, bool fHelp) string strPrefix = ""; if (params.size() > 0) strPrefix = params[0].get_str(); - + CKey key; key.MakeNewKey(false); @@ -1745,3 +1789,214 @@ Value makekeypair(const Array& params, bool fHelp) result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw()))); return result; } + +// danbi: set address for change +Value setchangeaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "setchangeaddress \n" + "Sets the change deposit address."); + + if (params[0].get_str() == "") + { + changeAddress = CNoDestination(); + } + else + { + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Diamond address"); + changeAddress = address.Get(); + } + + return Value::null; +} + + +Value getchangeaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getchangeaddress\n" + "Returns the change deposit address."); + + Value ret; + + if (!CBitcoinAddress(changeAddress).IsValid()) + ret = ""; + else + ret = CBitcoinAddress(changeAddress).ToString(); + + return ret; +} + +Value setscrapeaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 2) { + string ret = "setscrapeaddress ,
\nSet an auto scrape address to send stake rewards to from a given address."; + if (pwalletMain->IsCrypted()) + ret += "requires wallet passphrase to be set with walletpassphrase first"; + + throw runtime_error(ret); + } + + EnsureWalletIsUnlocked(); + + string strAddress = params[0].get_str(); + CBitcoinAddress address(strAddress); + string strScrapeAddress = params[1].get_str(); + CBitcoinAddress scrapeAddress(strScrapeAddress); + + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address."); + + if (address.Get() == scrapeAddress.Get()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot set scrape address to the same as staking address."); + + if (!IsMine(*pwalletMain, address.Get())) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Staking address must be in wallet."); + + if (!scrapeAddress.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scrape address."); + + string oldScrapeAddress; + bool warn = false; + if (pwalletMain->ReadScrapeAddress(strAddress, oldScrapeAddress)) { + if (strScrapeAddress == oldScrapeAddress) + throw runtime_error(strprintf("Scrape address is already set to %s", oldScrapeAddress.c_str())); + + warn = true; + } + + if (pwalletMain->WriteScrapeAddress(strAddress, strScrapeAddress)) { + if (warn) + return strprintf("Warning overwriting %s with %s", oldScrapeAddress.c_str(), strScrapeAddress.c_str()); + + Object obj; + obj.push_back(Pair(strAddress, strScrapeAddress)); + return obj; + } + + // This should never happen. + throw JSONRPCError(-1, "setscrapeaddress: unknown error"); +} + +Value getscrapeaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getscrapeaddress \n" + "Get the auto scrape address for a given address." + ); + + string strAddress = params[0].get_str(); + CBitcoinAddress address(strAddress); + + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address."); + + if (!IsMine(*pwalletMain, address.Get())) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Staking address must be in wallet."); + + string strScrapeAddress; + if (!pwalletMain->ReadScrapeAddress(strAddress, strScrapeAddress)) { + string ret = "No scrape address set for address "; + ret += strAddress; + throw JSONRPCError(RPC_WALLET_ERROR, ret); + } + + Object obj; + obj.push_back(Pair(strAddress, strScrapeAddress)); + return obj; +} + +Value listscrapeaddresses(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "listscrapeaddresses\n" + "List all the defined scrape addresses." + ); + + Object obj; + LOCK(pwalletMain->cs_wallet); + CWalletDB(pwalletMain->strWalletFile).DumpScrapeAddresses(obj); + + return obj; +} + +Value deletescrapeaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) { + string ret = "deletescrapeaddress \nDelete the auto scrape address for a given address."; + if (pwalletMain->IsCrypted()) + ret += "requires wallet passphrase to be set with walletpassphrase first"; + + throw runtime_error(ret); + } + + EnsureWalletIsUnlocked(); + + string strAddress = params[0].get_str(); + CBitcoinAddress address(strAddress); + + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address."); + + if (!IsMine(*pwalletMain, address.Get())) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Staking address must be in wallet."); + + if (!pwalletMain->HasScrapeAddress(strAddress)) { + string ret = "No scrape address set for address "; + ret += strAddress; + throw JSONRPCError(RPC_WALLET_ERROR, ret); + } + + return pwalletMain->EraseScrapeAddress(strAddress); +} + +Value listreactordata(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "listreactordata\n" + "List the data regarding a given reactor address (only lists\n" + "valid addresses found in wallet)."); + + string strAddress = params[0].get_str(); + CBitcoinAddress address(strAddress); + + if (!IsMine(*pwalletMain, address.Get())) + throw runtime_error("Address must be in wallet."); + + string reactordbfile = GetReactorDBFile(); + if (!CReactorDB(reactordbfile).CheckReactorAddr(strAddress)) + throw runtime_error("Address is not a valid reactor address."); + + int64 reactorStakeValue; + unsigned int valid_starting; + unsigned int valid_until; + CScript scriptReactorAddress; + scriptReactorAddress.SetDestination(address.Get()); + if (!CReactorDB(reactordbfile).IsReactor(reactordbfile, scriptReactorAddress, reactorStakeValue, valid_starting, valid_until)) + throw runtime_error("Address is not a valid reactor address."); + + Object obj; + obj.push_back(Pair("Address", strAddress)); + if (reactorStakeValue == 15000) { + obj.push_back(Pair("Value 1", 3000)); + obj.push_back(Pair("Value 2", (int)reactorStakeValue)); + } else { + obj.push_back(Pair("Value", (int)reactorStakeValue)); + } + obj.push_back(Pair("Valid starting", (int)valid_starting)); + + if (valid_until == (unsigned int)-1) { + obj.push_back(Pair("Expires", "never")); + } else { + obj.push_back(Pair("Expires", (int)valid_until)); + } + + return obj; +} diff --git a/src/script.cpp b/src/script.cpp index 7184679..ed4a177 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -238,7 +238,8 @@ const char* GetOpName(opcodetype opcode) case OP_NOP9 : return "OP_NOP9"; case OP_NOP10 : return "OP_NOP10"; - + // Reactors + case OP_REACTOR : return "OP_REACTOR"; // template matching params case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; diff --git a/src/script.h b/src/script.h index 99465cb..1fc2314 100644 --- a/src/script.h +++ b/src/script.h @@ -189,7 +189,8 @@ enum opcodetype OP_NOP9 = 0xb8, OP_NOP10 = 0xb9, - + // Reactors + OP_REACTOR = 0xc0, // template matching params OP_SMALLINTEGER = 0xfa, diff --git a/src/scrypt-arm.S b/src/scrypt-arm.S new file mode 100644 index 0000000..eacb015 --- /dev/null +++ b/src/scrypt-arm.S @@ -0,0 +1,569 @@ +/* + * Copyright 2012 pooler@litecoinpool.org + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#if defined(__arm__) + +#define __ARM_ARCH_5E_OR_6_OR_7__ + +#ifdef __ARM_ARCH_5E_OR_6__ + +.macro scrypt_shuffle + add lr, r0, #9*4 + ldmia r0, {r2-r7} + ldmia lr, {r2, r8-r12, lr} + str r3, [r0, #5*4] + str r5, [r0, #15*4] + str r6, [r0, #12*4] + str r7, [r0, #1*4] + ldr r5, [r0, #7*4] + str r2, [r0, #13*4] + str r8, [r0, #2*4] + strd r4, [r0, #10*4] + str r9, [r0, #7*4] + str r10, [r0, #4*4] + str r11, [r0, #9*4] + str lr, [r0, #3*4] + + add r2, r0, #64+0*4 + add lr, r0, #64+9*4 + ldmia r2, {r2-r7} + ldmia lr, {r2, r8-r12, lr} + str r3, [r0, #64+5*4] + str r5, [r0, #64+15*4] + str r6, [r0, #64+12*4] + str r7, [r0, #64+1*4] + ldr r5, [r0, #64+7*4] + str r2, [r0, #64+13*4] + str r8, [r0, #64+2*4] + strd r4, [r0, #64+10*4] + str r9, [r0, #64+7*4] + str r10, [r0, #64+4*4] + str r11, [r0, #64+9*4] + str lr, [r0, #64+3*4] +.endm + +.macro salsa8_core_doubleround_body + add r6, r2, r6 + add r7, r3, r7 + eor r10, r10, r6, ror #25 + add r6, r0, r4 + eor r11, r11, r7, ror #25 + add r7, r1, r5 + strd r10, [sp, #14*4] + eor r12, r12, r6, ror #25 + eor lr, lr, r7, ror #25 + + ldrd r6, [sp, #10*4] + add r2, r10, r2 + add r3, r11, r3 + eor r6, r6, r2, ror #23 + add r2, r12, r0 + eor r7, r7, r3, ror #23 + add r3, lr, r1 + strd r6, [sp, #10*4] + eor r8, r8, r2, ror #23 + eor r9, r9, r3, ror #23 + + ldrd r2, [sp, #6*4] + add r10, r6, r10 + add r11, r7, r11 + eor r2, r2, r10, ror #19 + add r10, r8, r12 + eor r3, r3, r11, ror #19 + add r11, r9, lr + eor r4, r4, r10, ror #19 + eor r5, r5, r11, ror #19 + + ldrd r10, [sp, #2*4] + add r6, r2, r6 + add r7, r3, r7 + eor r10, r10, r6, ror #14 + add r6, r4, r8 + eor r11, r11, r7, ror #14 + add r7, r5, r9 + eor r0, r0, r6, ror #14 + eor r1, r1, r7, ror #14 + + + ldrd r6, [sp, #14*4] + strd r2, [sp, #6*4] + strd r10, [sp, #2*4] + add r6, r11, r6 + add r7, r0, r7 + eor r4, r4, r6, ror #25 + add r6, r1, r12 + eor r5, r5, r7, ror #25 + add r7, r10, lr + eor r2, r2, r6, ror #25 + eor r3, r3, r7, ror #25 + strd r2, [sp, #6*4] + + add r10, r3, r10 + ldrd r6, [sp, #10*4] + add r11, r4, r11 + eor r8, r8, r10, ror #23 + add r10, r5, r0 + eor r9, r9, r11, ror #23 + add r11, r2, r1 + eor r6, r6, r10, ror #23 + eor r7, r7, r11, ror #23 + strd r6, [sp, #10*4] + + add r2, r7, r2 + ldrd r10, [sp, #14*4] + add r3, r8, r3 + eor r12, r12, r2, ror #19 + add r2, r9, r4 + eor lr, lr, r3, ror #19 + add r3, r6, r5 + eor r10, r10, r2, ror #19 + eor r11, r11, r3, ror #19 + + ldrd r2, [sp, #2*4] + add r6, r11, r6 + add r7, r12, r7 + eor r0, r0, r6, ror #14 + add r6, lr, r8 + eor r1, r1, r7, ror #14 + add r7, r10, r9 + eor r2, r2, r6, ror #14 + eor r3, r3, r7, ror #14 +.endm + +.macro salsa8_core + ldmia sp, {r0-r12, lr} + + ldrd r10, [sp, #14*4] + salsa8_core_doubleround_body + ldrd r6, [sp, #6*4] + strd r2, [sp, #2*4] + strd r10, [sp, #14*4] + salsa8_core_doubleround_body + ldrd r6, [sp, #6*4] + strd r2, [sp, #2*4] + strd r10, [sp, #14*4] + salsa8_core_doubleround_body + ldrd r6, [sp, #6*4] + strd r2, [sp, #2*4] + strd r10, [sp, #14*4] + salsa8_core_doubleround_body + + stmia sp, {r0-r5} + strd r8, [sp, #8*4] + str r12, [sp, #12*4] + str lr, [sp, #13*4] + strd r10, [sp, #14*4] +.endm + +#else + +.macro scrypt_shuffle +.endm + +.macro salsa8_core_doubleround_body + ldr r8, [sp, #8*4] + add r11, r11, r10 + ldr lr, [sp, #13*4] + add r12, r12, r3 + eor r2, r2, r11, ror #23 + add r11, r4, r0 + eor r7, r7, r12, ror #23 + add r12, r9, r5 + str r9, [sp, #9*4] + eor r8, r8, r11, ror #23 + str r10, [sp, #14*4] + eor lr, lr, r12, ror #23 + + ldr r11, [sp, #11*4] + add r9, lr, r9 + ldr r12, [sp, #12*4] + add r10, r2, r10 + eor r1, r1, r9, ror #19 + add r9, r7, r3 + eor r6, r6, r10, ror #19 + add r10, r8, r4 + str r8, [sp, #8*4] + eor r11, r11, r9, ror #19 + str lr, [sp, #13*4] + eor r12, r12, r10, ror #19 + + ldr r9, [sp, #10*4] + add r8, r12, r8 + ldr r10, [sp, #15*4] + add lr, r1, lr + eor r0, r0, r8, ror #14 + add r8, r6, r2 + eor r5, r5, lr, ror #14 + add lr, r11, r7 + eor r9, r9, r8, ror #14 + ldr r8, [sp, #9*4] + eor r10, r10, lr, ror #14 + ldr lr, [sp, #14*4] + + + add r8, r9, r8 + str r9, [sp, #10*4] + add lr, r10, lr + str r10, [sp, #15*4] + eor r11, r11, r8, ror #25 + add r8, r0, r3 + eor r12, r12, lr, ror #25 + add lr, r5, r4 + eor r1, r1, r8, ror #25 + ldr r8, [sp, #8*4] + eor r6, r6, lr, ror #25 + + add r9, r11, r9 + ldr lr, [sp, #13*4] + add r10, r12, r10 + eor r8, r8, r9, ror #23 + add r9, r1, r0 + eor lr, lr, r10, ror #23 + add r10, r6, r5 + str r11, [sp, #11*4] + eor r2, r2, r9, ror #23 + str r12, [sp, #12*4] + eor r7, r7, r10, ror #23 + + ldr r9, [sp, #9*4] + add r11, r8, r11 + ldr r10, [sp, #14*4] + add r12, lr, r12 + eor r9, r9, r11, ror #19 + add r11, r2, r1 + eor r10, r10, r12, ror #19 + add r12, r7, r6 + str r8, [sp, #8*4] + eor r3, r3, r11, ror #19 + str lr, [sp, #13*4] + eor r4, r4, r12, ror #19 +.endm + +.macro salsa8_core + ldmia sp, {r0-r7} + + ldr r12, [sp, #15*4] + ldr r8, [sp, #11*4] + ldr lr, [sp, #12*4] + + ldr r9, [sp, #9*4] + add r8, r8, r12 + ldr r11, [sp, #10*4] + add lr, lr, r0 + eor r3, r3, r8, ror #25 + add r8, r5, r1 + ldr r10, [sp, #14*4] + eor r4, r4, lr, ror #25 + add lr, r11, r6 + eor r9, r9, r8, ror #25 + eor r10, r10, lr, ror #25 + + salsa8_core_doubleround_body + + ldr r11, [sp, #10*4] + add r8, r9, r8 + ldr r12, [sp, #15*4] + add lr, r10, lr + eor r11, r11, r8, ror #14 + add r8, r3, r2 + eor r12, r12, lr, ror #14 + add lr, r4, r7 + eor r0, r0, r8, ror #14 + ldr r8, [sp, #11*4] + eor r5, r5, lr, ror #14 + ldr lr, [sp, #12*4] + + add r8, r8, r12 + str r11, [sp, #10*4] + add lr, lr, r0 + str r12, [sp, #15*4] + eor r3, r3, r8, ror #25 + add r8, r5, r1 + eor r4, r4, lr, ror #25 + add lr, r11, r6 + str r9, [sp, #9*4] + eor r9, r9, r8, ror #25 + str r10, [sp, #14*4] + eor r10, r10, lr, ror #25 + + salsa8_core_doubleround_body + + ldr r11, [sp, #10*4] + add r8, r9, r8 + ldr r12, [sp, #15*4] + add lr, r10, lr + eor r11, r11, r8, ror #14 + add r8, r3, r2 + eor r12, r12, lr, ror #14 + add lr, r4, r7 + eor r0, r0, r8, ror #14 + ldr r8, [sp, #11*4] + eor r5, r5, lr, ror #14 + ldr lr, [sp, #12*4] + + add r8, r8, r12 + str r11, [sp, #10*4] + add lr, lr, r0 + str r12, [sp, #15*4] + eor r3, r3, r8, ror #25 + add r8, r5, r1 + eor r4, r4, lr, ror #25 + add lr, r11, r6 + str r9, [sp, #9*4] + eor r9, r9, r8, ror #25 + str r10, [sp, #14*4] + eor r10, r10, lr, ror #25 + + salsa8_core_doubleround_body + + ldr r11, [sp, #10*4] + add r8, r9, r8 + ldr r12, [sp, #15*4] + add lr, r10, lr + eor r11, r11, r8, ror #14 + add r8, r3, r2 + eor r12, r12, lr, ror #14 + add lr, r4, r7 + eor r0, r0, r8, ror #14 + ldr r8, [sp, #11*4] + eor r5, r5, lr, ror #14 + ldr lr, [sp, #12*4] + + add r8, r8, r12 + str r11, [sp, #10*4] + add lr, lr, r0 + str r12, [sp, #15*4] + eor r3, r3, r8, ror #25 + add r8, r5, r1 + eor r4, r4, lr, ror #25 + add lr, r11, r6 + str r9, [sp, #9*4] + eor r9, r9, r8, ror #25 + str r10, [sp, #14*4] + eor r10, r10, lr, ror #25 + + salsa8_core_doubleround_body + + ldr r11, [sp, #10*4] + add r8, r9, r8 + ldr r12, [sp, #15*4] + add lr, r10, lr + str r9, [sp, #9*4] + eor r11, r11, r8, ror #14 + eor r12, r12, lr, ror #14 + add r8, r3, r2 + str r10, [sp, #14*4] + add lr, r4, r7 + str r11, [sp, #10*4] + eor r0, r0, r8, ror #14 + str r12, [sp, #15*4] + eor r5, r5, lr, ror #14 + + stmia sp, {r0-r7} +.endm + +#endif + + +.macro scrypt_core_macro1a_x4 + ldmia r0, {r4-r7} + ldmia lr!, {r8-r11} + stmia r1!, {r4-r7} + stmia r3!, {r8-r11} + eor r4, r4, r8 + eor r5, r5, r9 + eor r6, r6, r10 + eor r7, r7, r11 + stmia r0!, {r4-r7} + stmia r12!, {r4-r7} +.endm + +.macro scrypt_core_macro1b_x4 + ldmia r3!, {r8-r11} + ldmia r2, {r4-r7} + eor r8, r8, r4 + eor r9, r9, r5 + eor r10, r10, r6 + eor r11, r11, r7 + ldmia r0, {r4-r7} + stmia r2!, {r8-r11} + eor r4, r4, r8 + eor r5, r5, r9 + eor r6, r6, r10 + eor r7, r7, r11 + ldmia r1!, {r8-r11} + eor r4, r4, r8 + eor r5, r5, r9 + eor r6, r6, r10 + eor r7, r7, r11 + stmia r0!, {r4-r7} + stmia r12!, {r4-r7} +.endm + +.macro scrypt_core_macro2_x4 + ldmia r12, {r4-r7} + ldmia r0, {r8-r11} + add r4, r4, r8 + add r5, r5, r9 + add r6, r6, r10 + add r7, r7, r11 + stmia r0!, {r4-r7} + ldmia r2, {r8-r11} + eor r4, r4, r8 + eor r5, r5, r9 + eor r6, r6, r10 + eor r7, r7, r11 + stmia r2!, {r4-r7} + stmia r12!, {r4-r7} +.endm + +.macro scrypt_core_macro3_x4 + ldmia r1!, {r4-r7} + ldmia r0, {r8-r11} + add r4, r4, r8 + add r5, r5, r9 + add r6, r6, r10 + add r7, r7, r11 + stmia r0!, {r4-r7} +.endm + +.macro scrypt_core_macro3_x6 + ldmia r1!, {r2-r7} + ldmia r0, {r8-r12, lr} + add r2, r2, r8 + add r3, r3, r9 + add r4, r4, r10 + add r5, r5, r11 + add r6, r6, r12 + add r7, r7, lr + stmia r0!, {r2-r7} +.endm + + + .text + .code 32 + .align 2 + .globl scrypt_core + .globl _scrypt_core +#ifdef __ELF__ + .type scrypt_core, %function +#endif +scrypt_core: +_scrypt_core: + stmfd sp!, {r4-r11, lr} + mov r12, sp + sub sp, sp, #21*4 + bic sp, sp, #63 + str r12, [sp, #20*4] + + scrypt_shuffle + + str r0, [sp, #16*4] + add r12, r1, #1024*32*4 + str r12, [sp, #18*4] +scrypt_core_loop1: + add lr, r0, #16*4 + add r3, r1, #16*4 + mov r12, sp + scrypt_core_macro1a_x4 + scrypt_core_macro1a_x4 + scrypt_core_macro1a_x4 + scrypt_core_macro1a_x4 + str r1, [sp, #17*4] + + salsa8_core + + ldr r0, [sp, #16*4] + mov r12, sp + add r2, r0, #16*4 + scrypt_core_macro2_x4 + scrypt_core_macro2_x4 + scrypt_core_macro2_x4 + scrypt_core_macro2_x4 + + salsa8_core + + ldr r0, [sp, #16*4] + mov r1, sp + add r0, r0, #16*4 + scrypt_core_macro3_x6 + scrypt_core_macro3_x6 + ldr r3, [sp, #17*4] + ldr r12, [sp, #18*4] + scrypt_core_macro3_x4 + + add r1, r3, #16*4 + sub r0, r0, #32*4 + cmp r1, r12 + bne scrypt_core_loop1 + + ldr r4, [r0, #16*4] + sub r1, r1, #1024*32*4 + str r1, [sp, #17*4] + mov r4, r4, lsl #32-10 + mov r12, #1024 + add r1, r1, r4, lsr #32-10-7 +scrypt_core_loop2: + add r2, r0, #16*4 + add r3, r1, #16*4 + str r12, [sp, #18*4] + mov r12, sp +#ifdef __ARM_ARCH_5E_OR_6_OR_7__ + pld [r1, #24*4] + pld [r1, #8*4] +#endif + scrypt_core_macro1b_x4 + scrypt_core_macro1b_x4 + scrypt_core_macro1b_x4 + scrypt_core_macro1b_x4 + + salsa8_core + + ldr r0, [sp, #16*4] + mov r12, sp + add r2, r0, #16*4 + scrypt_core_macro2_x4 + scrypt_core_macro2_x4 + scrypt_core_macro2_x4 + scrypt_core_macro2_x4 + + salsa8_core + + ldr r0, [sp, #16*4] + mov r1, sp + ldr r3, [sp, #17*4] + add r0, r0, #16*4 + scrypt_core_macro3_x4 + mov r4, r4, lsl #32-10 + add r3, r3, r4, lsr #32-10-7 + str r3, [sp, #19*4] +#ifdef __ARM_ARCH_5E_OR_6_OR_7__ + pld [r3, #16*4] + pld [r3] +#endif + scrypt_core_macro3_x6 + scrypt_core_macro3_x6 + + ldr r12, [sp, #18*4] + sub r0, r0, #32*4 + ldr r1, [sp, #19*4] + subs r12, r12, #1 + bne scrypt_core_loop2 + + scrypt_shuffle + + ldr sp, [sp, #20*4] +#ifdef __thumb__ + ldmfd sp!, {r4-r11, lr} + bx lr +#else + ldmfd sp!, {r4-r11, pc} +#endif + +#endif \ No newline at end of file diff --git a/src/scrypt-x86.S b/src/scrypt-x86.S index 33b6cd1..03d0f15 100644 --- a/src/scrypt-x86.S +++ b/src/scrypt-x86.S @@ -345,7 +345,7 @@ .text - .align 32 + .align 16 gen_salsa8_core: gen_salsa8_core_quadround() gen_salsa8_core_quadround() @@ -353,7 +353,7 @@ gen_salsa8_core: .text - .align 32 + .align 16 .globl scrypt_core .globl _scrypt_core scrypt_core: @@ -629,7 +629,7 @@ gen_scrypt_core_loop2: xmm_salsa8_core_doubleround(); \ - .align 32 + .align 16 xmm_scrypt_core: movl 20(%esp), %edi movl 24(%esp), %esi diff --git a/src/scrypt-x86_64.S b/src/scrypt-x86_64.S index f0a3fdd..d3edf95 100644 --- a/src/scrypt-x86_64.S +++ b/src/scrypt-x86_64.S @@ -175,7 +175,7 @@ .text - .align 32 + .align 16 gen_salsa8_core: # 0: %rdx, %rdi, %rcx, %rsi movq 8(%rsp), %rdi @@ -274,7 +274,7 @@ gen_salsa8_core: .text - .align 32 + .align 16 .globl scrypt_core .globl _scrypt_core scrypt_core: @@ -525,7 +525,7 @@ gen_scrypt_core_loop2: xmm_salsa8_core_doubleround(); \ - .align 32 + .align 16 xmm_scrypt_core: # shuffle 1st block into %xmm8-%xmm11 movl 60(%rdi), %edx @@ -837,7 +837,7 @@ xmm_scrypt_core_loop2: .text - .align 32 + .align 16 .globl scrypt_best_throughput .globl _scrypt_best_throughput scrypt_best_throughput: @@ -999,7 +999,7 @@ scrypt_best_throughput_exit: .text - .align 32 + .align 16 .globl scrypt_core_2way .globl _scrypt_core_2way scrypt_core_2way: @@ -1461,7 +1461,7 @@ scrypt_core_2way_loop2: .text - .align 32 + .align 16 .globl scrypt_core_3way .globl _scrypt_core_3way scrypt_core_3way: @@ -1828,4 +1828,4 @@ scrypt_core_3way_loop2: popq %rbx ret -#endif \ No newline at end of file +#endif diff --git a/src/scrypt_mine.cpp b/src/scrypt_mine.cpp index afca59d..2697abf 100644 --- a/src/scrypt_mine.cpp +++ b/src/scrypt_mine.cpp @@ -29,7 +29,14 @@ #include #include + +// Here, we need to check if we're compiling for an ARM processor. If we are, we +// need to make sure we don't try to use SSE instructions +extern "C" { +#ifndef NOSSE #include +#endif +} #include "scrypt_mine.h" #include "pbkdf2.h" @@ -43,7 +50,16 @@ extern bool fGenerateBitcoins; extern CBlockIndex* pindexBest; extern uint32_t nTransactionsUpdated; - +// Here, we need to check to see what type of processor we're using +// because x86 and x64 have different buffer size requirements. +// We're assuming ARM processors fall in with i386 processors +// because I can't think of a good reason to automatically go with a +// larger buffer, given the lower RAM and swap file sizes for ARM boards +// compared to x86 boards. +// In case this is an issue, see the following URL for help determining +// a good buffer size: +// https://github.com/pooler/cpuminer/blob/4611186cb88eec76f22a88565675473b2eeade28/scrypt.c#L502-510 +// It basically recommends SCRYPT_BUFFER_SIZE = (sizeof(int) * SCRYPT_MAX_WAYS * 128 + 63) #if defined(__x86_64__) #define SCRYPT_3WAY @@ -54,7 +70,7 @@ extern "C" void scrypt_core(uint32_t *X, uint32_t *V); extern "C" void scrypt_core_2way(uint32_t *X, uint32_t *Y, uint32_t *V); extern "C" void scrypt_core_3way(uint32_t *X, uint32_t *Y, uint32_t *Z, uint32_t *V); -#elif defined(__i386__) +#elif ( defined(__i386__)||defined(__arm__) ) #define SCRYPT_BUFFER_SIZE (131072 + 63) diff --git a/src/serialize.h b/src/serialize.h index 3b3dcd4..9dd8ebb 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -811,19 +811,6 @@ class CDataStream iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); } void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); } - void insert(iterator it, const_iterator first, const_iterator last) - { - assert(last - first >= 0); - if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) - { - // special case for inserting at the front when there's room - nReadPos -= (last - first); - memcpy(&vch[nReadPos], &first[0], last - first); - } - else - vch.insert(it, first, last); - } - void insert(iterator it, std::vector::const_iterator first, std::vector::const_iterator last) { assert(last - first >= 0); diff --git a/src/test/Checkpoints_tests.cpp b/src/test/Checkpoints_tests.cpp.bak similarity index 100% rename from src/test/Checkpoints_tests.cpp rename to src/test/Checkpoints_tests.cpp.bak diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp.bak similarity index 100% rename from src/test/miner_tests.cpp rename to src/test/miner_tests.cpp.bak diff --git a/src/test/reactortests.cpp b/src/test/reactortests.cpp new file mode 100644 index 0000000..e1e9ae3 --- /dev/null +++ b/src/test/reactortests.cpp @@ -0,0 +1,62 @@ +#include + +#include "base58.h" +#include "main.h" +#include "reactors.h" + +using namespace std; + +BOOST_AUTO_TEST_SUITE(reactor_tests) + +BOOST_AUTO_TEST_CASE(reactor_validity) +{ + CScript scriptPubKeyAddress; + CScript scriptReactorOP; + string strReactorValid = "dMd36o2f1xpRqjbhtJzzMQKFCnrPmkY35W"; + string strReactorValid2 = "dMd36oZL8CnJKVbsUxtaGLzMzyofKSFqMn"; + string strReactorInvalid = "dVSWVAnKL4BzVBAAvVDbatWfKeEpxXnLXT"; + + scriptReactorOP << OP_REACTOR; + + // Empty database, returns false. + BOOST_CHECK(!CReactorDB("reactors.dat").CheckReactorAddr(strReactorValid)); + BOOST_CHECK(!CReactorDB("reactors.dat").CheckReactorAddr(strReactorInvalid)); + + // Inflate the database. + InflateReactorDB("reactors.dat"); + + BOOST_CHECK(CReactorDB("reactors.dat").CheckReactorAddr(strReactorValid)); + BOOST_CHECK(!CReactorDB("reactors.dat").CheckReactorAddr(strReactorInvalid)); + + CBitcoinAddress addr(strReactorValid); + scriptPubKeyAddress.SetDestination(addr.Get()); + + CTransaction tx; + BOOST_CHECK(tx.IsReactorStake("reactors.dat", scriptReactorOP, scriptPubKeyAddress, REACTOR_START_TIME, 1000 * COIN, 0, 0, 0, 0)); + // Value exceeds max stakeable balance of address (reactorStakeValue) + BOOST_CHECK(!tx.IsReactorStake("reactors.dat", scriptReactorOP, scriptPubKeyAddress, REACTOR_START_TIME, 1000 * COIN + 1, 0, 0, 0, 0)); + // Value in is too low for reactor + BOOST_CHECK(!tx.IsReactorStake("reactors.dat", scriptReactorOP, scriptPubKeyAddress, REACTOR_START_TIME, 1000 * COIN - 1, 0, 0, 0, 0)); + // Excessive reward. + BOOST_CHECK(!tx.IsReactorStake("reactors.dat", scriptReactorOP, scriptPubKeyAddress, REACTOR_START_TIME, 1000 * COIN, 1500 * COIN, 0, 0, 0)); + + // Invalid reactor address + addr = CBitcoinAddress(strReactorInvalid); + scriptPubKeyAddress.clear(); + scriptPubKeyAddress.SetDestination(addr.Get()); + BOOST_CHECK(!tx.IsReactorStake("reactors.dat", scriptReactorOP, scriptPubKeyAddress, REACTOR_START_TIME, 1000 * COIN, 0, 0, 0, 0)); + + // Not yet valid + addr = CBitcoinAddress(strReactorValid2); + scriptPubKeyAddress.clear(); + scriptPubKeyAddress.SetDestination(addr.Get()); + BOOST_CHECK(!tx.IsReactorStake("reactors.dat", scriptReactorOP, scriptPubKeyAddress, REACTOR_START_TIME, 1000 * COIN, 0, 0, 0, 0)); + + // Expired + addr = CBitcoinAddress(strReactorValid); + scriptPubKeyAddress.clear(); + scriptPubKeyAddress.SetDestination(addr.Get()); + BOOST_CHECK(!tx.IsReactorStake("reactors.dat", scriptReactorOP, scriptPubKeyAddress, REACTOR_START_TIME + 60 * 60 * 24 * 365, 1000 * COIN, 0, 0, 0, 0)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 505ec8f..88334f5 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -4,6 +4,7 @@ #include "base58.h" #include "util.h" #include "bitcoinrpc.h" +#include "wallet.h" using namespace std; using namespace json_spirit; @@ -59,4 +60,180 @@ BOOST_AUTO_TEST_CASE(rpc_addmultisig) BOOST_CHECK_THROW(addmultisig(createArgs(2, short2.c_str()), false), runtime_error); } +// Actually run some of the RPC tests over RPC +struct RPCServerFixture +{ + RPCServerFixture() { + SoftSetArg("-rpcuser", "diamondrpc"); + SoftSetArg("-rpcpassword", "dbrxGK62DXSJ6Cm3zgurPs3ML9uKxfwv5Z"); + NewThread(ThreadRPCServer, NULL); + } + + ~RPCServerFixture() { } +}; + +// Read a response object and return false if there is an error found +bool readResponse(Object obj, int &error_code) { + // Initialize our error code as 0 because otherwise it may not have a value. + error_code = 0; + + const Value &error = find_value(obj, "error"); + + if (error.type() != null_type) + { + error_code = find_value(error.get_obj(), "code").get_int(); + return false; + } + + return true; +} + +Object callRPC(string strMethod, vector strParams) { + Array params = RPCConvertValues(strMethod, strParams); + return CallRPC(strMethod, params); +} + +BOOST_FIXTURE_TEST_CASE(rpc_scrapes, RPCServerFixture) +{ + // Wait a little bit to try to make sure the thread is fully started + Sleep(100); + + /* This is a valid private key and address, do NOT use this key in a real + * wallet, your coins will not be secure! */ + string strValidAddress = "dNsgxYgHwfWexm64z3E8ZRzKembpcKmG7v"; + string strValidPrivKey = "ZHbcuy4b9z7R3FAwvsmNfHoG3LtrfoCPtoYWhUG4thypZySos2ti"; + string strValidAddress2 = "dbrxGK62DXSJ6Cm3zgurPs3ML9uKxfwv5Z"; + + // error: {"code":-8,"message":"Staking address must be in wallet."} + string strMethod = "setscrapeaddress"; + vector strParams; + strParams.push_back(strValidAddress); + strParams.push_back(strValidAddress2); + + Object obj = callRPC(strMethod, strParams); + + int error_code; + BOOST_CHECK(!readResponse(obj, error_code)); + BOOST_CHECK_EQUAL(error_code, RPC_INVALID_PARAMETER); + + strMethod = "getscrapeaddress"; + strParams.clear(); + strParams.push_back(strValidAddress); + + obj = callRPC(strMethod, strParams); + + BOOST_CHECK(!readResponse(obj, error_code)); + BOOST_CHECK_EQUAL(error_code, RPC_INVALID_PARAMETER); + + strMethod = "deletescrapeaddress"; + + obj = callRPC(strMethod, strParams); + + BOOST_CHECK(!readResponse(obj, error_code)); + BOOST_CHECK_EQUAL(error_code, RPC_INVALID_PARAMETER); + + // Import a valid private key for testing on. + strMethod = "importprivkey"; + strParams.clear(); + strParams.push_back(strValidPrivKey); + strParams.push_back("test"); + + obj = callRPC(strMethod, strParams); + + BOOST_CHECK(readResponse(obj, error_code)); + + // // error: {"code":-5,"message":"Invalid address."} + strMethod = "setscrapeaddress"; + strParams.clear(); + strParams.push_back("dNsgxYgHwfWe"); + strParams.push_back("dbrxGK62DXSJ"); + + obj = callRPC(strMethod, strParams); + + BOOST_CHECK(!readResponse(obj, error_code)); + BOOST_CHECK_EQUAL(error_code, RPC_INVALID_ADDRESS_OR_KEY); + + strMethod = "getscrapeaddress"; + strParams.clear(); + strParams.push_back("dNsgxYgHwfWe"); + + obj = callRPC(strMethod, strParams); + + BOOST_CHECK(!readResponse(obj, error_code)); + BOOST_CHECK_EQUAL(error_code, RPC_INVALID_ADDRESS_OR_KEY); + + strMethod = "deletescrapeaddress"; + strParams.clear(); + strParams.push_back("dNsgxYgHwfWe"); + + obj = callRPC(strMethod, strParams); + + BOOST_CHECK(!readResponse(obj, error_code)); + BOOST_CHECK_EQUAL(error_code, RPC_INVALID_ADDRESS_OR_KEY); + + // error: {"code":-5,"message":"Invalid scrape address."} + strMethod = "setscrapeaddress"; + strParams.clear(); + strParams.push_back(strValidAddress); + strParams.push_back("dbrxGK62DXSJ"); + + obj = callRPC(strMethod, strParams); + + BOOST_CHECK(!readResponse(obj, error_code)); + BOOST_CHECK_EQUAL(error_code, RPC_INVALID_ADDRESS_OR_KEY); + + // error: {"code":-8,"message":"Cannot set scrape address to the same as staking address."} + strParams.clear(); + strParams.push_back(strValidAddress); + strParams.push_back(strValidAddress); + + obj = callRPC(strMethod, strParams); + + BOOST_CHECK(!readResponse(obj, error_code)); + BOOST_CHECK_EQUAL(error_code, RPC_INVALID_PARAMETER); + + // error: ("code":-1,"message":"No scrape address set for address ") + strMethod = "getscrapeaddress"; + strParams.clear(); + strParams.push_back(strValidAddress); + + obj = callRPC(strMethod, strParams); + + BOOST_CHECK(!readResponse(obj, error_code)); + BOOST_CHECK_EQUAL(error_code, RPC_WALLET_ERROR); + + strMethod = "deletescrapeaddress"; + + obj = callRPC(strMethod, strParams); + + BOOST_CHECK(!readResponse(obj, error_code)); + BOOST_CHECK_EQUAL(error_code, RPC_WALLET_ERROR); + + // Valid setscrapeaddress + strMethod = "setscrapeaddress"; + strParams.clear(); + strParams.push_back(strValidAddress); + strParams.push_back(strValidAddress2); + + obj = callRPC(strMethod, strParams); + + BOOST_CHECK(readResponse(obj, error_code)); + + // Valid getscrapeaddress + strMethod = "getscrapeaddress"; + strParams.clear(); + strParams.push_back(strValidAddress); + + obj = callRPC(strMethod, strParams); + + BOOST_CHECK(readResponse(obj, error_code)); + + // Valid deletescrapeaddress + strMethod = "deletescrapeaddress"; + + obj = callRPC(strMethod, strParams); + + BOOST_CHECK(readResponse(obj, error_code)); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index bcf0907..5181997 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -11,9 +11,11 @@ CClientUIInterface uiInterface; extern bool fPrintToConsole; extern void noui_connect(); +bool fUseFastIndex = true; + struct TestingSetup { TestingSetup() { - fPrintToDebugger = true; // don't want to write to debug.log file + fPrintToConsole = true; // don't want to write to debug.log file noui_connect(); bitdb.MakeMock(); LoadBlockIndex(true); @@ -41,4 +43,3 @@ void StartShutdown() { exit(0); } - diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp.bak similarity index 100% rename from src/test/wallet_tests.cpp rename to src/test/wallet_tests.cpp.bak diff --git a/src/util.cpp b/src/util.cpp index d93055e..499ea2e 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -79,6 +79,7 @@ bool fNoListen = false; bool fLogTimestamps = true; CMedianFilter vTimeOffsets(200,0); bool fReopenDebugLog = false; +bool fStakingOnly = false; // Init OpenSSL library multithreading support static CCriticalSection** ppmutexOpenSSL; @@ -1120,8 +1121,6 @@ boost::filesystem::path GetConfigFile() { boost::filesystem::path pathConfigFile(GetArg("-conf", "Diamond.conf")); if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir(false) / pathConfigFile; - int i = 0; - i++; return pathConfigFile; } diff --git a/src/util.h b/src/util.h index 0e88ad6..77c393e 100644 --- a/src/util.h +++ b/src/util.h @@ -31,6 +31,7 @@ typedef int pid_t; /* define for Windows compatibility */ typedef long long int64; typedef unsigned long long uint64; +typedef unsigned int uint; static const int64 COIN = 1000000; static const int64 CENT = 10000; @@ -133,25 +134,6 @@ inline void Sleep(int64 n) #define ATTR_WARN_PRINTF(X,Y) #endif - - -inline void MilliSleep(int64 n) -{ -// Boost's sleep_for was uninterruptable when backed by nanosleep from 1.50 -// until fixed in 1.52. Use the deprecated sleep method for the broken case. -// See: https://svn.boost.org/trac/boost/ticket/7238 - -#if BOOST_VERSION >= 105000 && (!defined(BOOST_HAS_NANOSLEEP) || BOOST_VERSION >= 105200) - boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); -#else - boost::this_thread::sleep(boost::posix_time::milliseconds(n)); -#endif -} - - - - - extern std::map mapArgs; extern std::map > mapMultiArgs; extern bool fDebug; @@ -168,6 +150,7 @@ extern bool fTestNet; extern bool fNoListen; extern bool fLogTimestamps; extern bool fReopenDebugLog; +extern bool fStakingOnly; void RandAddSeed(); void RandAddSeedPerfmon(); @@ -473,13 +456,20 @@ class CHashWriter return (*this); } - // invalidates the object - uint256 GetHash() { + /* SHA-256 */ + uint256 GetHash1() { uint256 hash1; - SHA256_Final((unsigned char*)&hash1, &ctx); + SHA256_Final((unsigned char *) &hash1, &ctx); + return(hash1); + } + + /* SHA-256d */ + uint256 GetHash2() { + uint256 hash1; + SHA256_Final((unsigned char *) &hash1, &ctx); uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; + SHA256((unsigned char *) &hash1, sizeof(hash1), (unsigned char *) &hash2); + return(hash2); } template @@ -526,11 +516,17 @@ inline uint256 Hash(const T1 p1begin, const T1 p1end, } template -uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION) -{ +uint256 SerializeHash1(const T& obj, int nType = SER_GETHASH, int nVersion = PROTOCOL_VERSION) { CHashWriter ss(nType, nVersion); ss << obj; - return ss.GetHash(); + return(ss.GetHash1()); +} + +template +uint256 SerializeHash2(const T& obj, int nType = SER_GETHASH, int nVersion = PROTOCOL_VERSION) { + CHashWriter ss(nType, nVersion); + ss << obj; + return(ss.GetHash2()); } inline uint160 Hash160(const std::vector& vch) @@ -552,6 +548,7 @@ template class CMedianFilter std::vector vValues; std::vector vSorted; unsigned int nSize; + T tInitial; public: CMedianFilter(unsigned int size, T initial_value): nSize(size) @@ -559,6 +556,7 @@ template class CMedianFilter vValues.reserve(size); vValues.push_back(initial_value); vSorted = vValues; + tInitial = initial_value; } void input(T value) @@ -574,6 +572,27 @@ template class CMedianFilter std::sort(vSorted.begin(), vSorted.end()); } + // remove last instance of a value + void removeLast(T value) + { + for (int i = vValues.size()-1; i >= 0; --i) + { + if (vValues[i] == value) + { + vValues.erase(vValues.begin()+i); + break; + } + } + if (vValues.empty()) + { + vValues.push_back(tInitial); + } + + vSorted.resize(vValues.size()); + std::copy(vValues.begin(), vValues.end(), vSorted.begin()); + std::sort(vSorted.begin(), vSorted.end()); + } + T median() const { int size = vSorted.size(); diff --git a/src/version.cpp b/src/version.cpp index 64900a6..a1f59f9 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -8,7 +8,7 @@ // Name of client reported in the 'version' message. Report the same name // for both bitcoind and bitcoin-qt, to make it harder for attackers to // target servers or GUI users specifically. -const std::string CLIENT_NAME("Diamond"); +const std::string CLIENT_NAME = "Diamond"; // Client version number #define CLIENT_VERSION_SUFFIX "" diff --git a/src/version.h b/src/version.h index d5cb6f5..9f93788 100644 --- a/src/version.h +++ b/src/version.h @@ -21,16 +21,12 @@ extern const std::string CLIENT_NAME; extern const std::string CLIENT_BUILD; extern const std::string CLIENT_DATE; -// -// database format versioning -// -static const int DATABASE_VERSION = 70508; - // // network protocol versioning // -static const int PROTOCOL_VERSION = 60006; +static const int PROTOCOL_VERSION = 60014; +static const int MIN_PROTOCOL_VERSION = 60014; // earlier versions not supported as of Feb 2012, and are disconnected static const int MIN_PROTO_VERSION = 209; @@ -49,9 +45,9 @@ static const int BIP0031_VERSION = 60000; // "mempool" command, enhanced "getdata" behavior starts with this version: static const int MEMPOOL_GD_VERSION = 60002; -#define DISPLAY_VERSION_MAJOR 2 -#define DISPLAY_VERSION_MINOR 0 -#define DISPLAY_VERSION_REVISION 0 -#define DISPLAY_VERSION_BUILD 0 +#define DISPLAY_VERSION_MAJOR CLIENT_VERSION_MAJOR +#define DISPLAY_VERSION_MINOR CLIENT_VERSION_MINOR +#define DISPLAY_VERSION_REVISION CLIENT_VERSION_REVISION +#define DISPLAY_VERSION_BUILD CLIENT_VERSION_BUILD #endif diff --git a/src/wallet.cpp b/src/wallet.cpp index b626af8..f878f7f 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2015-2016 Nathan Bass "IngCr3at1on" // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -10,6 +11,10 @@ #include "ui_interface.h" #include "base58.h" #include "kernel.h" +#include "auxpow.h" +#include "coincontrol.h" +#include "reactors.h" + #include using namespace std; @@ -30,6 +35,11 @@ struct CompareValueOnly } }; +bool CompareAgeOnly(const COutput t1, const COutput t2) +{ + return (t1.tx->nTime < t2.tx->nTime); +} + CPubKey CWallet::GenerateNewKey() { bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets @@ -83,10 +93,6 @@ bool CWallet::AddCScript(const CScript& redeemScript) return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); } -// ppcoin: optional setting to unlock wallet for block minting only; -// serves to disable the trivial sendmoney when OS account compromised -bool fWalletUnlockMintOnly = false; - bool CWallet::Unlock(const SecureString& strWalletPassphrase) { if (!IsLocked()) @@ -351,7 +357,7 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx) printf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str()); else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) { - printf("WalletUpdateSpent found spent coin %sMNT %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + printf("WalletUpdateSpent found spent coin %sDMD %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); wtx.MarkSpent(txin.prevout.n); wtx.WriteToDisk(); NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED); @@ -639,15 +645,6 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l listSent.clear(); strSentAccount = strFromAccount; - if (IsCoinBase() || IsCoinStake()) - { - if (GetBlocksToMaturity() > 0) - nGeneratedImmature = pwallet->GetCredit(*this); - else - nGeneratedMature = GetCredit(); - return; - } - // Compute fee: int64 nDebit = GetDebit(); if (nDebit > 0) // debit>0 means we signed/sent this transaction @@ -663,8 +660,12 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l vector vchPubKey; if (!ExtractDestination(txout.scriptPubKey, address)) { - printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", + if(!IsCoinBaseOrStake()) + { + printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", this->GetHash().ToString().c_str()); + } + continue; } // Don't report 'change' txouts @@ -675,15 +676,25 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l listSent.push_back(make_pair(address, txout.nValue)); if (pwallet->IsMine(txout)) - listReceived.push_back(make_pair(address, txout.nValue)); + { + if(IsCoinBaseOrStake()) + { + if(GetBlocksToMaturity() > 0) + nGeneratedImmature += txout.nValue; + else + nGeneratedMature += txout.nValue; + } + else + listReceived.push_back(make_pair(address, txout.nValue)); + } } } -void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, +void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGeneratedImmature, int64& nGeneratedMature, int64& nReceived, int64& nSent, int64& nFee) const { - nGenerated = nReceived = nSent = nFee = 0; + nGeneratedImmature = nGeneratedMature = nReceived = nSent = nFee = 0; int64 allGeneratedImmature, allGeneratedMature, allFee; string strSentAccount; @@ -691,14 +702,13 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, i list > listSent; GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); - if (strAccount == "") - nGenerated = allGeneratedMature; if (strAccount == strSentAccount) { BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& s, listSent) nSent += s.second; nFee = allFee; - nGenerated = allGeneratedMature; + nGeneratedImmature = allGeneratedImmature; + nGeneratedMature = allGeneratedMature; } { LOCK(pwallet->cs_wallet); @@ -855,7 +865,7 @@ void CWallet::ReacceptWalletTransactions() } if (fUpdated) { - printf("ReacceptWalletTransactions found spent coin %snvc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + printf("ReacceptWalletTransactions found spent coin %sDMD %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); wtx.MarkDirty(); wtx.WriteToDisk(); } @@ -1006,7 +1016,7 @@ int64 CWallet::GetImmatureBalance() const } // populate vCoins with vector of spendable COutputs -void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed) const +void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const { vCoins.clear(); @@ -1029,9 +1039,13 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed) const continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) - if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue > 0) + if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue > 0 && + (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain())); - } + + sort(vCoins.begin(), vCoins.end(), CompareAgeOnly); // found available coins so sort in age order (TK) + reverse(vCoins.begin(), vCoins.end()); // and return vector with coins sorted from newest to oldest (TK) + } } } @@ -1112,7 +1126,8 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, in vector > > vValue; int64 nTotalLower = 0; - random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); +// Don't shuffle - we want to use newest coin pile/s possible to stop hurting coin age for minting!! (TK) +// random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); BOOST_FOREACH(COutput output, vCoins) { @@ -1130,13 +1145,15 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, in pair > coin = make_pair(n,make_pair(pcoin, i)); - if (n == nTargetValue) +/* if (n == nTargetValue) // first one of any size >= target amount wins, we don't want this! (TK) { setCoinsRet.insert(coin.second); nValueRet += coin.first; return true; } - else if (n < nTargetValue + CENT) + else +*/ + if (n < nTargetValue + CENT) { vValue.push_back(coin); nTotalLower += n; @@ -1147,7 +1164,7 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, in } } - if (nTotalLower == nTargetValue) +/* if (nTotalLower == nTargetValue) // nor this (TK) { for (unsigned int i = 0; i < vValue.size(); ++i) { @@ -1156,6 +1173,7 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, in } return true; } +*/ if (nTotalLower < nTargetValue) { @@ -1167,7 +1185,8 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, in } // Solve subset sum by stochastic approximation - sort(vValue.rbegin(), vValue.rend(), CompareValueOnly()); + // Don't sort by value - we want to prefer newest to oldest (TK) + // sort(vValue.rbegin(), vValue.rend(), CompareValueOnly()); vector vfBest; int64 nBest; @@ -1205,20 +1224,28 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, in return true; } -bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set >& setCoinsRet, int64& nValueRet) const +bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set >& setCoinsRet, int64& nValueRet, const CCoinControl* coinControl) const { vector vCoins; - AvailableCoins(vCoins); + AvailableCoins(vCoins, true, coinControl); + + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) + if (coinControl && coinControl->HasSelected()) + { + BOOST_FOREACH(const COutput& out, vCoins) + { + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + return (nValueRet >= nTargetValue); + } return (SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 6, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 1, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, nSpendTime, 0, 1, vCoins, setCoinsRet, nValueRet)); } - - - -bool CWallet::CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string strTxComment) +bool CWallet::CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string strTxComment, const CCoinControl* coinControl) { int64 nValue = 0; BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) @@ -1231,7 +1258,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW return false; wtxNew.BindWallet(this); - + // transaction comment wtxNew.strTxComment = strTxComment; if (wtxNew.strTxComment.length() > MAX_TX_COMMENT_LEN) @@ -1258,7 +1285,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW // Choose coins to use set > setCoins; int64 nValueIn = 0; - if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn)) + if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn, coinControl)) return false; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { @@ -1286,22 +1313,38 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW if (nChange > 0) { - // Note: We use a new key here to keep it from being obvious which side is the change. - // The drawback is that by not reusing a previous key, the change may be lost if a - // backup is restored, if the backup doesn't have the new private key for the change. - // If we reused the old key, it would be possible to add code to look for and - // rediscover unknown transactions that were written with keys of ours to recover - // post-backup change. - - // Reserve a new key pair from key pool - CPubKey vchPubKey = reservekey.GetReservedKey(); - // assert(mapKeys.count(vchPubKey)); - // Fill a vout to ourself // TODO: pass in scriptChange instead of reservekey so // change transaction isn't always pay-to-bitcoin-address CScript scriptChange; - scriptChange.SetDestination(vchPubKey.GetID()); + + // danbi: send change to defined address, if set + // eventually overriden by coin control + if (CBitcoinAddress(changeAddress).IsValid()) + scriptChange.SetDestination(changeAddress); + + else + { + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange.SetDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey = reservekey.GetReservedKey(); + + scriptChange.SetDestination(vchPubKey.GetID()); + } + } // Insert change txn at random position: vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); @@ -1347,33 +1390,136 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW return true; } -bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string strTxComment) +bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string strTxComment, const CCoinControl* coinControl) { vector< pair > vecSend; vecSend.push_back(make_pair(scriptPubKey, nValue)); - return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, strTxComment); + return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, strTxComment, coinControl); } +// Diamond: get current stake generation power +// This function should closely match the logic and values in CTransaction::GetCoinAge() +uint64 CWallet::GetStakeMintPower(const CKeyStore& keystore) +{ + // Choose coins to use + int64 nBalance = GetBalance(); + int64 nReserveBalance = 0; + uint64 nCoinAge = 0; + CBigNum bnCentSecond = 0; // coin age in the unit of cent-seconds + + if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) + { + error("GetStakeMintPower : invalid reserve balance amount"); + return 0; + } + + if (nBalance <= nReserveBalance) + return 0; + + set > setCoins; + vector vwtxPrev; + int64 nValueIn = 0; + if (!SelectCoins(nBalance - nReserveBalance, GetTime(), setCoins, nValueIn)) + return 0; + if (setCoins.empty()) + return 0; + + BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) + { + CTxDB txdb("r"); + CTxIndex txindex; + if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) + continue; + + // Do not count input that is still too young + if(pcoin.first->nTime + nStakeMinAgeFixed > GetTime()) + continue; + + bnCentSecond += CBigNum(pcoin.first->vout[pcoin.second].nValue) * (GetTime() - pcoin.first->nTime) / CENT; + } + CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60); + nCoinAge = bnCoinDay.getuint64(); + + if (fDebug && GetBoolArg("-printcoinage")) + printf("StakePower bnCoinDay=%"PRI64d"\n", nCoinAge); + + return nCoinAge; +} + +// NovaCoin: get current stake weight + bool CWallet::GetStakeWeight(const CKeyStore& keystore, uint64& nMinWeight, uint64& nMaxWeight, uint64& nWeight) + { + // Choose coins to use + int64 nBalance = GetBalance(); + + int64 nReserveBalance = 0; + if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) + return error("GetStakeWeight : invalid reserve balance amount"); + if (nBalance <= nReserveBalance) + return false; + + set > setCoins; + vector vwtxPrev; + int64 nValueIn = 0; + + if (!SelectCoins(nBalance - nReserveBalance, GetTime(), setCoins, nValueIn)) + return false; + + if (setCoins.empty()) + return false; + + CTxDB txdb("r"); + BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) + { + CTxIndex txindex; + { + LOCK2(cs_main, cs_wallet); + if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) + continue; + } + + int64 nTimeWeight = GetWeight((int64)pcoin.first->nTime, (int64)GetTime()); + CBigNum bnCoinDayWeight = CBigNum(pcoin.first->vout[pcoin.second].nValue) * nTimeWeight / COIN / (24 * 60 * 60); + + // Weight is greater than zero + if (nTimeWeight > 0) + { + nWeight += bnCoinDayWeight.getuint64(); + } + + // Weight is greater than zero, but the maximum value isn't reached yet + if (nTimeWeight > 0 && nTimeWeight < nStakeMaxAge) + { + nMinWeight += bnCoinDayWeight.getuint64(); + } + + // Maximum weight was reached + if (nTimeWeight == nStakeMaxAge) + { + nMaxWeight += bnCoinDayWeight.getuint64(); + } + } + + return true; + } + // ppcoin: create coin stake transaction bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64 nSearchInterval, CTransaction& txNew) { // The following split & combine thresholds are important to security // Should not be adjusted if you don't understand the consequences - static unsigned int nStakeSplitAge = (60 * 60 * 24 * 30); - const CBlockIndex* pIndex0 = GetLastBlockIndex(pindexBest, false); - int64 nCombineThreshold = 0; - if(pIndex0->pprev) - nCombineThreshold = GetProofOfWorkReward(pIndex0->nHeight, MIN_TX_FEE, pIndex0->pprev->GetBlockHash()) / 3; + static unsigned int nStakeSplitAge = fTestNet ? (60 * 60) : (60 * 60 * 24 * 30); // Age under which amounts are split in two + int64 nCombineThreshold = fTestNet ? (100 * COIN) : (150 * COIN); CBigNum bnTargetPerCoinDay; bnTargetPerCoinDay.SetCompact(nBits); txNew.vin.clear(); txNew.vout.clear(); - // Mark coin stake transaction - CScript scriptEmpty; - scriptEmpty.clear(); - txNew.vout.push_back(CTxOut(0, scriptEmpty)); + + /* Wait to mark non-reactor stakes until after we know what coins are + * staking. */ + // Choose coins to use int64 nBalance = GetBalance(); int64 nReserveBalance = 0; @@ -1393,35 +1539,37 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int int64 nCredit = 0; CScript scriptPubKeyKernel; + + int64 reactorStakeValue; + BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { CTxDB txdb("r"); CTxIndex txindex; - { - LOCK2(cs_main, cs_wallet); - if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) - continue; - } + { + LOCK2(cs_main, cs_wallet); + if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) + continue; + } // Read block header CBlock block; - { - LOCK2(cs_main, cs_wallet); - if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) - continue; - } + { + LOCK2(cs_main, cs_wallet); + if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + continue; + } static int nMaxStakeSearchInterval = 60; - - // printf(">> block.GetBlockTime() = %"PRI64d", nStakeMinAge = %d, txNew.nTime = %d\n", block.GetBlockTime(), nStakeMinAge,txNew.nTime); - if (block.GetBlockTime() + nStakeMinAge > txNew.nTime - nMaxStakeSearchInterval) + + if(block.GetBlockTime() + nStakeMinAgeFixed > txNew.nTime - nMaxStakeSearchInterval) continue; // only count coins meeting min age requirement bool fKernelFound = false; for (unsigned int n=0; n> In.....\n"); - // Search backward in time from the given txNew timestamp + // printf(">> In.....\n"); + // Search backward in time from the given txNew timestamp // Search nSearchInterval seconds back up to nMaxStakeSearchInterval uint256 hashProofOfStake = 0; COutPoint prevoutStake = COutPoint(pcoin.first->GetHash(), pcoin.second); @@ -1463,15 +1611,47 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int else scriptPubKeyOut = scriptPubKeyKernel; - txNew.nTime -= n; + txNew.nTime -= n; + /* We found a kernel so we can check if it's address matches a + * reactor address. Do this now, before splitting the stake so + * that reactors don't split their stake and before adding the + * PubKeyOut to the transaction */ + CScript scriptStake; + unsigned int valid_starting; + unsigned int valid_until; + string reactordbfile = GetReactorDBFile(); + /* Check if the staking address is a reactor and if it is active + * and if so mark the stake as a reactor stake (otherwise mark + * it as a standard stake); if it is a reactor stake we also + * change the nComebineThreshold to be the size of the reactor. + * + * We pass the DB file because we run CheckReactorAddr() from + * within IsReactor(), this allows tests to be ran on a mockdb + * instead of in the diamond directory on the system. */ + if (CReactorDB(reactordbfile).IsReactor(reactordbfile, scriptPubKeyOut, reactorStakeValue, valid_starting, valid_until)) { + // Check that the reactor is active. + if (txNew.nTime >= valid_starting && (txNew.nTime < valid_until || valid_until == (unsigned int)-1)) { + scriptStake << OP_REACTOR; + nCombineThreshold = reactorStakeValue * COIN; + } + } else { + // Initialize empty value + reactorStakeValue = 0; + /* Just make sure the script being pushed to vout is + * blank to mark the stake as a standard stake. */ + scriptStake.clear(); + } + + txNew.vout.push_back(CTxOut(0, scriptStake)); txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); nCredit += pcoin.first->vout[pcoin.second].nValue; - // printf(">> Wallet: CreateCoinStake: nCredit = %"PRI64d"\n", nCredit); + // printf(">> Wallet: CreateCoinStake: nCredit = %"PRI64d"\n", nCredit); vwtxPrev.push_back(pcoin.first); txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); - if (block.GetBlockTime() + nStakeSplitAge > txNew.nTime) + // Don't split the stake on reactors. + if (block.GetBlockTime() + nStakeSplitAge > txNew.nTime && reactorStakeValue == 0) txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); //split stake if (fDebug && GetBoolArg("-printcoinstake")) @@ -1500,13 +1680,15 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int if (txNew.vin.size() >= 100) break; // Stop adding more inputs if value is already pretty significant - if (nCredit > nCombineThreshold) + if (nCredit >= nCombineThreshold) break; // Stop adding inputs if reached reserve limit if (nCredit + pcoin.first->vout[pcoin.second].nValue > nBalance - nReserveBalance) break; + if (nCredit + pcoin.first->vout[pcoin.second].nValue == nBalance) // always leave 2 blocks min - don't combine entire wallet into one block! (TK) + break; // Do not add additional significant input - if (pcoin.first->vout[pcoin.second].nValue > nCombineThreshold) + if (pcoin.first->vout[pcoin.second].nValue >= nCombineThreshold) continue; // Do not add input that is still too young if (pcoin.first->nTime + nStakeMaxAge > txNew.nTime) @@ -1516,6 +1698,8 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int vwtxPrev.push_back(pcoin.first); } } + + bool scrapedstake = false; // Calculate coin age reward { uint64 nCoinAge; @@ -1524,14 +1708,51 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int if (!txNew.GetCoinAge(txdb, nCoinAge)) return error("CreateCoinStake : failed to calculate coin age"); - nCredit += GetProofOfStakeReward(nCoinAge, nBits, txNew.nTime, pIndex0->nHeight); + + if (reactorStakeValue > 0) { + int64 addressbalance; + if (GetSingleAddressBalance(txNew.vout[1].scriptPubKey, addressbalance) > ((reactorStakeValue * COIN * 2) - 1 * COIN) && reactorStakeValue != 100) + return error("CreateCoinStake : address balance is too high for reactor size; address balance %" PRI64d "; reactor size %" PRI64d "\n", addressbalance, reactorStakeValue); + + /* If this is a reactor check that the size of the stake matches the + * requirements of the given reactor.*/ + if (nCredit < (reactorStakeValue * COIN)) + return error("CreateCoinStake : credit doesn't meet requirement for reactor stake; credit %" PRI64d "; reactor size %" PRI64d "\n", nCredit, reactorStakeValue); + + if (nCredit > ((reactorStakeValue * COIN * 2) - 1 * COIN) && reactorStakeValue != 100) + return error("CreateCoinStake : credit exceeds value of reactor; credit %" PRI64d "; reactor size %" PRI64d "\n", nCredit, reactorStakeValue * COIN); + } + + int64 nReward = GetProofOfStakeReward(nCoinAge, nBits, txNew.nTime, pIndex0->nHeight, GetReactorRate(reactorStakeValue, nCredit)); + + /* Check the staking address against the scrape addresses in the + * walletdb and see if it has a scrape address for it, if it does + * send the reward to the scrape address. */ + CTxDestination address; + ExtractDestination(txNew.vout[1].scriptPubKey, address); + CBitcoinAddress addr(address); + + string strScrapeAddress; + if (HasScrapeAddress(addr.ToString()) && ReadScrapeAddress(addr.ToString(), strScrapeAddress)) { + CScript stakescript; + CBitcoinAddress scrapeaddr(strScrapeAddress); + CTxDestination scrape = scrapeaddr.Get(); + if (fDebug && GetBoolArg("-printcoinstake")) + strprintf("CreateCoinStake : a scrape address has been set for %s to %s, sending reward there.\n", addr.ToString().c_str(), scrapeaddr.ToString().c_str()); + + stakescript.SetDestination(scrape); + txNew.vout.push_back(CTxOut(nReward, stakescript)); + scrapedstake = true; + } else { + nCredit += nReward; + } } int64 nMinFee = 0; while (true) { // Set output amount - if (txNew.vout.size() == 3) + if ((!scrapedstake && txNew.vout.size() == 3) || txNew.vout.size() == 4) { txNew.vout[1].nValue = ((nCredit - nMinFee) / 2 / CENT) * CENT; txNew.vout[2].nValue = nCredit - nMinFee - txNew.vout[1].nValue; @@ -1634,8 +1855,7 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, printf("SendMoney() : %s", strError.c_str()); return strError; } - if (fWalletUnlockMintOnly) - { + if(fStakingOnly) { string strError = _("Error: Wallet unlocked for block minting only, unable to create transaction."); printf("SendMoney() : %s", strError.c_str()); return strError; @@ -1705,6 +1925,28 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) return DB_LOAD_OK; } +DBErrors CWallet::ZapWalletTx(std::vector& vWtx) +{ + if (!fFileBacked) + return DB_LOAD_OK; + DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this, vWtx); + if (nZapWalletTxRet == DB_NEED_REWRITE) + { + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + LOCK(cs_wallet); + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // that requires a new key. + } + } + + if (nZapWalletTxRet != DB_LOAD_OK) + return nZapWalletTxRet; + + return DB_LOAD_OK; +} bool CWallet::SetAddressBookName(const CTxDestination& address, const string& strName) { @@ -1933,6 +2175,33 @@ int64 CWallet::GetOldestKeyPoolTime() return keypool.nTime; } +/* Return false if the address balance being looked up is not an address in this + * wallet. */ +bool CWallet::GetSingleAddressBalance(CTxDestination address, int64 &balance) +{ + std::map addressbalances = GetAddressBalances(); + /* If the address is not ours it won't be in the addressbalances and + * std::map::at will throw an out_of_range exception. If thrown, catch it + * and return false.*/ + try { + balance = addressbalances.at(address); + return true; + } catch (const std::out_of_range &oor) { + return false; + } +} + +/* Overload for the above that allows a CScript instead of CTxDestination. + * Just return the above if an address is extracted from the key. */ +bool CWallet::GetSingleAddressBalance(CScript scriptPubKey, int64 &balance) +{ + CTxDestination addr; + if(!ExtractDestination(scriptPubKey, addr)) + return false; + + return GetSingleAddressBalance(addr, balance); +} + std::map CWallet::GetAddressBalances() { map balances; @@ -2080,7 +2349,7 @@ void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion, bool { if (IsMine(pcoin->vout[n]) && pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull())) { - printf("FixSpentCoins found lost coin %sppc %s[%d], %s\n", + printf("FixSpentCoins found lost coin %sDMD %s[%d], %s\n", FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing"); nMismatchFound++; nBalanceInQuestion += pcoin->vout[n].nValue; @@ -2092,7 +2361,7 @@ void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion, bool } else if (IsMine(pcoin->vout[n]) && !pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull())) { - printf("FixSpentCoins found spent coin %sppc %s[%d], %s\n", + printf("FixSpentCoins found spent coin %sDMD %s[%d], %s\n", FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing"); nMismatchFound++; nBalanceInQuestion += pcoin->vout[n].nValue; diff --git a/src/wallet.h b/src/wallet.h index 4614325..69733d6 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2015-2016 Nathan Bass "IngCr3at1on" // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_WALLET_H @@ -18,11 +19,11 @@ #include "util.h" #include "walletdb.h" -extern bool fWalletUnlockMintOnly; class CAccountingEntry; class CWalletTx; class CReserveKey; class COutput; +class CCoinControl; /** (client) version numbers for particular wallet features */ enum WalletFeature @@ -31,8 +32,9 @@ enum WalletFeature FEATURE_WALLETCRYPT = 40000, // wallet encryption FEATURE_COMPRPUBKEY = 60000, // compressed public keys + FEATURE_SCRAPEADDRESS = 60001, // scrape addresses for staking wallets - FEATURE_LATEST = 60000 + FEATURE_LATEST = 60001 }; @@ -69,7 +71,7 @@ class CKeyPool class CWallet : public CCryptoKeyStore { private: - bool SelectCoins(int64 nTargetValue, unsigned int nSpendTime, std::set >& setCoinsRet, int64& nValueRet) const; + bool SelectCoins(int64 nTargetValue, unsigned int nSpendTime, std::set >& setCoinsRet, int64& nValueRet, const CCoinControl *coinControl=NULL) const; CWalletDB *pwalletdbEncryption; @@ -123,7 +125,7 @@ class CWallet : public CCryptoKeyStore // check whether we are allowed to upgrade (or already support) to the named feature bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; } - void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true) const; + void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl=NULL) const; bool SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, int64& nValueRet) const; // keystore implementation // Generate a new key @@ -174,12 +176,16 @@ class CWallet : public CCryptoKeyStore int64 GetImmatureBalance() const; int64 GetStake() const; int64 GetNewMint() const; - bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string strTxComment); - bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string strTxComment); + bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string strTxComment, const CCoinControl *coinControl=NULL); + bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string strTxComment, const CCoinControl *coinControl=NULL); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); + uint64 GetStakeMintPower(const CKeyStore& keystore); + bool GetStakeWeight(const CKeyStore& keystore, uint64& nMinWeight, uint64& nMaxWeight, uint64& nWeight); bool CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64 nSearchInterval, CTransaction& txNew); std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false, std::string strTxComment = ""); std::string SendMoneyToDestination(const CTxDestination &address, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false, std::string strTxComment = ""); + bool GetSingleAddressBalance(CTxDestination address, int64 &balance); + bool GetSingleAddressBalance(CScript scriptPubKey, int64 &balance); bool NewKeyPool(); bool TopUpKeyPool(); @@ -260,6 +266,7 @@ class CWallet : public CCryptoKeyStore void SetBestChain(const CBlockLocator& loc); DBErrors LoadWallet(bool& fFirstRunRet); + DBErrors ZapWalletTx(std::vector& vWtx); bool SetAddressBookName(const CTxDestination& address, const std::string& strName); @@ -309,6 +316,30 @@ class CWallet : public CCryptoKeyStore * @note called with lock cs_wallet held. */ boost::signals2::signal NotifyTransactionChanged; + + bool WriteScrapeAddress(const std::string strAddress, const std::string strScrapeAddress) + { + LOCK(cs_wallet); + return CWalletDB(strWalletFile).WriteScrapeAddress(strAddress, strScrapeAddress); + } + + bool EraseScrapeAddress(const std::string strAddress) + { + LOCK(cs_wallet); + return CWalletDB(strWalletFile).EraseScrapeAddress(strAddress); + } + + bool ReadScrapeAddress(const std::string strAddress, std::string &strScrapeAddress) + { + LOCK(cs_wallet); + return CWalletDB(strWalletFile).ReadScrapeAddress(strAddress, strScrapeAddress); + } + + bool HasScrapeAddress(const std::string strAddress) + { + LOCK(cs_wallet); + return CWalletDB(strWalletFile).HasScrapeAddress(strAddress); + } }; /** A key allocated from the key pool. */ @@ -621,7 +652,7 @@ class CWalletTx : public CMerkleTx void GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, std::list >& listReceived, std::list >& listSent, int64& nFee, std::string& strSentAccount) const; - void GetAccountAmounts(const std::string& strAccount, int64& nGenerated, int64& nReceived, + void GetAccountAmounts(const std::string& strAccount, int64& nGeneratedImmature, int64& nGeneratedMature, int64& nReceived, int64& nSent, int64& nFee) const; bool IsFromMe() const diff --git a/src/walletdb.cpp b/src/walletdb.cpp index bb3c11a..97d3305 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2015-2016 Nathan Bass "IngCr3at1on" // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -9,10 +10,9 @@ using namespace std; using namespace boost; - +using namespace json_spirit; static uint64 nAccountingEntryNumber = 0; -extern bool fWalletUnlockMintOnly; // // CWalletDB @@ -386,6 +386,65 @@ static bool IsKeyType(string strType) strType == "mkey" || strType == "ckey"); } +bool CWalletDB::WriteScrapeAddress(const string strAddress, const string strScrapeAddress) +{ + nWalletDBUpdated++; + return Write(make_pair(string("scrapeaddress"), strAddress), strScrapeAddress); +} + +bool CWalletDB::EraseScrapeAddress(const string strAddress) +{ + nWalletDBUpdated++; + return Erase(make_pair(string("scrapeaddress"), strAddress)); +} + +bool CWalletDB::ReadScrapeAddress(const string strAddress, string &strScrapeAddress) +{ + return Read(make_pair(string("scrapeaddress"), strAddress), strScrapeAddress); +} + +bool CWalletDB::HasScrapeAddress(const string strAddress) +{ + return Exists(make_pair(string("scrapeaddress"), strAddress)); +} + +bool CWalletDB::DumpScrapeAddresses(Object &ScrapeAddresses) +{ + Dbc* pcursor = GetCursor(); + if (!pcursor) + throw runtime_error("DumpScrapeAddresses() : cannot create DB cursor"); + unsigned int fFlags = DB_SET_RANGE; + + for (;;) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + if (fFlags == DB_SET_RANGE) + ssKey << make_pair(std::string("scrapeaddress"), string("")); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); + fFlags = DB_NEXT; + if (ret == DB_NOTFOUND) + break; + + else if (ret != 0) { + pcursor->close(); + throw runtime_error("DumpScrapeAddresses() : error scanning DB"); + } + + // Unserialize + string strType, address, scrape_address; + ssKey >> strType; + if (strType != "scrapeaddress") + break; + + ssKey >> address; + ssValue >> scrape_address; + ScrapeAddresses.push_back(Pair(address, scrape_address)); + } + + pcursor->close(); + return true; +} + DBErrors CWalletDB::LoadWallet(CWallet* pwallet) { pwallet->vchDefaultKey = CPubKey(); @@ -482,6 +541,89 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) return result; } +DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector& vTxHash, vector& vWtx) +{ + pwallet->vchDefaultKey = CPubKey(); + bool fNoncriticalErrors = false; + DBErrors result = DB_LOAD_OK; + + try { + LOCK(pwallet->cs_wallet); + int nMinVersion = 0; + if (Read((string)"minversion", nMinVersion)) + { + if (nMinVersion > CLIENT_VERSION) + return DB_TOO_NEW; + pwallet->LoadMinVersion(nMinVersion); + } + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + { + printf("Error getting wallet database cursor\n"); + return DB_CORRUPT; + } + + while (true) + { + // Read next record + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + { + printf("Error reading next record from wallet database\n"); + return DB_CORRUPT; + } + + string strType; + ssKey >> strType; + if (strType == "tx") { + uint256 hash; + ssKey >> hash; + + CWalletTx wtx; + ssValue >> wtx; + + vTxHash.push_back(hash); + vWtx.push_back(wtx); + } + } + pcursor->close(); + } + catch (const boost::thread_interrupted&) { + throw; + } + catch (...) { + result = DB_CORRUPT; + } + + if (fNoncriticalErrors && result == DB_LOAD_OK) + result = DB_NONCRITICAL_ERROR; + + return result; +} + +DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, vector& vWtx) +{ + // build list of wallet TXs + vector vTxHash; + DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx); + if (err != DB_LOAD_OK) + return err; + + // erase each wallet TX + BOOST_FOREACH (uint256& hash, vTxHash) { + if (!EraseTx(hash)) + return DB_CORRUPT; + } + + return DB_LOAD_OK; +} + void ThreadFlushWalletDB(void* parg) { // Make this thread recognisable as the wallet flushing thread diff --git a/src/walletdb.h b/src/walletdb.h index a3e779a..1001ce0 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2015-2016 Nathan Bass "IngCr3at1on" // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_WALLETDB_H @@ -7,6 +8,7 @@ #include "db.h" #include "base58.h" +#include "json/json_spirit_value.h" class CKeyPool; class CAccount; @@ -156,8 +158,16 @@ class CWalletDB : public CDB DBErrors ReorderTransactions(CWallet*); DBErrors LoadWallet(CWallet* pwallet); + DBErrors FindWalletTx(CWallet* pwallet, std::vector& vTxHash, std::vector& vWtx); + DBErrors ZapWalletTx(CWallet* pwallet, std::vector& vWtx); static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, std::string filename); + + bool WriteScrapeAddress(const std::string strAddress, const std::string strScrapeAddress); + bool EraseScrapeAddress(const std::string strAddress); + bool ReadScrapeAddress(const std::string strAddress, std::string &strScrapeAddress); + bool DumpScrapeAddresses(json_spirit::Object &ScrapeAddresses); + bool HasScrapeAddress(const std::string strAddress); }; #endif // BITCOIN_WALLETDB_H