From a7759718cff9894839027fb35c9aa9e992a82c90 Mon Sep 17 00:00:00 2001 From: Nathan Bass Date: Wed, 2 Sep 2015 18:17:59 -0500 Subject: [PATCH] Implement scrape addresses Enable setting a scrape address for any in wallet address: This will scrape all stake rewards at a transaction level and send the reward to the scrape address. Scrape addresses are stored in their own database and can be set, listed or deleted using RPC via daemon or the in QT RPC console. --- paycoin.pro | 2 + src/bitcoinrpc.cpp | 9 ++ src/db.cpp | 4 +- src/init.cpp | 6 ++ src/makefile.linux-mingw | 3 +- src/makefile.mingw | 3 +- src/makefile.osx | 3 +- src/makefile.osx-mavericks | 3 +- src/makefile.unix | 4 +- src/scrapesdb.cpp | 188 +++++++++++++++++++++++++++++++++++++ src/scrapesdb.h | 25 +++++ src/wallet.cpp | 29 +++++- 12 files changed, 269 insertions(+), 10 deletions(-) create mode 100644 src/scrapesdb.cpp create mode 100644 src/scrapesdb.h diff --git a/paycoin.pro b/paycoin.pro index f4e1e7f7..3cb11c5a 100644 --- a/paycoin.pro +++ b/paycoin.pro @@ -129,6 +129,7 @@ HEADERS += src/qt/bitcoingui.h \ src/script.h \ src/init.h \ src/mruset.h \ + src/scrapesdb.h \ src/json/json_spirit_writer_template.h \ src/json/json_spirit_writer.h \ src/json/json_spirit_value.h \ @@ -204,6 +205,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/db.cpp \ src/walletdb.cpp \ src/primekeys.cpp \ + src/scrapesdb.cpp \ src/json/json_spirit_writer.cpp \ src/json/json_spirit_value.cpp \ src/json/json_spirit_reader.cpp \ diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index be1154ec..fcb29919 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -58,6 +58,11 @@ extern Value createrawtransaction(const Array& params, bool fHelp); extern Value decoderawtransaction(const Array& params, bool fHelp); extern Value signrawtransaction(const Array& params, bool fHelp); extern Value sendrawtransaction(const Array& params, bool fHelp); +// In scrapesdb.cpp +extern Value setscrapeaddress(const Array& params, bool fHelp); +extern Value getscrapeaddress(const Array& params, bool fHelp); +extern Value listscrapeaddresses(const Array& params, bool fHelp); +extern Value deletescrapeaddress(const Array& params, bool fHelp); Object JSONRPCError(int code, const string& message) { @@ -2889,6 +2894,10 @@ static const CRPCCommand vRPCCommands[] = { "gettxout", &gettxout, true }, { "getrawmempool", &getrawmempool, true }, { "clearorphans", &clearorphans, true }, + { "getscrapeaddress", &getscrapeaddress, true }, + { "listscrapeaddresses", &listscrapeaddresses, true }, + { "setscrapeaddress", &setscrapeaddress, true }, + { "deletescrapeaddress", &deletescrapeaddress, true } }; CRPCTable::CRPCTable() diff --git a/src/db.cpp b/src/db.cpp index 8fb88314..83cb12f5 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -174,6 +174,8 @@ void CDB::Close() nMinutes = 2; if (strFile == "blkindex.dat") nMinutes = 2; + if (strFile == "scrapes.dat") + nMinutes = 2; if (strFile == "blkindex.dat" && IsInitialBlockDownload()) nMinutes = 5; @@ -851,5 +853,3 @@ bool LoadAddresses() { return CAddrDB("cr+").LoadAddresses(); } - - diff --git a/src/init.cpp b/src/init.cpp index 41519e23..5e44e0d4 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -13,6 +13,7 @@ #include "ui_interface.h" #include "checkpoints.h" #include "version.h" +#include "scrapesdb.h" #include #include #include @@ -29,6 +30,7 @@ using namespace std; using namespace boost; CWallet* pwalletMain; +CScrapesDB* scrapesDB; int MIN_PROTO_VERSION = 70003; ////////////////////////////////////////////////////////////////////////////// @@ -73,12 +75,14 @@ void Shutdown(void* parg) { fShutdown = true; nTransactionsUpdated++; + scrapesDB->Close(); DBFlush(false); StopNode(); DBFlush(true); boost::filesystem::remove(GetPidFile()); UnregisterWallet(pwalletMain); delete pwalletMain; + delete scrapesDB; NewThread(ExitTimeout, NULL); Sleep(50); printf("Paycoin exiting\n\n"); @@ -508,6 +512,8 @@ bool AppInit2(int argc, char* argv[]) printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); } + scrapesDB = new CScrapesDB("cw"); + InitMessage(_("Done loading")); printf("Done loading\n"); diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index f3ff93af..79d45a08 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -69,7 +69,8 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/primekeys.o + obj/primekeys.o \ + obj/scrapesdb.o all: paycoind.exe diff --git a/src/makefile.mingw b/src/makefile.mingw index 3fff5060..b042a987 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -122,7 +122,8 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/primekeys.o + obj/primekeys.o \ + obj/scrapesdb.o all: paycoind.exe diff --git a/src/makefile.osx b/src/makefile.osx index ffbd71bd..c7394fe1 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -103,7 +103,8 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/primekeys.o + obj/primekeys.o \ + obj/scrapesdb.o ifdef USE_UPNP DEFS += -DUSE_UPNP=$(USE_UPNP) diff --git a/src/makefile.osx-mavericks b/src/makefile.osx-mavericks index a4ca8eb3..276422fa 100644 --- a/src/makefile.osx-mavericks +++ b/src/makefile.osx-mavericks @@ -104,7 +104,8 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/primekeys.o + obj/primekeys.o \ + obj/scrapesdb.o ifdef USE_UPNP DEFS += -DUSE_UPNP=$(USE_UPNP) diff --git a/src/makefile.unix b/src/makefile.unix index 5b085fba..bce8fdf2 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -113,8 +113,8 @@ OBJS= \ obj/walletdb.o \ obj/noui.o \ obj/kernel.o \ - obj/primekeys.o - + obj/primekeys.o \ + obj/scrapesdb.o all: paycoind diff --git a/src/scrapesdb.cpp b/src/scrapesdb.cpp new file mode 100644 index 00000000..bedaf644 --- /dev/null +++ b/src/scrapesdb.cpp @@ -0,0 +1,188 @@ +// Copyright (c) 2015 The Paycoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "init.h" +#include "bitcoinrpc.h" +#include "scrapesdb.h" + +#define printf OutputDebugStringF + +using namespace json_spirit; +using namespace std; + +extern CScrapesDB* scrapesDB; + +Value setscrapeaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error( + "setscrapeaddress ,
\n" + "Set an auto scrape address to send stake rewards to from a given address." + ); + + string strAddress = params[0].get_str(); + CBitcoinAddress address(strAddress); + string strScrapeAddress = params[1].get_str(); + CBitcoinAddress scrapeAddress(strScrapeAddress); + + if (address.Get() == scrapeAddress.Get()) + throw runtime_error( + "Cannot set scrape address to the same as staking address." + ); + + if (!IsMine(*pwalletMain, address.Get())) + throw runtime_error( + "Staking address must be in wallet." + ); + + if (!scrapeAddress.IsValid()) + throw runtime_error( + "Invalid scrape address." + ); + + string oldScrapeAddress; + bool warn = false; + if (scrapesDB->ReadScrapeAddress(strAddress, oldScrapeAddress)) { + if (strScrapeAddress == oldScrapeAddress) + throw runtime_error(strprintf("Scrape address is already set to %s", oldScrapeAddress.c_str())); + + warn = true; + } + + if (scrapesDB->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 runtime_error( + "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 (!IsMine(*pwalletMain, address.Get())) + throw runtime_error( + "Staking address must be in wallet." + ); + + string strScrapeAddress; + if (!scrapesDB->ReadScrapeAddress(strAddress, strScrapeAddress)) { + string ret = "No scrape address set for address "; + ret += strAddress; + throw runtime_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; + scrapesDB->DumpScrapeAddresses(obj); + + return obj; +} + +Value deletescrapeaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "deletescrapeaddress \n" + "Delete the auto scrape address for a given address." + ); + + string strAddress = params[0].get_str(); + CBitcoinAddress address(strAddress); + + if (!IsMine(*pwalletMain, address.Get())) + throw runtime_error( + "Staking address must be in wallet." + ); + + if (!scrapesDB->HasScrapeAddress(strAddress)) { + string ret = "No scrape address set for address "; + ret += strAddress; + throw runtime_error(ret); + } + + return scrapesDB->EraseScrapeAddress(strAddress); +} + +bool CScrapesDB::WriteScrapeAddress(const string strAddress, const string strScrapeAddress) +{ + return Write(make_pair(string("scrapeaddress"), strAddress), strScrapeAddress); +} + +bool CScrapesDB::EraseScrapeAddress(const string strAddress) +{ + return Erase(make_pair(string("scrapeaddress"), strAddress)); +} + +bool CScrapesDB::ReadScrapeAddress(const string strAddress, string &strScrapeAddress) +{ + return Read(make_pair(string("scrapeaddress"), strAddress), strScrapeAddress); +} + +bool CScrapesDB::HasScrapeAddress(const string strAddress) +{ + return Exists(make_pair(string("scrapeaddress"), strAddress)); +} + +bool CScrapesDB::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; +} diff --git a/src/scrapesdb.h b/src/scrapesdb.h new file mode 100644 index 00000000..254be9c7 --- /dev/null +++ b/src/scrapesdb.h @@ -0,0 +1,25 @@ +// Copyright (c) 2015 The Paycoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef SCRAPESDB_H +#define SCRAPESDB_H + +#include "db.h" +#include "json/json_spirit_value.h" + +class CScrapesDB : public CDB +{ +public: + CScrapesDB(const char* pszMode="r+") : CDB("scrapes.dat", pszMode) { } +private: + CScrapesDB(const CScrapesDB&); + void operator=(const CScrapesDB); +public: + 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 // SCRAPESDB_H diff --git a/src/wallet.cpp b/src/wallet.cpp index fbca6918..46e26cbd 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -12,10 +12,12 @@ #include "base58.h" #include "coincontrol.h" #include "kernel.h" +#include "scrapesdb.h" #include using namespace std; +extern CScrapesDB* scrapesDB; ////////////////////////////////////////////////////////////////////////////// // @@ -1487,6 +1489,8 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int } } + + bool scrapedstake = false; // Calculate coin age reward { uint64 nCoinAge; @@ -1507,14 +1511,35 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int if(nReward <= 0) { return false; } - nCredit += nReward; + + /* Check the staking address against the scrapes database 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 (scrapesDB->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; for (;;) { // 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;