diff --git a/.gitignore b/.gitignore index c96bcdf52..17cbc6cde 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,19 @@ build-aux/m4/ltversion.m4 build-aux/missing build-aux/compile build-aux/test-driver +src/build-aux/config.guess +src/build-aux/config.sub +src/build-aux/depcomp +src/build-aux/install-sh +src/build-aux/ltmain.sh +src/build-aux/libtool.m4 +src/build-aux/lt~obsolete.m4 +src/build-aux/ltoptions.m4 +src/build-aux/ltsugar.m4 +src/build-aux/ltversion.m4 +src/build-aux/missing +src/build-aux/compile +src/build-aux/test-driver config.log config.status configure diff --git a/src/Makefile.am b/src/Makefile.am index a49073fae..570a8f996 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -69,6 +69,7 @@ BITCOIN_CORE_H = \ tinyformat.h \ txdb.h \ txmempool.h \ + ubi.h \ ui_interface.h \ uint256.h \ util.h \ @@ -122,6 +123,7 @@ libbitcoin_server_a_SOURCES = \ leveldbwrapper.cpp \ main.cpp \ miner.cpp \ + ubi.cpp \ net.cpp \ noui.cpp \ richlistdb.cpp \ diff --git a/src/coins.cpp b/src/coins.cpp index 0c51c11df..c467e07b8 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -63,6 +63,8 @@ bool CCoinsView::GetDexList(const std::string &key, std::tuple &value) { return false; } bool CCoinsView::GetBookList(const std::string &key, std::tuple &value) { return false; } bool CCoinsView::SetBookList(const std::string &key, const std::tuple &value) { return false; } +bool CCoinsView::GetNPList(const std::string &key, std::tuple &value) { return false; } +bool CCoinsView::SetNPList(const std::string &key, const std::tuple &value) { return false; } bool CCoinsView::HaveCoins(const uint256 &txid) { return false; } uint256 CCoinsView::GetBestBlock() { return uint256(0); } bool CCoinsView::SetBestBlock(const uint256 &hashBlock) { return false; } @@ -72,6 +74,7 @@ bool CCoinsView::BatchWrite(const std::map &mapCoins, const std const std::map > &mapServiceUbiList, const std::map > &mapServiceDexList, const std::map > &mapServiceBookList, + const std::map > &mapServiceNPList, const uint256 &hashBlock) { return false; } bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } @@ -91,6 +94,8 @@ bool CCoinsViewBacked::GetDexList(const std::string &key, std::tuple &value) { return base->SetDexList(key, value); } bool CCoinsViewBacked::GetBookList(const std::string &key, std::tuple &value) { return base->GetBookList(key, value); } bool CCoinsViewBacked::SetBookList(const std::string &key, const std::tuple &value) { return base->SetBookList(key, value); } +bool CCoinsViewBacked::GetNPList(const std::string &key, std::tuple &value) { return base->GetNPList(key, value); } +bool CCoinsViewBacked::SetNPList(const std::string &key, const std::tuple &value) { return base->SetNPList(key, value); } bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(txid); } uint256 CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } bool CCoinsViewBacked::SetBestBlock(const uint256 &hashBlock) { return base->SetBestBlock(hashBlock); } @@ -101,7 +106,8 @@ bool CCoinsViewBacked::BatchWrite(const std::map &mapCoins, con const std::map > &mapServiceUbiList, const std::map > &mapServiceDexList, const std::map > &mapServiceBookList, - const uint256 &hashBlock) { return base->BatchWrite(mapCoins, mapAddressInfo, mapServiceInfo, mapServiceTicketList, mapServiceUbiList, mapServiceDexList, mapServiceBookList, hashBlock); } + const std::map > &mapServiceNPList, + const uint256 &hashBlock) { return base->BatchWrite(mapCoins, mapAddressInfo, mapServiceInfo, mapServiceTicketList, mapServiceUbiList, mapServiceDexList, mapServiceBookList, mapServiceNPList, hashBlock); } bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), hashBlock(0) { } @@ -230,6 +236,25 @@ bool CCoinsViewCache::SetBookList(const std::string &key, const std::tuple &value) { + std::map >::iterator it = cacheServiceNPList.find(key); + + if(it!=cacheServiceNPList.end()) { + value = it->second; + return true; + } + if(base->GetNPList(key,value)) { + cacheServiceNPList[key] = value; + return true; + } + return false; +} + +bool CCoinsViewCache::SetNPList(const std::string &key, const std::tuple &value) { + cacheServiceNPList[key] = value; + return true; +} + std::map::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { std::map::iterator it = cacheCoins.lower_bound(txid); if (it != cacheCoins.end() && it->first == txid) @@ -275,6 +300,7 @@ bool CCoinsViewCache::BatchWrite(const std::map &mapCoins, const std::map > &mapServiceUbiList, const std::map > &mapServiceDexList, const std::map > &mapServiceBookList, + const std::map > &mapServiceNPList, const uint256 &hashBlockIn) { for (std::map::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) cacheCoins[it->first] = it->second; @@ -290,13 +316,15 @@ bool CCoinsViewCache::BatchWrite(const std::map &mapCoins, cacheServiceDexList[it->first] = it->second; for (std::map >::const_iterator it = mapServiceBookList.begin(); it != mapServiceBookList.end(); it++) cacheServiceBookList[it->first] = it->second; + for (std::map >::const_iterator it = mapServiceNPList.begin(); it != mapServiceNPList.end(); it++) + cacheServiceNPList[it->first] = it->second; hashBlock = hashBlockIn; return true; } bool CCoinsViewCache::Flush() { - bool fOk = base->BatchWrite(cacheCoins, cacheAddressInfo, cacheServiceInfo, cacheServiceTicketList, cacheServiceUbiList, cacheServiceDexList, cacheServiceBookList, hashBlock); + bool fOk = base->BatchWrite(cacheCoins, cacheAddressInfo, cacheServiceInfo, cacheServiceTicketList, cacheServiceUbiList, cacheServiceDexList, cacheServiceBookList, cacheServiceNPList, hashBlock); if (fOk) { cacheCoins.clear(); cacheAddressInfo.clear(); @@ -305,6 +333,7 @@ bool CCoinsViewCache::Flush() { cacheServiceUbiList.clear(); cacheServiceDexList.clear(); cacheServiceBookList.clear(); + cacheServiceNPList.clear(); } return fOk; } diff --git a/src/coins.h b/src/coins.h index 611f360ab..9c49bcbbc 100644 --- a/src/coins.h +++ b/src/coins.h @@ -248,6 +248,11 @@ class CCoinsView //Modify the balance of and height where a given scriptPubKey was last used - servicebooklist virtual bool SetBookList(const std::string &key, const std::tuple &value); + virtual bool GetNPList(const std::string &key, std::tuple &value); + + //Modify the balance of and height where a given scriptPubKey was last used - servicebooklist + virtual bool SetNPList(const std::string &key, const std::tuple &value); + //Just check whether we have data for a given txid. virtual bool HaveCoins(const uint256 &txid); @@ -264,6 +269,7 @@ class CCoinsView const std::map > &mapServiceUbiList, const std::map > &mapServiceDexList, const std::map > &mapServiceBookList, + const std::map > &mapServiceNPList, const uint256 &hashBlock); //Calculate statistics about the unspent transaction output set @@ -296,6 +302,8 @@ class CCoinsViewBacked : public CCoinsView bool SetDexList(const std::string &key, const std::tuple &value); bool GetBookList(const std::string &key, std::tuple &value); bool SetBookList(const std::string &key, const std::tuple &value); + bool GetNPList(const std::string &key, std::tuple &value); + bool SetNPList(const std::string &key, const std::tuple &value); bool HaveCoins(const uint256 &txid); uint256 GetBestBlock(); bool SetBestBlock(const uint256 &hashBlock); @@ -306,6 +314,7 @@ class CCoinsViewBacked : public CCoinsView const std::map > &mapServiceUbiList, const std::map > &mapServiceDexList, const std::map > &mapServiceBookList, + const std::map > &mapServiceNPList, const uint256 &hashBlock); bool GetStats(CCoinsStats &stats); }; @@ -323,6 +332,7 @@ class CCoinsViewCache : public CCoinsViewBacked std::map > cacheServiceUbiList; std::map > cacheServiceDexList; std::map > cacheServiceBookList; + std::map > cacheServiceNPList; public: CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); @@ -342,6 +352,8 @@ class CCoinsViewCache : public CCoinsViewBacked bool SetDexList(const std::string &key, const std::tuple &value); bool GetBookList(const std::string &key, std::tuple &value); bool SetBookList(const std::string &key, const std::tuple &value); + bool GetNPList(const std::string &key, std::tuple &value); + bool SetNPList(const std::string &key, const std::tuple &value); bool HaveCoins(const uint256 &txid); uint256 GetBestBlock(); bool SetBestBlock(const uint256 &hashBlock); @@ -351,6 +363,7 @@ class CCoinsViewCache : public CCoinsViewBacked const std::map > &mapServiceUbiList, const std::map > &mapServiceDexList, const std::map > &mapServiceBookList, + const std::map > &mapServiceNPList, const uint256 &hashBlock); //Return a modifiable reference to a CCoins. Check HaveCoins first. diff --git a/src/init.cpp b/src/init.cpp index b6dac33ab..df6d4f2f3 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -561,6 +561,7 @@ bool AppInit2(boost::thread_group& threadGroup) fServer = GetBoolArg("-server", false); fPrintToConsole = GetBoolArg("-printtoconsole", false); fLogTimestamps = GetBoolArg("-logtimestamps", true); + fUBI = GetBoolArg("-ubi", false); setvbuf(stdout, NULL, _IOLBF, 0); #ifdef ENABLE_WALLET bool fDisableWallet = GetBoolArg("-disablewallet", false); @@ -891,6 +892,11 @@ bool AppInit2(boost::thread_group& threadGroup) break; } + if (!InitServiceNPList(*pcoinsdbview)) { + strLoadError = _("Error initializing service np list. You need to rebuild the database using -reindex"); + break; + } + // Reading rich addresses into memory if(!pcoinsdbview -> GetRichAddresses(RichList)) { strLoadError = _("Error loading rich list"); @@ -927,6 +933,12 @@ bool AppInit2(boost::thread_group& threadGroup) break; } + // reading npos addresses into memory + if(!pcoinsdbview -> GetNPList(ServiceItemList)) { + strLoadError = _("Error loading service np list"); + break; + } + // Initialize the block index (no-op if non-empty database was already loaded) if (!InitBlockIndex()) { strLoadError = _("Error initializing block database"); @@ -981,6 +993,13 @@ bool AppInit2(boost::thread_group& threadGroup) ServiceItemList.SetForked(fFork); } + if(!pblocktree -> ReadServiceNPListFork(fFork)) { + strLoadError = _("Error reading service np chapter list fork status"); + break; + } else { + ServiceItemList.SetForked(fFork); + } + uiInterface.InitMessage(_("Verifying blocks...")); if (!VerifyDB(GetArg("-checklevel", 3), GetArg("-checkblocks", 288))) { @@ -1048,6 +1067,16 @@ bool AppInit2(boost::thread_group& threadGroup) } } + if(fFork) { + if(!ServiceItemList.UpdateNPListHeights()) { + strLoadError = _("Error rollbacking NP chapter list heights"); + break; + } + else { + ServiceItemList.SetForked(false); + } + } + } catch(std::exception &e) { if (fDebug) LogPrintf("%s\n", e.what()); strLoadError = _("Error opening block database"); diff --git a/src/main.cpp b/src/main.cpp index 602bc19a5..4c94c37f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1820,6 +1820,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex std::map > serviceUbiList; std::map > serviceDexList; std::map > serviceBookList; + std::map > serviceNPList; // undo transactions in reverse order for (int i = block.vtx.size() - 1; i >= 0; i--) { @@ -1868,7 +1869,8 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex if (sAddress.IsValid() && !ServiceList.IsService(asciiAddress) && serviceName.length() <= 50 && (hexToAscii(serviceType) == "1" || hexToAscii(serviceType) == "2" || hexToAscii(serviceType) == "3"|| hexToAscii(serviceType) == "4"|| - hexToAscii(serviceType) == "5"|| hexToAscii(serviceType) == "6" || hexToAscii(serviceType) == "7")) { + hexToAscii(serviceType) == "5"|| hexToAscii(serviceType) == "6" || hexToAscii(serviceType) == "7" + || hexToAscii(serviceType) == "8")) { std::tuple value; if (!view.GetServiceInfo(asciiAddress, value)) { return state.Abort(_("Failed to read service index")); @@ -1988,6 +1990,29 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } } } + // New Np + // If the string starts with "NN" + else if (hexData.substr(0, 4) == "4e4e") { + std::string newDex = hexData.substr(4, hexString.size()); + std::vector strs = splitString(newDex, "20"); + // The string has to have 2 params to be valid as a np and saved into the db + if (strs.size() == 2) { + std::string npAddress = strs.at(0); + std::string npName = strs.at(1); + std::string asciiAddress = hexToAscii(npAddress); + CBitcoinAddress nAddress = CBitcoinAddress(asciiAddress); + if (nAddress.IsValid() && npName.length() <= 60 && ServiceList.IsService(toAddress)) { + std::tuple value; + if(!view.GetNPList(asciiAddress, value)) + return state.Abort(_("Failed to read np index")); + else { + value = std::make_tuple("NN", toAddress, hexToAscii(npName)); + assert(view.SetNPList(asciiAddress, value)); + serviceNPList[asciiAddress]=value; + } + } + } + } // New book chapter // If the string starts with "NB" else if (hexData.substr(0, 4) == "4e42") { @@ -2201,6 +2226,10 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex return state.Abort(_("Failed to update book chapter list")); } + if(!ServiceItemList.UpdateNPList(serviceNPList)) { + return state.Abort(_("Failed to update np chapter list")); + } + return fClean; } @@ -2293,6 +2322,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C std::map > serviceUbiList; std::map > serviceDexList; std::map > serviceBookList; + std::map > serviceNPList; for (unsigned int i = 0; i < block.vtx.size(); i++) { @@ -2392,7 +2422,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C if (sAddress.IsValid() && !ServiceList.IsService(asciiAddress) && serviceName.length() <= 50 && (hexToAscii(serviceType) == "1" || hexToAscii(serviceType) == "2" || hexToAscii(serviceType) == "3"|| hexToAscii(serviceType) == "4"|| - hexToAscii(serviceType) == "5"|| hexToAscii(serviceType) == "6" || hexToAscii(serviceType) == "7")) { + hexToAscii(serviceType) == "5"|| hexToAscii(serviceType) == "6" || hexToAscii(serviceType) == "7" + || hexToAscii(serviceType) == "8")) { std::tuple value; value = std::make_tuple("NS", hexToAscii(serviceName), hexToAscii(serviceType)); assert(view.SetServiceInfo(asciiAddress, value)); @@ -2493,6 +2524,24 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C } } } + // If the string starts with "NN" (new np) + else if (hexData.substr(0, 4) == "4e4e") { + std::string newNP = hexData.substr(4, hexString.size()); + std::vector strs = splitString(newNP, "20"); + // The string has to have 2 params to be valid as a dex and saved into the db + if (strs.size() == 2) { + std::string npAddress = strs.at(0); + std::string npName = strs.at(1); + std::string asciiAddress = hexToAscii(npAddress); + CBitcoinAddress nAddress = CBitcoinAddress(asciiAddress); + if (nAddress.IsValid() && npName.length() <= 60 && ServiceList.IsService(toAddress)) { + std::tuple value; + value = std::make_tuple("NN", toAddress, hexToAscii(npName)); + assert(view.SetNPList(asciiAddress, value)); + serviceNPList[asciiAddress]=value; + } + } + } // If the string starts with "NB" (new book chapter) else if (hexData.substr(0, 4) == "4e42") { std::string newBookChapter = hexData.substr(4, hexString.size()); @@ -2585,6 +2634,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C } } } + std::cout << hexData.substr(0, 4) << std::endl; } } } @@ -4079,6 +4129,21 @@ bool InitServiceBookList(CCoinsView &dbview) } +bool InitServiceNPList(CCoinsView &dbview) +{ + LOCK(cs_main); + if (fReindex || chainActive.Genesis() == NULL) { + if(!dbview.SetBookList(string("np"), std::make_tuple("1", "0", "0"))) { + return false; } + pblocktree->WriteFlag("nplist", true); + } + bool dummy; + if(!pblocktree->ReadFlag("nplist", dummy)) + return false; + return true; + +} + void PrintBlockTree() { AssertLockHeld(cs_main); diff --git a/src/main.h b/src/main.h index 5cbe0fa63..162e56844 100644 --- a/src/main.h +++ b/src/main.h @@ -156,6 +156,7 @@ bool InitServiceTicketList(CCoinsView &dbview); bool InitServiceUbiList(CCoinsView &dbview); bool InitServiceDexList(CCoinsView &dbview); bool InitServiceBookList(CCoinsView &dbview); +bool InitServiceNPList(CCoinsView &dbview); /** Load the block tree and coins database from disk */ bool LoadBlockIndex(); /** Unload database information */ diff --git a/src/miner.cpp b/src/miner.cpp index 71b81935f..8b009b719 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -10,6 +10,7 @@ #include "main.h" #include "net.h" #include "richlistdb.h" +#include "ubi.h" #ifdef ENABLE_WALLET #include "wallet.h" #endif @@ -366,7 +367,10 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, int algo) LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize); - txNew.vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); + // miner's reward, if ubi then we take a 90% of the tx fees + if (fUBI) { + txNew.vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees/10); + } //CTxOut minerTxOut = CTxOut(0, scriptPubKeyIn); CScript richpubkey; if(!RichList.NextRichScriptPubKey(richpubkey)) @@ -376,6 +380,19 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, int algo) //txNew.vout.push_back(minerTxOut); txNew.vout.push_back(richTxOut); txNew.vout.push_back(EIASTxOut); + + + if (fUBI && UBI::GetUBIDividends(nFees) > 0) + { + vector ubi_pubkeys = UBI::NextBatch(pindexPrev->nHeight + 1); + for (auto ubi_pubkey : ubi_pubkeys) + { + CTxOut ubi_txout = CTxOut(UBI::GetUBIDividends(nFees), ubi_pubkey); + txNew.vout.push_back(ubi_txout); + } + } + + pblock->vtx[0] = txNew; pblocktemplate->vTxFees[0] = -nFees; diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index f7e249077..f666c15ad 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -218,6 +218,96 @@ Value adddex(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } +Value addnpo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "addnpo \"nptype\" \"npaddress\" \"npname\" \n" + "\n Add new non-profit address to a non-profit group/type.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"nptype\" (string, required) The DEX service name associated with the new DEX address.\n" + "2. \"npaddress\" (string, required) The smileycoin DEX address associated with the DEX service.\n" + "3. \"npname\" (string, required) A short description of the DEX. \n" + + "\nResult:\n" + "\"transactionid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("adddex", "SmileyDEX 1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd dexdescription") + + HelpExampleCli("adddex", "SmileyDEX 1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd dexdescription") + + HelpExampleRpc("adddex", "SmileyDEX 1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd dexdescription") + ); + + std::string serviceName = params[0].get_str(); + std::string npAddress = params[1].get_str(); + std::string npName = params[2].get_str(); + + CBitcoinAddress address(npAddress); + if (!address.IsValid()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Smileycoin address"); + } else if (ServiceItemList.IsNP(npAddress)) { + throw runtime_error("The entered address is already on the non-profit list. Please use another address."); + } else if (npName.length() > 30) { + throw runtime_error("Non-profit name cannot be more than 30 characters long."); + } + + // Amount + int64_t nValue = 1*COIN; + int64_t DEFAULT_AMOUNT = 0; + + vector > vecSend; + + std::multiset>> myServices; + ServiceList.GetMyServiceAddresses(myServices); + + std::string npServiceAddress = ""; + // Send new ticket transaction to corresponding service address + for(std::set< std::pair< std::string, std::tuple > >::const_iterator it = myServices.begin(); it!=myServices.end(); it++ ) + { + if(serviceName == get<1>(it->second)) { + npServiceAddress = it->first; + } + } + + if (!ServiceList.IsService(npServiceAddress)) { + throw runtime_error("The entered non-profit group name cannot be found on service list."); + } + + // Parse Smileycoin address + CBitcoinAddress dServiceAddress(npServiceAddress); + CScript scriptPubKey; + scriptPubKey.SetDestination(dServiceAddress.Get()); + + // Pay 1 SMLY to own np service address + vecSend.push_back(make_pair(scriptPubKey, nValue)); + + vector str; + int64_t amount = 0; + + std::string txData = HexStr("NN " + npAddress + " " + npName, false); + str.push_back(txData); + vector data = ParseHexV(str[0], "Data"); + + // Create op_return script in the form -> NN npaddress npname + vecSend.push_back(make_pair(CScript() << OP_RETURN << data, max(DEFAULT_AMOUNT, amount) * COIN)); + + CWalletTx wtx; + + EnsureWalletIsUnlocked(); + + // Send + CReserveKey keyChange(pwalletMain); + int64_t nFeeRequired = 0; + string strFailReason; + bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason); + if (!fCreated) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); + if (!pwalletMain->CommitTransaction(wtx, keyChange)) + throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed"); + + return wtx.GetHash().GetHex(); +} + Value addubi(const Array& params, bool fHelp) { if (fHelp || params.size() != 2) @@ -410,6 +500,7 @@ Value createservice(const Array& params, bool fHelp) "5 = Nonprofit Organization \n" "6 = DEX \n" "7 = Survey \n \n" + "8 = Non-profit groups \n \n" "\nResult:\n" "\"transactionid\" (string) The transaction id.\n" @@ -425,13 +516,15 @@ Value createservice(const Array& params, bool fHelp) // VANTAR CHECK FYRIR LENGD + int intServiceType = stoi(serviceType); + CBitcoinAddress address(serviceAddress); if (!address.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Smileycoin address"); } else if (ServiceList.IsService(serviceAddress)) { throw runtime_error("The entered address is already on service list. Please use another address."); - } else if (serviceType != "1" || serviceType != "2" || serviceType != "3"|| serviceType != "4"|| serviceType != "5" || serviceType != "6" || serviceType != "7") { - throw runtime_error("Invalid service type. Please choose a type between 1 - 7."); + } else if (intServiceType < 1 || intServiceType > 8 ) { + throw runtime_error("Invalid service type. Please choose a type between 1 - 8."); } // Amount @@ -737,10 +830,12 @@ Value getserviceaddresses(const Array& params, bool fHelp) Object root; Array tservices; /* TicketSales */ + Array uservices; /* UBI */ Array bservices; /* BookChapter */ Array nservices; /* NPO */ Array dservices; /* DEX */ Array sservices; /* Survey */ + Array nposervices; /* NP groups */ Object name_address; std::multiset>> services; @@ -755,6 +850,10 @@ Value getserviceaddresses(const Array& params, bool fHelp) name_address.push_back(Pair("name", get<1>(s->second))); name_address.push_back(Pair("address", s->first)); tservices.push_back(name_address); + } else if (get<2>(s->second) == "UBI") { // "2" + name_address.push_back(Pair("name", get<1>(s->second))); + name_address.push_back(Pair("address", s->first)); + uservices.push_back(name_address); } else if (get<2>(s->second) == "Book Chapter") { // "3" name_address.push_back(Pair("name", get<1>(s->second))); name_address.push_back(Pair("address", s->first)); @@ -770,15 +869,21 @@ Value getserviceaddresses(const Array& params, bool fHelp) } else if (get<2>(s->second) == "Survey") { // "7" name_address.push_back(Pair("name", get<1>(s->second))); name_address.push_back(Pair("address", s->first)); - sservices.push_back(name_address); + nservices.push_back(name_address); + } else if (get<2>(s->second) == "Non-profit Group") { // "8" + name_address.push_back(Pair("name", get<1>(s->second))); + name_address.push_back(Pair("address", s->first)); + nposervices.push_back(name_address); } } root.push_back(Pair("Ticket Sales", tservices)); + root.push_back(Pair("UBI", uservices)); root.push_back(Pair("Book Chapter", bservices)); root.push_back(Pair("Nonprofit Organization", nservices)); root.push_back(Pair("DEX", dservices)); root.push_back(Pair("Survey", sservices)); + root.push_back(Pair("Non-profit Group", nposervices)); return root; } @@ -840,7 +945,7 @@ Value getubilist(const Array& params, bool fHelp) ServiceList.GetServiceAddresses(services); bool isUbi = false; for(std::multiset< std::pair > >::const_iterator it = services.begin(); it!=services.end(); it++ ) { - if (get<2>(it->second) == "2") { + if (get<2>(it->second) == "UBI") { isUbi = true; } } @@ -890,45 +995,53 @@ Value getdexlist(const Array& params, bool fHelp) return obj; } -Value getnpolist(const Array& params, bool fHelp) +Value getbooklist(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) - throw runtime_error("getnpolist\n" - "Returns all non profit organization addresses\n" + throw runtime_error("getbooklist \"address\"\n" + "Returns all book chapters that belong to the specified book service address\n" ); Object obj; - /*std::multiset>> info; - - ServiceItemList.GetNpoList(info); + std::multiset>> info; + ServiceItemList.GetBookList(info); - for(std::set< std::pair< CScript, std::tuple > >::const_iterator it = info.begin(); it!=info.end(); it++ ) + //TODO bæta við book name, book author og year + for(std::set< std::pair< std::string, std::tuple > >::const_iterator it = info.begin(); it!=info.end(); it++ ) { - obj.push_back(Pair("Npo name: ", get<1>(it->second))); - obj.push_back(Pair("Npo address: ", get<2>(it->second))); - }*/ + obj.push_back(Pair("Chapter Number: ", get<2>(it->second))); + obj.push_back(Pair("Chapter Address: ", it->first)); + } return obj; } -Value getbooklist(const Array& params, bool fHelp) +Value getnpolist(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) - throw runtime_error("getbooklist \"address\"\n" - "Returns all book chapters that belong to the specified book service address\n" + throw runtime_error("getnpolist \"address\"\n" + "Returns all nps that belong to the service/type of np\n" ); + CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); + if(!address.IsValid()) + throw runtime_error("Not a valid Smileycoin address"); + + std::multiset > > services; + ServiceList.GetServiceAddresses(services); Object obj; + std::multiset>> info; - ServiceItemList.GetBookList(info); + ServiceItemList.GetNPList(info); - //TODO bæta við book name, book author og year - for(std::set< std::pair< std::string, std::tuple > >::const_iterator it = info.begin(); it!=info.end(); it++ ) + for(std::set< std::pair< std::string, std::tuple > >::const_iterator it = info.begin(); it!=info.end(); it++) { - obj.push_back(Pair("Chapter Number: ", get<2>(it->second))); - obj.push_back(Pair("Chapter Address: ", it->first)); + if (toAddress == address.ToString()) { + obj.push_back(Pair("Non-profit name: ", get<2>(it->second))); + obj.push_back(Pair("Non-profit address: ", it->first)); + } } return obj; @@ -1193,4 +1306,4 @@ Value verifymessage(const Array& params, bool fHelp) return (pubkey.GetID() == keyID); } -#pragma clang diagnostic pop \ No newline at end of file +#pragma clang diagnostic pop diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index c4999d599..9b7de719f 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -239,6 +239,7 @@ static const CRPCCommand vRPCCommands[] = /* Block chain and UTXO */ { "adddex", &adddex, false, false, false }, + { "addnpo", &addnpo, false, false, false }, { "addubi", &addubi, false, false, false }, { "addchapter", &addchapter, false, false, false }, { "getaddressinfo", &getaddressinfo, false, false, false }, @@ -253,6 +254,7 @@ static const CRPCCommand vRPCCommands[] = { "getdexlist", &getdexlist, false, false, false }, { "getnpolist", &getnpolist, false, false, false }, { "getbooklist", &getbooklist, false, false, false }, + { "getnpolist", &getnpolist, false, false, false }, { "getblockchaininfo", &getblockchaininfo, true, false, false }, { "getbestblockhash", &getbestblockhash, true, false, false }, { "getblockcount", &getblockcount, true, false, false }, diff --git a/src/rpcserver.h b/src/rpcserver.h index 91284fd8b..903e98d43 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -134,6 +134,7 @@ extern json_spirit::Value dumpwallet(const json_spirit::Array& params, bool fHel extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value adddex(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value addnpo(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value addubi(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value addchapter(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getrichaddresses(const json_spirit::Array& params, bool fHelp); diff --git a/src/serviceitemlistdb.cpp b/src/serviceitemlistdb.cpp index d8f6eddaf..b699f8dbe 100644 --- a/src/serviceitemlistdb.cpp +++ b/src/serviceitemlistdb.cpp @@ -39,6 +39,10 @@ inline std::string ChapterAction(const CServiceBook &ai) {return get<0>(ai);} inline std::string ChapterToAddress(const CServiceBook &ai) {return get<1>(ai);} inline std::string ChapterNum(const CServiceBook &ai) {return get<2>(ai);} +inline std::string NPAction(const CServiceNP &ai) {return get<0>(ai);} +inline std::string NPToAddress(const CServiceNP &ai) {return get<1>(ai);} +inline std::string NPDescription(const CServiceNP &ai) {return get<2>(ai);} + bool CServiceItemList::SetForked(const bool &fFork) { fForked = fFork; @@ -604,4 +608,147 @@ bool CServiceItemList::IsChapter(std::string address) { } } return false; -} \ No newline at end of file +} + +bool CServiceItemList::UpdateNPList(const std::map > &map) +{ + for(std::map >::const_iterator it = map.begin(); it!= map.end(); it++) + { + std::cout << get<2>(it->second) << std::endl; + + if (get<0>(it->second) == "DN") { // If op_return begins with DN (delete np) + mapServiceNPList::iterator itNP = naddresses.find(it->first); + // If key is found in dex list + if (itNP != naddresses.end()) { + naddresses.erase(itNP); + } + } else if (get<0>(it->second) == "NN") { // If op_return begins with NN (new np) + naddresses.insert(*it); + } + } + return true; +} + +// The heights need to be rolled back before new blocks are connected if any were disconnected. +// TODO: We should try to get rid of this and write the height undo information to the disk instead. +bool CServiceItemList::UpdateNPListHeights() +{ + if(!fForked) + return true; + + CBlockIndex* pindexSeek = mapBlockIndex.find(pcoinsTip->GetBestBlock())->second; + if(!chainActive.Contains(pindexSeek)) { + return false; + } + + CBlock block; + std::map > npItem; + mapServiceNPList mforkedServiceNPList; + + for(mapServiceNPList::const_iterator it = naddresses.begin(); it!=naddresses.end(); it++) + { + if (get<0>(it->second) == "DN") { // If op_return begins with DN (delete np) + mapServiceNPList::iterator itNP = naddresses.find(it->first); + // If key is found in dex list + if (itNP != naddresses.end()) { + naddresses.erase(itNP); + } + } else if (get<0>(it->second) == "NN") { // If op_return begins with NN (new np) + naddresses.insert(*it); + } + } + + if(fDebug) { + LogPrintf("%d addresses seen at fork and need to be relocated\n", mforkedServiceNPList.size()); + } + + while(pindexSeek->pprev && !mforkedServiceNPList.empty()) + { + + // return false; + + ReadBlockFromDisk(block,pindexSeek); + block.BuildMerkleTree(); + BOOST_FOREACH(const CTransaction &tx, block.vtx) + { + for(unsigned int j = 0; j < tx.vout.size(); j++) + { + CScript key = tx.vout[j].scriptPubKey; + /*mapServiceNPList::iterator it = mforkedServiceNPList.find(key); + if(it == mforkedServiceNPList.end()) + continue; + + dexItem.insert(std::make_pair(key, std::make_tuple(NPToAddress(it), NPAddress(it), NPDescription(it)))); + mforkedServiceNPList.erase(it);*/ + if(fDebug) { + CTxDestination dest; + ExtractDestination(key, dest); + CBitcoinAddress addr; + addr.Set(dest); + LogPrintf("%s found at height %d\n",addr.ToString(),pindexSeek->nHeight); + } + } + } + + CBlockUndo undo; + CDiskBlockPos pos = pindexSeek ->GetUndoPos(); + if (undo.ReadFromDisk(pos, pindexSeek->pprev->GetBlockHash())) //TODO: hvenær klikkar þetta? + { + for (unsigned int i=0; ifirst, std::make_tuple(NPToAddress(it), NPAddress(it), NPDescription(it)))); + mforkedServiceNPList.erase(it);*/ + if(fDebug) { + CTxDestination dest; + ExtractDestination(key, dest); + CBitcoinAddress addr; + addr.Set(dest); + LogPrintf("%s found at height %d\n",addr.ToString(),pindexSeek->nHeight); + } + } + } + } + else { + LogPrintf("UpdateNPListHeights(): Failed to read undo information\n"); + break; + } + pindexSeek = pindexSeek -> pprev; + } + + bool ret; + typedef std::pair > pairType; + BOOST_FOREACH(const pairType &pair, npItem) { + ret = pcoinsTip->SetNPList(pair.first, pair.second); + assert(ret); + } + + if(!UpdateNPList(npItem)) + return false; + return mforkedServiceNPList.empty(); +} + +bool CServiceItemList::GetNPList(std::multiset>> &retset) const { + for(std::map >::const_iterator it=naddresses.begin(); it!=naddresses.end(); it++) + { + retset.insert(std::make_pair(it->first, std::make_tuple(get<0>(it->second), get<1>(it->second), get<2>(it->second)))); + } + return true; +} + +bool CServiceItemList::IsNP(std::string address) { + for (std::map >::const_iterator it = naddresses.begin();it != naddresses.end(); it++) + { + // If address found on np list + if (address == it->first) { + return true; + } + } + return false; +} + diff --git a/src/serviceitemlistdb.h b/src/serviceitemlistdb.h index 702e34d8c..1d601ae1b 100644 --- a/src/serviceitemlistdb.h +++ b/src/serviceitemlistdb.h @@ -10,11 +10,13 @@ typedef std::tuple CServiceUbi; typedef std::tuple CServiceDex; typedef std::tuple CServiceBook; +typedef std::tuple CServiceNP; typedef std::map mapServiceTicketList; typedef std::map mapServiceUbiList; typedef std::map mapServiceDexList; typedef std::map mapServiceBookList; +typedef std::map mapServiceNPList; class CServiceItemList { @@ -23,6 +25,7 @@ class CServiceItemList mapServiceUbiList uaddresses; mapServiceDexList daddresses; mapServiceBookList baddresses; + mapServiceNPList naddresses; bool fForked; std::string TicketAddress(const mapServiceTicketList::iterator &it) const { return it -> first; } @@ -47,12 +50,18 @@ class CServiceItemList std::string ChapterToAddress(const mapServiceBookList::iterator &it) const { return get<1>(it -> second); } std::string ChapterNum(const mapServiceBookList::iterator &it) const { return get<2>(it -> second); } + std::string NPAddress(const mapServiceNPList::iterator &it) const { return it -> first; } + std::string NPAction(const mapServiceNPList::iterator &it) const { return get<0>(it -> second); } + std::string NPToAddress(const mapServiceNPList::iterator &it) const { return get<1>(it -> second); } + std::string NPDescription(const mapServiceNPList::iterator &it) const { return get<2>(it -> second); } + public: friend bool CCoinsViewDB::GetTicketList(CServiceItemList &ticketlist); friend bool CCoinsViewDB::GetUbiList(CServiceItemList &ubilist); friend bool CCoinsViewDB::GetDexList(CServiceItemList &dexlist); friend bool CCoinsViewDB::GetBookList(CServiceItemList &booklist); + friend bool CCoinsViewDB::GetNPList(CServiceItemList &nplist); bool GetTicketList(std::multiset>> &retset) const; bool IsTicket(std::string address); @@ -62,6 +71,8 @@ class CServiceItemList bool IsDex(std::string address); bool GetBookList(std::multiset>> &retset) const; bool IsChapter(std::string address); + bool GetNPList(std::multiset>> &retset) const; + bool IsNP(std::string address); bool SetForked(const bool &fFork); @@ -71,12 +82,14 @@ class CServiceItemList bool UpdateUbiList(const std::map > &map); bool UpdateDexList(const std::map > &map); bool UpdateBookList(const std::map > &map); + bool UpdateNPList(const std::map > &map); bool UpdateTicketListHeights(); bool UpdateUbiListHeights(); bool UpdateDexListHeights(); bool UpdateBookListHeights(); + bool UpdateNPListHeights(); }; -#endif //SMILEYCOIN_SERVICEITEMLISTDB_H \ No newline at end of file +#endif //SMILEYCOIN_SERVICEITEMLISTDB_H diff --git a/src/servicelistdb.cpp b/src/servicelistdb.cpp index 5aa6d28b2..d51297e81 100644 --- a/src/servicelistdb.cpp +++ b/src/servicelistdb.cpp @@ -108,6 +108,8 @@ bool CServiceList::GetServiceAddresses(std::multiset(it->second) == "7") { displayType = "Survey"; + } else if (get<2>(it->second) == "8") { + displayType = "Non-profit Group"; } else { displayType = get<2>(it->second); } @@ -135,6 +137,8 @@ bool CServiceList::GetMyServiceAddresses(std::multiset(it->second) == "7") { displayType = "Survey"; + } else if (get<2>(it->second) == "8") { + displayType = "Non-profit Group"; } else { displayType = get<2>(it->second); } @@ -167,4 +171,4 @@ bool CServiceList::GetTickets(std::string serviceAddress, std::multiset &value) { + // If op_return begins with "DN" (delete np ) + if (get<0>(value) == "DN") { + // Erase the ticket associated with address + batch.Erase(make_pair(DB_SERVICENPLIST, key)); + } else if (get<0>(value) == "NN") { // If op_return begins with NN + batch.Write(make_pair(DB_SERVICENPLIST, key), value); + } +} + void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { batch.Write(DB_BEST_BLOCK, hash); } @@ -182,6 +194,16 @@ bool CCoinsViewDB::SetBookList(const std::string &key, const std::tuple &value) { + return db.Read(make_pair(DB_SERVICENPLIST, key), value); +} + +bool CCoinsViewDB::SetNPList(const std::string &key, const std::tuple &value) { + CLevelDBBatch batch; + BatchWriteServiceNPList(batch, key, value); + return db.WriteBatch(batch); +} + bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) { return db.Read(make_pair(DB_COINS, txid), coins); } @@ -216,6 +238,7 @@ bool CCoinsViewDB::BatchWrite(const std::map &mapCoins, const std::map> &mapServiceUbiList, const std::map> &mapServiceDexList, const std::map> &mapServiceBookList, + const std::map> &mapServiceNPList, const uint256 &hashBlock) { LogPrint("coindb", "Committing %u changed transactions and %u address balances to coin database...\n",(unsigned int)mapCoins.size(), (unsigned int)mapAddressInfo.size()); @@ -234,6 +257,8 @@ bool CCoinsViewDB::BatchWrite(const std::map &mapCoins, BatchWriteServiceDexList(batch, it->first, it->second); for (std::map>::const_iterator it = mapServiceBookList.begin(); it != mapServiceBookList.end(); it++) BatchWriteServiceBookList(batch, it->first, it->second); + for (std::map>::const_iterator it = mapServiceNPList.begin(); it != mapServiceNPList.end(); it++) + BatchWriteServiceNPList(batch, it->first, it->second); if (hashBlock != uint256(0)) BatchWriteHashBestChain(batch, hashBlock); @@ -461,6 +486,40 @@ bool CCoinsViewDB::GetBookList(CServiceItemList &booklist) { return true; } +bool CCoinsViewDB::GetNPList(CServiceItemList &nplist) { + leveldb::Iterator *pcursor = db.NewIterator(); + CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); + ssKeySet << make_pair(DB_SERVICENPLIST, string("np")); + pcursor->Seek(ssKeySet.str()); + + while (pcursor->Valid()) + { + boost::this_thread::interruption_point(); + try + { + leveldb::Slice slKey = pcursor->key(); + CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); + std::pair key; + ssKey >> key; + if (key.first == DB_SERVICENPLIST) + { + leveldb::Slice slValue = pcursor->value(); + CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); + std::tuple npitem; + ssValue >> npitem; + nplist.naddresses.insert(make_pair(key.second, npitem)); + pcursor->Next(); + } else { + break; + } + } catch (std::exception &e) { + return error("%s : Deserialize or I/O error - %s", __func__, e.what()); + } + } + delete pcursor; + + return true; +} CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { } @@ -637,6 +696,18 @@ bool CBlockTreeDB::ReadServiceBookListFork(bool &fForked) { return true; } +bool CBlockTreeDB::WriteServiceNPListFork(bool fForked) { + if (fForked) + return Write(DB_SERVICENPLIST_FORK_FLAG, '1'); + else + return Erase(DB_SERVICENPLIST_FORK_FLAG); +} + +bool CBlockTreeDB::ReadServiceNPListFork(bool &fForked) { + fForked = Exists(DB_SERVICENPLIST_FORK_FLAG); + return true; +} + bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0'); } diff --git a/src/txdb.h b/src/txdb.h index 64834b9d8..b43df292c 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -50,6 +50,8 @@ class CCoinsViewDB : public CCoinsView bool SetDexList(const std::string &key, const std::tuple &value); bool GetBookList(const std::string &key, std::tuple &value); bool SetBookList(const std::string &key, const std::tuple &value); + bool GetNPList(const std::string &key, std::tuple &value); + bool SetNPList(const std::string &key, const std::tuple &value); bool HaveCoins(const uint256 &txid); uint256 GetBestBlock(); @@ -60,6 +62,7 @@ class CCoinsViewDB : public CCoinsView const std::map > &mapServiceUbiList, const std::map > &mapServiceDexList, const std::map > &mapServiceBookList, + const std::map > &mapServiceNPList, const uint256 &hashBlock); bool GetStats(CCoinsStats &stats); @@ -69,6 +72,7 @@ class CCoinsViewDB : public CCoinsView bool GetUbiList(CServiceItemList &ubilist); bool GetDexList(CServiceItemList &dexlist); bool GetBookList(CServiceItemList &booklist); + bool GetNPList(CServiceItemList &nplist); }; /** Access to the block database (blocks/index/) */ @@ -102,6 +106,8 @@ class CBlockTreeDB : public CLevelDBWrapper bool ReadServiceDexListFork(bool &fForked); bool WriteServiceBookListFork(bool fForked); bool ReadServiceBookListFork(bool &fForked); + bool WriteServiceNPListFork(bool fForked); + bool ReadServiceNPListFork(bool &fForked); bool WriteFlag(const std::string &name, bool fValue); bool ReadFlag(const std::string &name, bool &fValue); bool LoadBlockIndexGuts(); diff --git a/src/ubi.cpp b/src/ubi.cpp new file mode 100644 index 000000000..d6f8ffb72 --- /dev/null +++ b/src/ubi.cpp @@ -0,0 +1,109 @@ +#include "ubi.h" + +#include +#include + +#include + +#include "servicelistdb.h" +#include "serviceitemlistdb.h" + +using namespace std; + +// guards nCurrentHeigth, vBatch and vRecipients +CCriticalSection cs_ubi; + +// the height of vBatch, if the external blockheight changes then vBatch +// is updated +unsigned int nCurrentHeight = 0; + +// where we store our batch between blockchain updates +vector vBatch; + +// compare first using lastpaid, and then using the CBitcoinAddress +// builtin comparator +bool operator<(const Recipient& lhs, const Recipient& rhs) +{ + if (lhs.lastpaid != rhs.lastpaid) + return lhs.lastpaid < rhs.lastpaid; + else + return lhs.address < rhs.address; +} + +// our list of recipients from vRecipients, also only updated on a new blockchain +vector vRecipients; + +// how many recipient are paid per block +const size_t nBatchSize = 5; + +// remove once we move to servicelist, only for reading ubi_addresses +static boost::once_flag ubiInitFlag = BOOST_ONCE_INIT; + +namespace UBI +{ +static void InitCirculation() +{ + std::multiset>> ubilist; + + ServiceItemList.GetUbiList(ubilist); + + for (auto r : ubilist) + { + // the urUBI address + if (get<1>(r.second) == "BLsTgPMirCUmnd3TE5p9rBLQSXBid3KfnT") { + vRecipients.push_back({0, CBitcoinAddress(r.first)}); + } + } + + vBatch.assign(min(nBatchSize, vRecipients.size()), CScript()); +} + +// goes through vRecipients and selects the addresses that haven't been paid +// for the longest time. +vector NextBatch(const unsigned int nHeight) +{ + boost::call_once(&InitCirculation, ubiInitFlag); + + LOCK(cs_ubi); + // multiple miners run at the same time on the same block so we only need to compute + // the vBatch once for each height, if the height is the same then return the same + // batch + if (nHeight == nCurrentHeight) + return vBatch; + + // update our current height and compute the new vBatch + nCurrentHeight = nHeight; + + // choose the recipients with the lowest lastpaid values. + // if we just paid nbatchsize many recipients some would get paid multiple + // times without us knowing who, so we cap it at vRecipients.size() if it's + // lower than nBatchSize + for (unsigned int i = 0; i < min(vRecipients.size(), nBatchSize); i++) + { + // using our operator< get the iterator of the recipient in batch that + // has been paid most recently + auto it = min_element(vRecipients.begin(), vRecipients.end()); + + CScript s; + s.SetDestination(it->address.Get()); + vBatch[i] = s; + + // mark it as paid + it->lastpaid = nHeight; + } + + return vBatch; +} + +// Similar to GetBlockValueDividends but doesn't depend on block height since we only +// harvest from nFees +int64_t GetUBIDividends(const int64_t nFees) +{ + if (vRecipients.empty()) + return 0; + + return (int64_t)(9 * nFees / 10) / (int64_t)min(nBatchSize, vRecipients.size()); +} + +} // namespace UBI diff --git a/src/ubi.h b/src/ubi.h new file mode 100644 index 000000000..dc7ca5bb0 --- /dev/null +++ b/src/ubi.h @@ -0,0 +1,19 @@ +#include "base58.h" +#include "main.h" + +#include "core.h" +#include "init.h" +#include "txdb.h" + +// height last-paid and receiving address of a ubi recipient +typedef struct { + unsigned int lastpaid; + CBitcoinAddress address; +} Recipient; + +namespace UBI +{ +std::vector NextBatch(const unsigned int nHeight); +int64_t GetUBIDividends(const int64_t nFees); +} + diff --git a/src/util.cpp b/src/util.cpp index 12cc872a3..f75718dac 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -97,6 +97,7 @@ bool fServer = false; string strMiscWarning; bool fNoListen = false; bool fLogTimestamps = false; +bool fUBI = false; volatile bool fReopenDebugLog = false; CClientUIInterface uiInterface; diff --git a/src/util.h b/src/util.h index 2696c4e55..e972c68d0 100644 --- a/src/util.h +++ b/src/util.h @@ -106,6 +106,7 @@ extern bool fServer; extern std::string strMiscWarning; extern bool fNoListen; extern bool fLogTimestamps; +extern bool fUBI; extern volatile bool fReopenDebugLog; void RandAddSeed();