diff --git a/src/Makefile.am b/src/Makefile.am index e371e4e..d1fb827 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -205,6 +205,7 @@ DEVCOIN_CORE_H = \ psbt.h \ random.h \ randomenv.h \ + receiver.h \ reverse_iterator.h \ rpc/blockchain.h \ rpc/client.h \ @@ -358,6 +359,7 @@ libdevcoin_server_a_SOURCES = \ policy/rbf.cpp \ policy/settings.cpp \ pow.cpp \ + receiver.cpp \ rest.cpp \ rpc/blockchain.cpp \ rpc/mining.cpp \ diff --git a/src/devcoin.h b/src/devcoin.h index c36c666..8da08c2 100644 --- a/src/devcoin.h +++ b/src/devcoin.h @@ -2,14 +2,22 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef DEVCOIN_DEVCOIN_H +#define DEVCOIN_DEVCOIN_H + +#include +#include +#include + inline int64_t roundint64(double d) { return (int64_t)(d > 0 ? d + 0.5 : d - 0.5); } -//DEVCOIN globals static const int64_t initialSubsidy = 50000*COIN ; -static const int64_t share = roundint64(initialSubsidy * 0.9); -static const int64_t fallbackReduction = roundint64((initialSubsidy + share) / 2); -static const int step = 4000; +static const int64_t devcoinShare = roundint64(initialSubsidy * 0.9); +static const int64_t fallbackReduction = roundint64((initialSubsidy + devcoinShare) / 2); +static const int devcoinStep = 4000; const std::string receiverCSV = std::string("receiver.csv"); + +#endif // DEVCOIN_DEVCOIN_H \ No newline at end of file diff --git a/src/miner.cpp b/src/miner.cpp index 940aff6..544c72d 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -121,7 +123,11 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc assert(pindexPrev != nullptr); nHeight = pindexPrev->nHeight + 1; - pblock->nVersion = g_versionbitscache.ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); + // DEVCOIN + const int32_t nChainId = chainparams.GetConsensus ().nAuxpowChainId; + const int32_t nVersion = g_versionbitscache.ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); + pblock->SetBaseVersion(nVersion, nChainId); + // -regtest only: allow overriding block.nVersion with // -blockversion=N to test forking scenarios if (chainparams.MineBlocksOnDemand()) @@ -158,9 +164,51 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc CMutableTransaction coinbaseTx; coinbaseTx.vin.resize(1); coinbaseTx.vin[0].prevout.SetNull(); - coinbaseTx.vout.resize(1); + + // DEVCOIN + std::wstring dataDirectory = gArgs.GetDataDirBase().wstring(); + std::string dataDirectoryString(dataDirectory.begin(), dataDirectory.end()); + vector coinAddressStrings = getCoinAddressStrings(dataDirectoryString, receiverCSV, (int)pindexPrev->nHeight+1, devcoinStep); + coinbaseTx.vout.resize(coinAddressStrings.size() + 1); coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn; - coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); + + // Prepare to pay beneficiaries + int64_t nFees = 0; + int64_t minerValue = GetBlockSubsidy(nHeight, chainparams.GetConsensus()); + int64_t sharePerAddress = 0; + if (coinAddressStrings.size() == 0) + minerValue -= fallbackReduction; + else + sharePerAddress = roundint64(devcoinShare / (int64_t)coinAddressStrings.size()); + + LogPrintf("%s: coinAddressStrings: %d\n", __func__, coinAddressStrings.size()); + for (unsigned int i=0; i < coinAddressStrings.size(); i++) + { + // create transaction + const std::string coinAddressString = coinAddressStrings[i]; + string error_str; + + CTxDestination beneficiaryAddr = DecodeDestination(coinAddressString, error_str); + if (!IsValidDestination(beneficiaryAddr)) + { + LogPrintf("%s: Address not valid! %s %s\n", __func__, coinAddressString.c_str(), error_str); + return NULL; + } + + CScript scriptPubKey = GetScriptForDestination(beneficiaryAddr); + coinbaseTx.vout[i + 1] = CTxOut(sharePerAddress, scriptPubKey); + + if (coinbaseTx.vout[i + 1].nValue < 0) + { + LogPrintf("%s: negative vout value: %d\n", __func__, coinbaseTx.vout[i + 1].nValue); + coinbaseTx.vout[i + 1].nValue = 0; + } + + minerValue -= sharePerAddress; + LogPrintf("%s: Address %s valid, value %d, minerValue %d\n", __func__, coinAddressString.c_str(), coinbaseTx.vout[i + 1].nValue, minerValue); + } + + coinbaseTx.vout[0].nValue = minerValue + nFees; coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx)); pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus()); diff --git a/src/receiver.cpp b/src/receiver.cpp new file mode 100644 index 0000000..816dfc9 --- /dev/null +++ b/src/receiver.cpp @@ -0,0 +1,757 @@ +// Copyright (c) 2009-2022 The Bitcoin Core and Devcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#define CURL_STATICLIB +extern "C" { +#include +} +#include +#include +#include +#include +#include + +using namespace boost; +using namespace std; + +size_t curlWriteFunction(void* buf, size_t size, size_t nmemb, void* userp) +{ + if(userp) + { + std::ostream& os = *static_cast(userp); + std::streamsize len = size * nmemb; + + if(os.write(static_cast(buf), len)) + return len; + } + + return 0; +} + +string getCachedText(const string& fileName) +{ + if (globalCacheMap.count(fileName) == 0) + globalCacheMap[fileName] = getFileText(fileName); + + return globalCacheMap[fileName]; +} + +vector getCoinAddressStrings(const string& dataDirectory, const string& fileName, int height, int step) +{ + vector coinList; + vector > coinLists; + string stepOutput = getStepOutput(dataDirectory, fileName, height, step); + vector textLines = getTextLines(stepOutput); + bool isCoinSection = false; + string oldToken = string(); + + for (unsigned int lineIndex = 0; lineIndex < textLines.size(); lineIndex++) + { + string firstLowerSpaceless = string(); + string line = textLines[lineIndex]; + vector words = getCommaDividedWords(line); + + if (words.size() > 0) + firstLowerSpaceless = getReplaced(getLower(words[0])); + + if (firstLowerSpaceless == string("coin")) + { + vector coinList = getTokens(words[1], ","); + coinLists.push_back(coinList); + } + + if (firstLowerSpaceless == string("_endcoins") || firstLowerSpaceless == string("_endaddresses")) + isCoinSection = false; + + if (isCoinSection) + { + vector coinList = getTokens(line, ","); + coinLists.push_back(coinList); + } + + if (firstLowerSpaceless == string("_begincoins") || firstLowerSpaceless == string("_beginaddresses")) + isCoinSection = true; + } + + if ((unsigned int)coinLists.size() == 0) + { + LogPrintf("%s: Warning, no coin lists were found for the file: %s\n", __func__, fileName.c_str()); + return getTokens(); + } + + int remainder = height - step * (height / step); + int modulo = remainder % (int)coinLists.size(); + + vector originalList = coinLists[modulo]; + for (vector::iterator tokenIterator = originalList.begin(); tokenIterator != originalList.end(); tokenIterator++) + { + if (*tokenIterator != string("=")) + oldToken = tokenIterator->substr(); + + coinList.push_back(oldToken); + } + return coinList; +} + +vector getCommaDividedWords(const string& text) +{ + vector commaDividedWords; + size_t commaIndex = text.find(','); + + if (commaIndex == string::npos) + { + commaDividedWords.push_back(text); + return commaDividedWords; + } + + commaDividedWords.push_back(text.substr(0, commaIndex)); + commaDividedWords.push_back(text.substr(commaIndex + 1)); + return commaDividedWords; +} + +string getCommonOutputByText(const string& fileText, const string& suffix) +{ + if (suffix == string("0") || suffix == string("1")) + { + string receiverFileName; + ArgsManager argsman; + string chain = argsman.GetChainName(); + if (CBaseChainParams::MAIN == chain) { + receiverFileName = string("receiver_") + suffix + string(".csv"); + } + else if (CBaseChainParams::TESTNET == chain) { + receiverFileName = string("receiverTestNet_") + suffix + string(".csv"); + } + else if (CBaseChainParams::SIGNET == chain) { + receiverFileName = string("receiverSigNet_") + suffix + string(".csv"); + } + else if (CBaseChainParams::REGTEST == chain) { + receiverFileName = string("receiverRegTest_") + suffix + string(".csv"); + } + if (getExists(receiverFileName)) + return getFileText(receiverFileName); + } + + vector peerNames = getPeerNames(fileText); + vector pages = getLocationTexts(getSuffixedFileNames(peerNames, suffix)); + int minimumIdentical = (int)ceil(globalMinimumIdenticalProportion * (double)pages.size()); + map pageMap; + + for (vector::iterator pageIterator = pages.begin(); pageIterator != pages.end(); pageIterator++) + { + string firstLine = string(); + vector lines = getTextLines(*pageIterator); + string textWithoutWhitespace = getTextWithoutWhitespaceByLines(lines); + + if (lines.size() > 0) + firstLine = getLower(lines[0]); + + if (getStartsWith(firstLine, string("format")) && (firstLine.find(string("pluribusunum")) != string::npos)) + { + if (pageMap.count(textWithoutWhitespace)) + pageMap[textWithoutWhitespace] += 1; + else + pageMap[textWithoutWhitespace] = 1; + } + } + + LogPrintf("%s: Number of pages in getCommonOutputByText: %d\n", __func__, pages.size()); + + for (map::iterator pageMapIterator = pageMap.begin(); pageMapIterator != pageMap.end(); pageMapIterator++) { + if ((*pageMapIterator).second >= minimumIdentical) + return (*pageMapIterator).first; + + LogPrintf("%s: Number of identical pages in getCommonOutputByText: %d\n", __func__, (*pageMapIterator).second); + } + + LogPrintf("%s: Insufficient identical pages in getCommonOutputByText.\n", __func__); + + return string(); +} + +vector getDirectoryNames(const string& directoryName) +{ + vector directoryNames; + + if (!getExists(directoryName)) + { + LogPrintf("%s: Warning, can not open directory: %s\n", __func__, directoryName.c_str()); + return directoryNames; + } + + boost::filesystem::directory_iterator endIterator; + + for (boost::filesystem::directory_iterator directoryIterator(directoryName); directoryIterator != endIterator; ++directoryIterator) + { + if (!boost::filesystem::is_directory(directoryIterator->status())) + directoryNames.push_back(directoryIterator->path().string()); + } + + return directoryNames; +} + +string getDirectoryPath(const string& fileName) +{ + string directoryPath = (boost::filesystem::path(fileName)).parent_path().string(); + if (directoryPath == string()) + return string("."); + return directoryPath; +} + +double getDouble(const string& doubleString) +{ + double doublePrecision; + istringstream doubleStream(doubleString); + + doubleStream >> doublePrecision; + return doublePrecision; +} + +bool getExists(const string& fileName) +{ + return boost::filesystem::exists(fileName); +} + +double getFileRandomNumber(const string& dataDirectory, const string& fileName) +{ + string directoryPath = dataDirectory.substr(); + if (dataDirectory == string()) + directoryPath = getDirectoryPath(fileName); + string numberFilePath = getJoinedPath(directoryPath, string("random_number.txt")); + string numberFileText = getFileText(numberFilePath); + + if (numberFileText == string()) + { + srand(time(NULL)); + double randomDouble = ((double)(rand() % 10000) + 0.5) / 10000.0; + numberFileText = getStringByDouble(randomDouble); + writeFileText(numberFilePath, numberFileText); + } + + return getDouble(numberFileText); +} + +string getFileText(const string& fileName) +{ + ifstream fileStream(fileName.c_str()); + + if (!fileStream.is_open()) + return string(); + + string fileText; + fileStream.seekg(0, ios::end); + fileText.reserve(fileStream.tellg()); + fileStream.seekg(0, ios::beg); + fileText.assign((istreambuf_iterator(fileStream)), istreambuf_iterator()); + fileStream.close(); + return fileText; +} + +string getHttpsText(const string& address) +{ + + CURL *curl; + long http_code; + string ret = string(); + + curl = curl_easy_init(); + + if(curl) + { + CURLcode code; + std::ostringstream oss; +// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); +// curl_easy_setopt(curl, CURLOPT_HEADER, 1L); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &oss); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curlWriteFunction); + curl_easy_setopt(curl, CURLOPT_URL, address.c_str()); + code = curl_easy_perform(curl); +// printf("Curl-- Response: %d\n", code); + + if (code == CURLE_OK) { + code = curl_easy_getinfo(curl, CURLINFO_HTTP_CODE, &http_code); + // printf("Http Response: %l\n", http_code); + if (http_code == 200) ret = oss.str(); + } + } + curl_easy_cleanup(curl); + return ret; +} + +int getInt(const string& integerString) +{ + try + { + int integer; + istringstream intStream(integerString); + + intStream >> integer; + return integer; + } + catch (std::exception& e) + { + LogPrintf("%s: Could not get int for: %s\n", __func__, integerString); + LogPrintf("%s: Exception: %s\n", __func__, e.what()); + return 0; + } +} + +bool getIsSufficientAmount(vector addressStrings, vector amounts, const string& dataDirectory, const string& fileName, int height, int64_t share, int step) +{ + vector coinAddressStrings = getCoinAddressStrings(dataDirectory, fileName, height, step); + map receiverMap; + + if (coinAddressStrings.size() == 0) + { + LogPrintf("%s: No coin addresses were found, there may be something wrong with the receiver_x.csv files.\n", __func__); + return false; + } + + int64_t sharePerAddress = share / (int64_t)coinAddressStrings.size(); + + for (unsigned int i = 0; i < coinAddressStrings.size(); i++) + receiverMap[coinAddressStrings[i]] = (int64_t)0; + + for (unsigned int i = 0; i < addressStrings.size(); i++) + { + string addressString = addressStrings[i]; + + if (receiverMap.count(addressString) > 0) + receiverMap[addressString] += amounts[i]; + } + + for (unsigned int i = 0; i < coinAddressStrings.size(); i++) + { + if (receiverMap[coinAddressStrings[i]] < sharePerAddress) + { + LogPrintf("%s: In receiver.h, getIsSufficientAmount rejected the addresses or amounts.\n", __func__); + LogPrintf("For the given:\n"); + LogPrintf("- Height: %d\n", height); + LogPrintf("- Share: %d\n", share); + LogPrintf("- Step: %d\n", step); + LogPrintf("The receiverMap shows that they should get %d coins\n", receiverMap[coinAddressStrings[i]]); + LogPrintf("The shares per address however are set to %d\n", sharePerAddress); + LogPrintf("The expected addresses are:\n"); + for (unsigned int i = 0; i < coinAddressStrings.size(); i++) + LogPrintf(" %s\n", coinAddressStrings[i]); + + LogPrintf("The given addresses are:\n"); + for (unsigned int i = 0; i < addressStrings.size(); i++) + LogPrintf(" %s\n", addressStrings[i]); + + LogPrintf("The given amounts are:\n"); + for (unsigned int i = 0; i < amounts.size(); i++) + LogPrintf(" %d\n", amounts[i]); + + return false; + } + } + + return true; +} + +string getJoinedPath(const string& directoryPath, const string& fileName) +{ + boost::filesystem::path completePath = boost::filesystem::system_complete(boost::filesystem::path(directoryPath)); + return (completePath / (boost::filesystem::path(fileName))).string(); +} + +string getLocationText(const string& address) +{ + + if (getStartsWith(address, string("https://")) || getStartsWith(address, string("http://"))) + return getHttpsText(address); + + return getFileText(address); +} + +vector getLocationTexts(vector addresses) +{ + vector locationTexts; + + for(unsigned int addressIndex = 0; addressIndex < addresses.size(); addressIndex++) { + LogPrintf("%s: Fetching %s ...\n", __func__, addresses[addressIndex]); + const string locationText = getLocationText(addresses[addressIndex]); + LogPrintf("%s: got %d bytes\n", __func__, locationText.length()); + locationTexts.push_back(locationText); + } + + return locationTexts; +} + +string getLower(const string& text) +{ + unsigned int textLength = text.length(); + string lower = text.substr(); + + for(unsigned int characterIndex = 0; characterIndex < textLength; characterIndex++) + { + lower[characterIndex] = tolower(text[characterIndex]); + } + + return lower; +} + +vector getPeerNames(const string& text) +{ + bool isPeerSection = false; + vector peerNames; + vector textLines = getTextLines(text); + + for (unsigned int lineIndex = 0; lineIndex < textLines.size(); lineIndex++) + { + string firstLowerSpaceless = string(); + string line = textLines[lineIndex]; + vector words = getCommaDividedWords(line); + + if (words.size() > 0) + firstLowerSpaceless = getReplaced(getLower(words[0])); + + if (firstLowerSpaceless == string("peer")) + peerNames.push_back(getReplaced(words[1])); + + if (firstLowerSpaceless == string("_endpeers")) + isPeerSection = false; + + if (isPeerSection) + peerNames.push_back(getReplaced(words[0])); + + if (firstLowerSpaceless == string("_beginpeers")) + isPeerSection = true; + } + + return peerNames; +} + +string getReplaced(const string& text, const string& searchString, const string& replaceString) +{ + string::size_type position = 1; + string replaced = text.substr(); + + // position - 1 is to delete a repeated searchString + while ((position = replaced.find(searchString, position - 1)) != string::npos) + { + replaced.replace(position, searchString.size(), replaceString ); + position++; + } + + return replaced; +} + +bool getStartsWith(const string& firstString, const string& secondString) +{ + if (firstString.substr(0, secondString.size()) == secondString) + return true; + + return false; +} + +string getStepFileName(const string& fileName, int height, int step) +{ + return getSuffixedFileName(fileName, getStringByInt(height / step)); +} + +string getStepOutput(const string& directoryPathInput, const string& fileName, int height, int step) +{ + + string directoryPath = string(); + + if (directoryPathInput != string()) + directoryPath = getJoinedPath(directoryPathInput, fileName.substr(0, fileName.rfind('.'))); + + // To fix wrong receiver file problem and old receiver chain problem. +// string stepFileName = getStepFileName(fileName, height, step); +// if (stepFileName == string("receiver_1.csv")) +// { +// if (getExists(stepFileName)) +// writeFileTextByDirectory(directoryPath, stepFileName, getFileText(stepFileName)); +// } + +// if (stepFileName == string("receiver_2.csv")) +// { +// if (getExists(stepFileName)) +// writeFileTextByDirectory(directoryPath, stepFileName, getFileText(stepFileName)); +// } + string stepText = getStepText(directoryPath, fileName, height, step); + + if (stepText != string()) + { + writeNextIfValueHigher(directoryPath, fileName, height, step, stepText); + return stepText; + } + + int valueDown = height - step; + string previousText = string(); + while (valueDown >= 0) + { + previousText = getStepText(directoryPath, fileName, valueDown, step); + + if (previousText != string()) + return getStepTextRecursively(directoryPath, fileName, height, previousText, step, valueDown); + + valueDown -= step; + } + + return string(); +} + +string getStepText(const string& dataDirectory, const string& fileName, int height, int step) +{ + + string stepFileName = getStepFileName(fileName, height, step); + if (dataDirectory == string()) + return getFileText(stepFileName); + + string directorySubName = getJoinedPath(dataDirectory, stepFileName); + if (getExists(directorySubName)) + { + return getCachedText(directorySubName); + } +// return getFileText(directorySubName); + + string stepText = getFileText(stepFileName); + + if (stepText == string()) + { + LogPrintf("%s: Need to download %s to %s\n", __func__, stepFileName, directorySubName); + if (stepFileName == string("receiver_0.csv")) { + string peerText = string("_beginpeers\n"); + peerText += string("https://galaxies.mygamesonline.org/receiver.csv\n"); + peerText += string("https://receiver01.devcoin.org/receiver.csv\n"); + peerText += string("https://receiver02.devcoin.org/receiver/receiver.csv\n"); + peerText += string("https://receiver03.devcoin.org/receiver.csv\n"); + peerText += string("https://receiver04.devcoin.org/receiver.csv\n"); + peerText += string("_endpeers\n"); + stepText = getCommonOutputByText(peerText, string("0")); + if (getStartsWith(stepText, string("Format,pluribusunum"))) + writeFileText(directorySubName, stepText); + } + else if (stepFileName == string("receiverTestNet_0.csv")) { + string peerText = string("_beginpeers\n"); + peerText += string("https://testnet-receiver01.devcoin.org/receiverTestNet.csv\n"); + peerText += string("https://testnet-receiver02.devcoin.org/receiverTestNet.csv\n"); + peerText += string("_endpeers\n"); + stepText = getCommonOutputByText(peerText, string("0")); + if (getStartsWith(stepText, string("Format,pluribusunum"))) + writeFileText(directorySubName, stepText); + } + else if (stepFileName == string("receiverSigNet_0.csv")) { + string peerText = string("_beginpeers\n"); + peerText += string("https://signet-receiver01.devcoin.org/receiverSigNet.csv\n"); + peerText += string("https://signet-receiver02.devcoin.org/receiverSigNet.csv\n"); + peerText += string("_endpeers\n"); + stepText = getCommonOutputByText(peerText, string("0")); + if (getStartsWith(stepText, string("Format,pluribusunum"))) + writeFileText(directorySubName, stepText); + } + else if (stepFileName == string("receiverRegTest_0.csv")) { + string peerText = string("_beginpeers\n"); + peerText += string("https://signet-receiver01.devcoin.org/receiverRegTest.csv\n"); + peerText += string("https://signet-receiver02.devcoin.org/receiverRegTest.csv\n"); + peerText += string("_endpeers\n"); + stepText = getCommonOutputByText(peerText, string("0")); + if (getStartsWith(stepText, string("Format,pluribusunum"))) + writeFileText(directorySubName, stepText); + } + else + return string(); + } + + writeFileText(directorySubName, stepText); + return stepText; +} + +string getStepTextRecursively(const string& directoryPath, const string& fileName, int height, const string& previousTextInput, int step, int valueDown) +{ + string previousText = previousTextInput.substr(); + string stepFileName; + + for(int valueUp = valueDown; valueUp < height; valueUp += step) + { + int nextValue = valueUp + step; + previousText = getCommonOutputByText(previousText, getStringByInt(nextValue / step)); + stepFileName = getStepFileName(fileName, nextValue, step); + writeFileTextByDirectory(directoryPath, stepFileName, previousText); + + if (ShutdownRequested()) break; + } + + return previousText; +} + +string getStringByBoolean(bool boolean) +{ + if (boolean) + return string("true"); + return string("false"); +} + +string getStringByDouble(double doublePrecision) +{ + ostringstream doubleStream; + + doubleStream << doublePrecision; + + return doubleStream.str(); +} + +string getStringByInt(int integer) +{ + ostringstream integerStream; + + integerStream << integer; + + return integerStream.str(); +} + +string getSuffixedFileName(const string& fileName, const string& suffix) +{ + if (suffix == string()) + return fileName; + + size_t lastDotIndex = fileName.rfind("."); + + if (lastDotIndex == string::npos) + return fileName + suffix; + return fileName.substr(0, lastDotIndex) + string("_") + suffix + fileName.substr(lastDotIndex); +} + +vector getSuffixedFileNames(vector fileNames, const string& suffix) +{ + vector suffixedFileNames; + + for (unsigned int fileNameIndex = 0; fileNameIndex < fileNames.size(); fileNameIndex++) + { + string fileName = fileNames[fileNameIndex]; + size_t doNotAddSuffixIndex = fileName.find("_do_not_add_suffix_"); + + if (doNotAddSuffixIndex == string::npos) + suffixedFileNames.push_back(getSuffixedFileName(fileName, suffix)); + else + suffixedFileNames.push_back(fileName.substr(0, doNotAddSuffixIndex)); + } + + return suffixedFileNames; +} + +vector getTextLines(const string& text) +{ + return getTokens(getReplaced(getReplaced(text, string("\r"), string("\n")), string("\n\n"), string("\n")), string("\n")); +} + +string getTextWithoutWhitespaceByLines(vector lines) +{ + string textWithoutWhitespace = string(); + + for (vector::iterator lineIterator = lines.begin(); lineIterator != lines.end(); lineIterator++) + { + string line = getReplaced(*lineIterator); + + if (line.size() > 0) + textWithoutWhitespace += line + string("\n"); + } + + return textWithoutWhitespace; +} + +vector getTokens(const string& text, const string& delimiters) +{ + vector tokens; + string::size_type lastPosition = text.find_first_not_of(delimiters, 0); + string::size_type position = text.find_first_of(delimiters, lastPosition); + + while (string::npos != position || string::npos != lastPosition) + { + tokens.push_back(text.substr(lastPosition, position - lastPosition)); + lastPosition = text.find_first_not_of(delimiters, position); + position = text.find_first_of(delimiters, lastPosition); + } + + return tokens; +} + +void makeDirectory(const string& directoryPath) +{ + if (getReplaced(directoryPath) == string() || directoryPath == string(".")) + return; + + if (getExists(directoryPath)) + return; + + if (boost::filesystem::create_directories(boost::filesystem::path(directoryPath))) + LogPrintf("%s: The following directory was made: %s\n", __func__, directoryPath.c_str()); + else + LogPrintf("%s: Receiver.h can not make the directory %s so give it read/write permission for that directory.\n", __func__, directoryPath.c_str()); +} + +void writeFileText(const string& fileName, const string& fileText) +{ + if (fileText == string()) + { + LogPrintf("%s: Warning, writeFileText in receiver.h won't write the file:\n%s\nbecause the text is blank.\n", __func__, fileName); + return; + } + + makeDirectory(getDirectoryPath(fileName)); + ofstream fileStream(fileName.c_str()); + + if (fileStream.is_open()) + { + fileStream << fileText; + fileStream.close(); + } + else LogPrintf("%s: The file %s can not be written to.\n", __func__, fileName.c_str()); +} + +void writeFileTextByDirectory(const string& directoryPath, const string& fileName, const string& fileText) +{ + writeFileText(getJoinedPath(directoryPath, fileName), fileText); +} + +void writeNextIfValueHigher(const string& directoryPath, const string& fileName, int height, int step, const string& stepText) +{ + int remainder = height - step * (height / step); + double aboveThreshold = globalLessThanOneMinusThreshold * getFileRandomNumber(directoryPath, fileName); + int remainderThreshold = (int)(double(step) * (globalWriteNextThreshold + aboveThreshold)); + string writeNextWhenFileName = getJoinedPath(directoryPath, string("write_next_when.txt")); + + if (remainder < remainderThreshold) + return; + + if (getExists(writeNextWhenFileName)) + { + if (remainder < getInt(getFileText(writeNextWhenFileName))) + return; + else + remove(writeNextWhenFileName.c_str()); + } + + int nextValue = height + step; + string nextFileName = getJoinedPath(directoryPath, getStepFileName(fileName, nextValue, step)); + + if (!getExists(nextFileName)) + { + string nextText = getCommonOutputByText(stepText, getStringByInt(nextValue / step)); + + if (nextText == string()) + { + int addition = 10; + + if (remainder > (int)(globalLessThanOne * (double)step)) + addition = 3; + + writeFileText(writeNextWhenFileName, getStringByInt(remainder + addition)); + } + else + writeFileText(nextFileName, nextText); + } +} diff --git a/src/receiver.h b/src/receiver.h new file mode 100644 index 0000000..a31b508 --- /dev/null +++ b/src/receiver.h @@ -0,0 +1,207 @@ +// Copyright (c) 2009-2022 The Bitcoin Core and Devcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef DEVCOIN_RECEIVER_H +#define DEVCOIN_RECEIVER_H + +#include +#include + +using namespace boost; +using namespace std; + +static map globalCacheMap; +static const double globalMinimumIdenticalProportion = 0.500001; +static int globalTimeOut = 10; +static int globalTimeOutDouble = globalTimeOut + globalTimeOut; +static const double globalWriteNextThreshold = 0.75; +static const double globalLessThanOne = 0.95; +static const double globalLessThanOneMinusThreshold = globalLessThanOne * (1.0 - globalWriteNextThreshold); + +/** + * Callback function writes data to a std::ostream. + */ +size_t curlWriteFunction(void* buf, size_t size, size_t nmemb, void* userp); + +/** + * /Get the cached text or read it from a file. + */ +string getCachedText(const string& fileName); + +/** + * Get the coin address strings for a height. + */ +vector getCoinAddressStrings(const string& dataDirectory, const string& fileName, int height, int step=devcoinStep); + +/** + * Get the words divided around the comma. + */ +vector getCommaDividedWords(const string& text); + +/** + * Get the common output according to the peers listed in a text. + */ +string getCommonOutputByText(const string& fileName, const string& suffix=string("")); + +/** + * Get the vector of directory names of the given directory. + */ +vector getDirectoryNames(const string& directoryName); + +/** + * Get the directory name of the given file. + */ +string getDirectoryPath(const string& fileName); + +/** + * Get a double precision float from a string. + */ +double getDouble(const string& doubleString); + +/** + * Determine if the file exists. + */ +bool getExists(const string& fileName); + +/** + * Get the random number from a file random_number in the same directory as the given file. + */ +double getFileRandomNumber(const string& dataDirectory, const string& fileName); + +/** + * Get the entire text of a file. + */ +string getFileText(const string& fileName); + +/** + * Get the entire text of an https page. + */ +string getHttpsText(const string& address); + +/** + * Get an integer from a string. + */ +int getInt(const string& integerString); + +/** + * Determine if the transactions add up to a Devcoin share per address for each address. + */ +bool getIsSufficientAmount(vector addressStrings, vector amounts, const string& dataDirectory, const string& fileName, int height, int64_t share, int step=devcoinStep); + +/** + * Get the directory path joined with the file name. + */ +string getJoinedPath(const string& directoryPath, const string& fileName); + +/** + * Get the page by the address, be it a file name or hypertext address. + */ +string getLocationText(const string& address); + +/** + * Get the pages by the addresses, be they file names or hypertext addresses. + */ +vector getLocationTexts(vector addresses); + +/** + * Get the lowercase string. + */ +string getLower(const string& text); + +/** + * Get the peer names from the text. + */ +vector getPeerNames(const string& text); + +/** + * Get the string with the search string replaced with the replace string. + */ +string getReplaced(const string& text, const string& searchString=string(" "), const string& replaceString=string()); + +/** + * Determine if the first string starts with the second string. + */ +bool getStartsWith(const string& firstString, const string& secondString); + +/** + * Get the step file name by the file name. + */ +string getStepFileName(const string& fileName, int height, int step); + +/** + * Get the step output according to the peers listed in a file. + */ +string getStepOutput(const string& directoryPathInput, const string& fileName, int height, int step); + +/** + * Get the step text by the file name. + */ +string getStepText(const string& dataDirectory, const string& fileName, int height, int step); + +/** + * Get the step text recursively. + */ +string getStepTextRecursively(const string& directoryPath, const string& fileName, int height, const string& previousTextInput, int step, int valueDown); + +/** + * Get the string from the boolean. + */ +string getStringByBoolean(bool boolean); + +/** + * Get the string from the double precision float. + */ +string getStringByDouble(double doublePrecision); + +/** + * Get the string from the integer. + */ +string getStringByInt(int integer); + +/** + * Get the file name with the suffix just before the extension. + */ +string getSuffixedFileName(const string& fileName, const string& suffix=string()); + +/** + * Get the file names with the suffixes just before the extension. + */ +vector getSuffixedFileNames(vector fileNames, const string& suffix=string()); + +/** + * Get all the lines of text of a text. + */ +vector getTextLines(const string& text); + +/** + * Get the text without whitespace, joined with newlines in between. + */ +string getTextWithoutWhitespaceByLines(vector lines); + +/** + * Get the tokens of the text split by the delimeters. + */ +vector getTokens(const string& text=string(), const string& delimiters=string(" ")); + +/** + * Make a directory if it does not already exist. + */ +void makeDirectory(const string& directoryPath); + +/** + * Write a text to a file. + */ +void writeFileText(const string& fileName, const string& fileText); + +/** + * Write a text to a file joined to the directory path. + */ +void writeFileTextByDirectory(const string& directoryPath, const string& fileName, const string& fileText); + +/** + * Write next step file if height is higher than the threshold. + */ +void writeNextIfValueHigher(const string& directoryPath, const string& fileName, int height, int step, const string& stepText); + +#endif // DEVCOIN_RECEIVER_H diff --git a/src/validation.cpp b/src/validation.cpp index 2563641..7071833 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -22,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -2047,6 +2049,36 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5; LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime6 - nTime5), nTimeCallbacks * MICRO, nTimeCallbacks * MILLI / nBlocksTotal); + // DEVCOIN + // Check that the required Devcoin share was sent to each beneficiary + if (block.vtx[0]->GetValueOut() > (GetBlockSubsidy(pindex->nHeight, m_params.GetConsensus()) - fallbackReduction)) + { + std::vector addressStrings; + std::vector amounts; + + for (unsigned int i = 1; i < block.vtx[0]->vout.size(); i++) + { + CTxDestination address; + if (ExtractDestination(block.vtx[0]->vout[i].scriptPubKey, address)) + { + if (IsValidDestination(address)) { + addressStrings.push_back(EncodeDestination(address)); + amounts.push_back(block.vtx[0]->vout[i].nValue); + } + } + } + + std::wstring dataDirectory = gArgs.GetDataDirBase().wstring(); + std::string dataDirectoryString(dataDirectory.begin(), dataDirectory.end()); + + if (!getIsSufficientAmount(addressStrings, amounts, dataDirectoryString, receiverCSV, (int)pindex->nHeight, devcoinShare, devcoinStep) && !ShutdownRequested()) { + // This error prevents the node from accepting further blocks. + // If the node is shutdown manually, next time the node will + // shutdown itself if the error persists. + return error("%s: Share to beneficiary is insufficient"); + } + } + return true; }