// Copyright (c) 2014-2015 The Dash developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "masternode-budget.h" #include "masternode.h" #include "darksend.h" #include "masternodeman.h" #include "util.h" #include "addrman.h" #include #include CBudgetManager budget; CCriticalSection cs_budget; std::map mapSeenMasternodeBudgetProposals; std::map mapSeenMasternodeBudgetVotes; std::map mapOrphanMasternodeBudgetVotes; std::map mapSeenFinalizedBudgets; std::map mapSeenFinalizedBudgetVotes; std::map mapOrphanFinalizedBudgetVotes; std::map askedForSourceProposalOrBudget; int nSubmittedFinalBudget; int GetBudgetPaymentCycleBlocks(){ if(Params().NetworkID() == CBaseChainParams::MAIN) return 16616; //(60*24*30)/2.6 //for testing purposes return 50; } void CheckOrphanVotes() { std::map::iterator it1 = mapOrphanMasternodeBudgetVotes.begin(); while(it1 != mapOrphanMasternodeBudgetVotes.end()){ if(budget.UpdateProposal(((*it1).second), NULL)){ LogPrintf("CheckOrphanVotes: Proposal/Budget is known, activating and removing orphan vote\n"); mapOrphanMasternodeBudgetVotes.erase(it1++); } else { ++it1; } } std::map::iterator it2 = mapOrphanFinalizedBudgetVotes.begin(); while(it2 != mapOrphanFinalizedBudgetVotes.end()){ if(budget.UpdateFinalizedBudget(((*it2).second),NULL)){ LogPrintf("CheckOrphanVotes: Proposal/Budget is known, activating and removing orphan vote\n"); mapOrphanFinalizedBudgetVotes.erase(it2++); } else { ++it2; } } } void CBudgetManager::ResignInvalidProposals() { if(!fMasterNode){ CheckSignatureValidity(); return; } CBlockIndex* pindexPrev = chainActive.Tip(); if(pindexPrev == NULL) return; //pick a few masternodes responsible for this each cycle int n = mnodeman.GetMasternodeRank(activeMasternode.vin, pindexPrev->nHeight, MIN_BUDGET_PEER_PROTO_VERSION); if(n == -1) { CheckSignatureValidity(); LogPrintf("CBudgetManager::ResignInvalidProposals - Unknown Masternode\n"); return; } if(n > 3) { CheckSignatureValidity(); LogPrintf("CBudgetManager::ResignInvalidProposals - Masternode not in the top %s\n", MIN_BUDGET_PEER_PROTO_VERSION); return; } CMasternode* pmn = mnodeman.Find(activeMasternode.vin); if(pmn == NULL) { LogPrintf("mprop - unknown masternode - vin:%s \n", pmn->vin.ToString().c_str()); return; } std::map::iterator it1 = mapProposals.begin(); while(it1 != mapProposals.end()) { if(pmn->nVotedTimes+VOTE_PROP_INC > 100) return; //can't submit to the network anyway CBudgetProposal* prop = &((*it1).second); CBudgetProposalBroadcast bprop(*prop); if(!bprop.SignatureValid()){ bprop.vin = activeMasternode.vin; LogPrintf("CBudgetManager::ResignInvalidProposals -- proposal - resigning proposal\n"); CPubKey pubKeyMasternode; CKey keyMasternode; std::string errorMessage; if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, keyMasternode, pubKeyMasternode)){ LogPrintf("CBudgetManager::ResignInvalidProposals - Error upon calling SetKey"); return; } if(!bprop.Sign(keyMasternode, pubKeyMasternode)){ LogPrintf("CBudgetManager::ResignInvalidProposals - Failure to sign"); return; } std::string strError = ""; if(bprop.IsValid(strError)){ //delete if it exists and insert the new object if(mapSeenMasternodeBudgetProposals.count(bprop.GetHash())) mapSeenMasternodeBudgetProposals.erase(bprop.GetHash()); mapSeenMasternodeBudgetProposals.insert(make_pair(bprop.GetHash(), bprop)); bprop.Relay(); } else { LogPrintf("CBudgetManager::ResignInvalidProposals -- proposal - still invalid with new signature\n"); } } ++it1; } std::map::iterator it2 = mapFinalizedBudgets.begin(); while(it2 != mapFinalizedBudgets.end()){ if(pmn->nVotedTimes+VOTE_PROP_INC > 100) return; //can't submit to the network anyway CFinalizedBudget* prop = &((*it2).second); if(!prop->IsValid()) continue; CFinalizedBudgetBroadcast bprop(*prop); if(!bprop.SignatureValid()){ bprop.vin = activeMasternode.vin; LogPrintf("CBudgetManager::ResignInvalidProposals -- finalized budget - resigning finalized budget\n"); CPubKey pubKeyMasternode; CKey keyMasternode; std::string errorMessage; if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, keyMasternode, pubKeyMasternode)){ LogPrintf("CBudgetManager::ResignInvalidProposals - Error upon calling SetKey"); return; } if(!bprop.Sign(keyMasternode, pubKeyMasternode)){ LogPrintf("CBudgetManager::ResignInvalidProposals - Failure to sign"); return; } if(!bprop.IsValid()){ //delete if it exists and insert the new object if(mapFinalizedBudgets.count(bprop.GetHash())) mapFinalizedBudgets.erase(bprop.GetHash()); mapFinalizedBudgets.insert(make_pair(bprop.GetHash(), bprop)); bprop.Relay(); } else { LogPrintf("CBudgetManager::ResignInvalidProposals -- finalized budget - still invalid with new signature\n"); } } ++it2; } } void CBudgetManager::CheckSignatureValidity() { std::map::iterator it1 = mapProposals.begin(); while(it1 != mapProposals.end()) { CBudgetProposal* prop = &((*it1).second); CBudgetProposalBroadcast bprop(*prop); if(!bprop.SignatureValid()){ if(mapSeenMasternodeBudgetProposals.count(bprop.GetHash())) { mapSeenMasternodeBudgetProposals[bprop.GetHash()].fInvalid = true; } } ++it1; } std::map::iterator it2 = mapFinalizedBudgets.begin(); while(it2 != mapFinalizedBudgets.end()) { CFinalizedBudget* prop = &((*it2).second); CFinalizedBudgetBroadcast bprop(*prop); if(!bprop.SignatureValid()){ if(mapSeenFinalizedBudgets.count(bprop.GetHash())) { mapSeenFinalizedBudgets[bprop.GetHash()].fInvalid = true; } } ++it2; } } void SubmitFinalBudget() { CBlockIndex* pindexPrev = chainActive.Tip(); if(!pindexPrev) return; int nBlockStart = pindexPrev->nHeight-(pindexPrev->nHeight % GetBudgetPaymentCycleBlocks())+GetBudgetPaymentCycleBlocks(); if(nSubmittedFinalBudget >= nBlockStart) return; if(nBlockStart - pindexPrev->nHeight > 100) return; std::vector props1 = budget.GetBudget(); std::string strBudgetName = "main"; std::vector vecPayments; for(unsigned int i = 0; i < props1.size(); i++){ CTxBudgetPayment out; out.nProposalHash = props1[i]->GetHash(); out.payee = props1[i]->GetPayee(); out.nAmount = props1[i]->GetAmount(); vecPayments.push_back(out); } if(vecPayments.size() < 1) { LogPrintf("SubmitFinalBudget - Found No Proposals For Period\n"); return; } nSubmittedFinalBudget = nBlockStart; CPubKey pubKeyMasternode; CKey keyMasternode; std::string errorMessage; if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, keyMasternode, pubKeyMasternode)){ LogPrintf("SubmitFinalBudget - Error upon calling SetKey\n"); } //create the proposal incase we're the first to make it CFinalizedBudgetBroadcast prop(activeMasternode.vin, strBudgetName, nBlockStart, vecPayments); if(!prop.Sign(keyMasternode, pubKeyMasternode)){ LogPrintf("SubmitFinalBudget - Failure to sign.\n"); } if(!prop.IsValid()){ LogPrintf("SubmitFinalBudget - Invalid prop (are all the hashes correct?)\n"); } mapSeenFinalizedBudgets.insert(make_pair(prop.GetHash(), prop)); prop.Relay(); budget.AddFinalizedBudget(prop); CFinalizedBudgetVote vote(activeMasternode.vin, prop.GetHash()); if(!vote.Sign(keyMasternode, pubKeyMasternode)){ LogPrintf("SubmitFinalBudget - Failure to sign.\n"); } mapSeenFinalizedBudgetVotes.insert(make_pair(vote.GetHash(), vote)); vote.Relay(); budget.UpdateFinalizedBudget(vote, NULL); } // // CBudgetDB // CBudgetDB::CBudgetDB() { pathDB = GetDataDir() / "budget.dat"; strMagicMessage = "MasternodeBudget"; } bool CBudgetDB::Write(const CBudgetManager& objToSave) { int64_t nStart = GetTimeMillis(); // serialize, checksum data up to that point, then append checksum CDataStream ssObj(SER_DISK, CLIENT_VERSION); ssObj << strMagicMessage; // masternode cache file specific magic message ssObj << FLATDATA(Params().MessageStart()); // network specific magic number ssObj << objToSave; uint256 hash = Hash(ssObj.begin(), ssObj.end()); ssObj << hash; // open output file, and associate with CAutoFile FILE *file = fopen(pathDB.string().c_str(), "wb"); CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) return error("%s : Failed to open file %s", __func__, pathDB.string()); // Write and commit header, data try { fileout << ssObj; } catch (std::exception &e) { return error("%s : Serialize or I/O error - %s", __func__, e.what()); } fileout.fclose(); LogPrintf("Written info to budget.dat %dms\n", GetTimeMillis() - nStart); return true; } CBudgetDB::ReadResult CBudgetDB::Read(CBudgetManager& objToLoad) { int64_t nStart = GetTimeMillis(); // open input file, and associate with CAutoFile FILE *file = fopen(pathDB.string().c_str(), "rb"); CAutoFile filein(file, SER_DISK, CLIENT_VERSION); if (filein.IsNull()) { error("%s : Failed to open file %s", __func__, pathDB.string()); return FileError; } // use file size to size memory buffer int fileSize = boost::filesystem::file_size(pathDB); int dataSize = fileSize - sizeof(uint256); // Don't try to resize to a negative number if file is small if (dataSize < 0) dataSize = 0; vector vchData; vchData.resize(dataSize); uint256 hashIn; // read data and checksum from file try { filein.read((char *)&vchData[0], dataSize); filein >> hashIn; } catch (std::exception &e) { error("%s : Deserialize or I/O error - %s", __func__, e.what()); return HashReadError; } filein.fclose(); CDataStream ssObj(vchData, SER_DISK, CLIENT_VERSION); // verify stored checksum matches input data uint256 hashTmp = Hash(ssObj.begin(), ssObj.end()); if (hashIn != hashTmp) { error("%s : Checksum mismatch, data corrupted", __func__); return IncorrectHash; } unsigned char pchMsgTmp[4]; std::string strMagicMessageTmp; try { // de-serialize file header (masternode cache file specific magic message) and .. ssObj >> strMagicMessageTmp; // ... verify the message matches predefined one if (strMagicMessage != strMagicMessageTmp) { error("%s : Invalid masternode cache magic message", __func__); return IncorrectMagicMessage; } // de-serialize file header (network specific magic number) and .. ssObj >> FLATDATA(pchMsgTmp); // ... verify the network matches ours if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) { error("%s : Invalid network magic number", __func__); return IncorrectMagicNumber; } // de-serialize data into CBudgetManager object ssObj >> objToLoad; } catch (std::exception &e) { objToLoad.Clear(); error("%s : Deserialize or I/O error - %s", __func__, e.what()); return IncorrectFormat; } objToLoad.CheckAndRemove(); // clean out expired LogPrintf("Loaded info from budget.dat %dms\n", GetTimeMillis() - nStart); LogPrintf(" %s\n", objToLoad.ToString()); return Ok; } void DumpBudgets() { int64_t nStart = GetTimeMillis(); CBudgetDB mndb; CBudgetManager tempbudget; LogPrintf("Verifying budget.dat format...\n"); CBudgetDB::ReadResult readResult = mndb.Read(tempbudget); // there was an error and it was not an error on file openning => do not proceed if (readResult == CBudgetDB::FileError) LogPrintf("Missing budget cache file - budget.dat, will try to recreate\n"); else if (readResult != CBudgetDB::Ok) { LogPrintf("Error reading budget.dat: "); if(readResult == CBudgetDB::IncorrectFormat) LogPrintf("magic is ok but data has invalid format, will try to recreate\n"); else { LogPrintf("file format is unknown or invalid, please fix it manually\n"); return; } } LogPrintf("Writting info to budget.dat...\n"); mndb.Write(budget); LogPrintf("Budget dump finished %dms\n", GetTimeMillis() - nStart); } void CBudgetManager::AddFinalizedBudget(CFinalizedBudget& prop) { LOCK(cs); if(!prop.IsValid()) return; if(mapFinalizedBudgets.count(prop.GetHash())) { //this budget must have went invalid, so update the vin to the new one LogPrintf("CBudgetManager::AddFinalizedBudget -- updated vin of invalid finalized budget (%s to %s)\n", mapFinalizedBudgets[prop.GetHash()].vin.prevout.ToStringShort().c_str(), prop.vin.prevout.ToStringShort().c_str()); mapFinalizedBudgets[prop.GetHash()].vin = prop.vin; return; } mapFinalizedBudgets.insert(make_pair(prop.GetHash(), prop)); } void CBudgetManager::AddProposal(CBudgetProposal& prop) { LOCK(cs); if(mapProposals.count(prop.GetHash())) { //this proposal must have went invalid, so update the vin to the new one LogPrintf("CBudgetManager::AddProposal -- updated vin of invalid finalized budget (%s to %s)\n", mapFinalizedBudgets[prop.GetHash()].vin.prevout.ToStringShort().c_str(), prop.vin.prevout.ToStringShort().c_str()); mapProposals[prop.GetHash()].vin = prop.vin; return; } mapProposals.insert(make_pair(prop.GetHash(), prop)); } void CBudgetManager::CheckAndRemove() { std::string strError = ""; std::map::iterator it = mapFinalizedBudgets.begin(); while(it != mapFinalizedBudgets.end()) { CFinalizedBudget* prop = &((*it).second); if(!prop->IsValid()){ mapFinalizedBudgets.erase(it++); } else { prop->AutoCheck(); ++it; } } std::map::iterator it2 = mapProposals.begin(); while(it2 != mapProposals.end()) { CBudgetProposal* prop = &((*it2).second); if(!prop->IsValid(strError)){ mapProposals.erase(it2++); } else { ++it2; } } } void CBudgetManager::FillBlockPayee(CMutableTransaction& txNew, int64_t nFees) { CBlockIndex* pindexPrev = chainActive.Tip(); if(!pindexPrev) return; int nHighestCount = 0; CScript payee; int64_t nAmount = 0; // ------- Grab The Highest Count std::map::iterator it = mapFinalizedBudgets.begin(); while(it != mapFinalizedBudgets.end()) { CFinalizedBudget* prop = &((*it).second); if(prop->GetVoteCount() > nHighestCount){ if(pindexPrev->nHeight+1 >= prop->GetBlockStart() && pindexPrev->nHeight+1 <= prop->GetBlockEnd()){ if(prop->GetPayeeAndAmount(pindexPrev->nHeight+1, payee, nAmount)){ nHighestCount = prop->GetVoteCount(); } } } it++; } CAmount blockValue = GetBlockValue(pindexPrev->nBits, pindexPrev->nHeight, nFees); //miners get the full amount on these blocks txNew.vout[0].nValue = blockValue; if(nHighestCount > 0){ txNew.vout.resize(2); //these are super blocks, so their value can be much larger than normal txNew.vout[1].scriptPubKey = payee; txNew.vout[1].nValue = nAmount; CTxDestination address1; ExtractDestination(payee, address1); CBitcoinAddress address2(address1); LogPrintf("Budget payment to %s for %lld\n", address2.ToString().c_str(), nAmount); } } CFinalizedBudget *CBudgetManager::FindFinalizedBudget(uint256 nHash) { if(mapFinalizedBudgets.count(nHash)) return &mapFinalizedBudgets[nHash]; return NULL; } CBudgetProposal *CBudgetManager::FindProposal(const std::string &strProposalName) { //find the prop with the highest yes count int nYesCount = -99999; CBudgetProposal* prop = NULL; std::map::iterator it = mapProposals.begin(); while(it != mapProposals.end()){ if((*it).second.strProposalName == strProposalName && (*it).second.GetYeas() > nYesCount){ prop = &((*it).second); nYesCount = prop->GetYeas(); } ++it; } if(nYesCount == -99999) return NULL; return prop; } CBudgetProposal *CBudgetManager::FindProposal(uint256 nHash) { if(mapProposals.count(nHash)) return &mapProposals[nHash]; return NULL; } bool CBudgetManager::IsBudgetPaymentBlock(int nBlockHeight){ std::map::iterator it = mapFinalizedBudgets.begin(); while(it != mapFinalizedBudgets.end()) { CFinalizedBudget* prop = &((*it).second); if(nBlockHeight >= prop->GetBlockStart() && nBlockHeight <= prop->GetBlockEnd()){ return true; } it++; } return false; } bool CBudgetManager::IsTransactionValid(const CTransaction& txNew, int nBlockHeight) { int nHighestCount = 0; std::vector ret; // ------- Grab The Highest Count std::map::iterator it = mapFinalizedBudgets.begin(); while(it != mapFinalizedBudgets.end()) { CFinalizedBudget* prop = &((*it).second); if(prop->GetVoteCount() > nHighestCount){ if(nBlockHeight >= prop->GetBlockStart() && nBlockHeight <= prop->GetBlockEnd()){ nHighestCount = prop->GetVoteCount(); } } it++; } if(nHighestCount < mnodeman.CountEnabled()/20) return true; // check the highest finalized budgets (+/- 10% to assist in consensus) std::map::iterator it2 = mapFinalizedBudgets.begin(); while(it2 != mapFinalizedBudgets.end()) { CFinalizedBudget* prop = &((*it2).second); if(prop->GetVoteCount() > nHighestCount-(mnodeman.CountEnabled()/10)){ if(nBlockHeight >= prop->GetBlockStart() && nBlockHeight <= prop->GetBlockEnd()){ if(prop->IsTransactionValid(txNew, nBlockHeight)){ return true; } } } it2++; } //we looked through all of the known budgets return false; } std::vector CBudgetManager::GetAllProposals() { std::vector ret; std::map::iterator it2 = mapProposals.begin(); while(it2 != mapProposals.end()) { (*it2).second.CleanAndRemove(); CBudgetProposal* prop = &((*it2).second); ret.push_back(prop); it2++; } return ret; } //Need to review this function std::vector CBudgetManager::GetBudget() { // ------- Sort budgets by Yes Count std::map mapList; std::map::iterator it = mapProposals.begin(); while(it != mapProposals.end()){ (*it).second.CleanAndRemove(); mapList.insert(make_pair((*it).second.GetHash(), (*it).second.GetYeas())); ++it; } //sort the map and grab the highest count item std::vector > vecList(mapList.begin(), mapList.end()); std::sort(vecList.begin(),vecList.end()); // ------- Grab The Budgets In Order std::vector ret; int64_t nBudgetAllocated = 0; CBlockIndex* pindexPrev = chainActive.Tip(); if(pindexPrev == NULL) return ret; int nBlockStart = pindexPrev->nHeight-(pindexPrev->nHeight % GetBudgetPaymentCycleBlocks())+GetBudgetPaymentCycleBlocks(); int nBlockEnd = nBlockStart + GetBudgetPaymentCycleBlocks() -1; int64_t nTotalBudget = GetTotalBudget(nBlockStart); std::map::iterator it2 = mapProposals.begin(); while(it2 != mapProposals.end()) { CBudgetProposal* prop = &((*it2).second); //prop start/end should be inside this period if(prop->nBlockStart <= nBlockStart && prop->nBlockEnd >= nBlockEnd && prop->GetYeas()-prop->GetNays() > mnodeman.CountEnabled()/10) { if(nTotalBudget == nBudgetAllocated){ prop->SetAllotted(0); } else if(prop->GetAmount() + nBudgetAllocated <= nTotalBudget) { prop->SetAllotted(prop->GetAmount()); nBudgetAllocated += prop->GetAmount(); } else { //couldn't pay for the entire budget, so it'll be partially paid. prop->SetAllotted(nTotalBudget - nBudgetAllocated); nBudgetAllocated = nTotalBudget; } ret.push_back(prop); } it2++; } return ret; } std::vector CBudgetManager::GetFinalizedBudgets() { std::vector ret; // ------- Grab The Budgets In Order std::map::iterator it2 = mapFinalizedBudgets.begin(); while(it2 != mapFinalizedBudgets.end()) { CFinalizedBudget* prop = &((*it2).second); ret.push_back(prop); it2++; } return ret; } std::string CBudgetManager::GetRequiredPaymentsString(int64_t nBlockHeight) { std::string ret = "unknown-budget"; std::map::iterator it = mapFinalizedBudgets.begin(); while(it != mapFinalizedBudgets.end()) { CFinalizedBudget* prop = &((*it).second); if(nBlockHeight >= prop->GetBlockStart() && nBlockHeight <= prop->GetBlockEnd()){ CTxBudgetPayment payment; if(prop->GetProposalByBlock(nBlockHeight, payment)){ if(ret == "unknown-budget"){ ret = payment.nProposalHash.ToString().c_str(); } else { ret += ","; ret += payment.nProposalHash.ToString().c_str(); } } else { LogPrintf("CBudgetManager::GetRequiredPaymentsString - Couldn't find budget payment for block %lld\n", nBlockHeight); } } it++; } return ret; } int64_t CBudgetManager::GetTotalBudget(int nHeight) { if(chainActive.Tip() == NULL) return 0; //get min block value and calculate from that int64_t nSubsidy = 5 * COIN; if(Params().NetworkID() == CBaseChainParams::TESTNET){ for(int i = 46200; i <= nHeight; i += 210240) nSubsidy -= nSubsidy/14; } else { // yearly decline of production by 7.1% per year, projected 21.3M coins max by year 2050. for(int i = 210240; i <= nHeight; i += 210240) nSubsidy -= nSubsidy/14; } return ((nSubsidy/100)*10)*576*30; } void CBudgetManager::NewBlock() { budget.CheckAndRemove(); if (strBudgetMode == "suggest") { //suggest the budget we see SubmitFinalBudget(); } ResignInvalidProposals(); //this function should be called 1/6 blocks, allowing up to 100 votes per day on all proposals if(chainActive.Height() % 6 != 0) return; mnodeman.DecrementVotedTimes(); //remove invalid votes once in a while (we have to check the signatures and validity of every vote, somewhat CPU intensive) std::map::iterator it = mapProposals.begin(); while(it != mapProposals.end()){ (*it).second.CleanAndRemove(); ++it; } std::map::iterator it2 = mapFinalizedBudgets.begin(); while(it2 != mapFinalizedBudgets.end()){ (*it2).second.CleanAndRemove(); ++it2; } } void CBudgetManager::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { // lite mode is not supported if(IsInitialBlockDownload()) return; LOCK(cs_budget); if (strCommand == "mnvs") { //Masternode vote sync bool IsLocal = pfrom->addr.IsRFC1918() || pfrom->addr.IsLocal(); if(!IsLocal){ if(pfrom->HasFulfilledRequest("mnvs")) { LogPrintf("mnvs - peer already asked me for the list\n"); Misbehaving(pfrom->GetId(), 20); return; } } uint256 nProp; vRecv >> nProp; pfrom->FulfilledRequest("mnvs"); budget.Sync(pfrom, nProp); LogPrintf("mnvs - Sent Masternode votes to %s\n", pfrom->addr.ToString()); } if (strCommand == "mprop") { //Masternode Proposal CBudgetProposalBroadcast prop; vRecv >> prop; if(mapSeenMasternodeBudgetProposals.count(prop.GetHash())){ //if this proposal went inactive, we'll update it with the new re-signature if(!mapSeenMasternodeBudgetProposals[prop.GetHash()].fInvalid){ return; } } //set time we first saw this prop prop.nTime = GetAdjustedTime(); CMasternode* pmn = mnodeman.Find(prop.vin); if(pmn == NULL) { if(fDebug) LogPrintf("mprop - unknown masternode - vin: %s\n", prop.vin.ToString()); return; } if(!prop.SignatureValid()){ LogPrintf("mprop - signature invalid\n"); Misbehaving(pfrom->GetId(), 20); return; } std::string strError = ""; if(!prop.IsValid(strError)) { LogPrintf("mprop - invalid budget proposal - %s\n", strError.c_str()); return; } //delete if it exists and insert the new object if(mapSeenMasternodeBudgetProposals.count(prop.GetHash())) mapSeenMasternodeBudgetProposals.erase(prop.GetHash()); mapSeenMasternodeBudgetProposals.insert(make_pair(prop.GetHash(), prop)); if(IsSyncingMasternodeAssets() || pmn->nVotedTimes < 100){ CBudgetProposal p(prop); budget.AddProposal(p); prop.Relay(); //can only do this six times a day on the network if(!IsSyncingMasternodeAssets()) pmn->nVotedTimes+=VOTE_PROP_INC; //We might have active votes for this proposal that are valid now CheckOrphanVotes(); } else { LogPrintf("mvote - masternode can't vote again - vin: %s\n", pmn->vin.ToString()); return; } } if (strCommand == "mvote") { //Masternode Vote CBudgetVote vote; vRecv >> vote; if(mapSeenMasternodeBudgetVotes.count(vote.GetHash())){ return; } CMasternode* pmn = mnodeman.Find(vote.vin); if(pmn == NULL) { if(fDebug) LogPrintf("mvote - unknown masternode - vin: %s\n", vote.vin.ToString()); return; } if(!vote.SignatureValid()){ LogPrintf("mvote - signature invalid\n"); Misbehaving(pfrom->GetId(), 20); return; } mapSeenMasternodeBudgetVotes.insert(make_pair(vote.GetHash(), vote)); if(IsSyncingMasternodeAssets() || pmn->nVotedTimes < 100){ budget.UpdateProposal(vote, pfrom); vote.Relay(); if(!IsSyncingMasternodeAssets()) pmn->nVotedTimes++; } else { LogPrintf("mvote - masternode can't vote again - vin: %s\n", pmn->vin.ToString()); return; } } if (strCommand == "fbs") { //Finalized Budget Suggestion CFinalizedBudgetBroadcast fbs; vRecv >> fbs; if(mapSeenFinalizedBudgets.count(fbs.GetHash())){ //if this budget went inactive, we'll update it with the new re-signature if(!mapSeenFinalizedBudgets[fbs.GetHash()].fInvalid){ return; } } CMasternode* pmn = mnodeman.Find(fbs.vin); if(pmn == NULL) { if(fDebug) LogPrintf("fbs - unknown masternode - vin: %s\n", fbs.vin.ToString()); return; } if(!fbs.SignatureValid()){ LogPrintf("fbs - signature invalid\n"); Misbehaving(pfrom->GetId(), 20); return; } if(!fbs.IsValid()) { LogPrintf("fbs - invalid finalized budget\n"); return; } //delete if it exists and insert the new object if(mapSeenFinalizedBudgets.count(fbs.GetHash())) mapSeenFinalizedBudgets.erase(fbs.GetHash()); mapSeenFinalizedBudgets.insert(make_pair(fbs.GetHash(), fbs)); if(IsSyncingMasternodeAssets() || pmn->nVotedTimes < 100){ CFinalizedBudget p(fbs); budget.AddFinalizedBudget(p); fbs.Relay(); if(!IsSyncingMasternodeAssets()) pmn->nVotedTimes+=VOTE_PROP_INC; //we might have active votes for this budget that are now valid CheckOrphanVotes(); } else { LogPrintf("fbs - masternode can't vote again - vin: %s\n", pmn->vin.ToString()); return; } } if (strCommand == "fbvote") { //Finalized Budget Vote CFinalizedBudgetVote vote; vRecv >> vote; if(mapSeenFinalizedBudgetVotes.count(vote.GetHash())){ return; } CMasternode* pmn = mnodeman.Find(vote.vin); if(pmn == NULL) { if(fDebug) LogPrintf("fbvote - unknown masternode - vin: %s\n", vote.vin.ToString()); return; } if(!vote.SignatureValid()){ LogPrintf("fbvote - signature invalid\n"); Misbehaving(pfrom->GetId(), 20); return; } mapSeenFinalizedBudgetVotes.insert(make_pair(vote.GetHash(), vote)); if(IsSyncingMasternodeAssets() || pmn->nVotedTimes < 100){ budget.UpdateFinalizedBudget(vote, pfrom); vote.Relay(); if(!IsSyncingMasternodeAssets()) pmn->nVotedTimes++; } else { LogPrintf("fbvote - masternode can't vote again - vin: %s\n", pmn->vin.ToString()); return; } } } bool CBudgetManager::PropExists(uint256 nHash) { if(mapProposals.count(nHash)) return true; return false; } void CBudgetManager::Sync(CNode* pfrom, uint256 nProp) { /* Sync with a client on the network -- This code checks each of the hash maps for all known budget proposals and finalized budget proposals, then checks them against the budget object to see if they're OK. If all checks pass, we'll send it to the peer. */ std::map::iterator it1 = mapSeenMasternodeBudgetProposals.begin(); while(it1 != mapSeenMasternodeBudgetProposals.end()){ CBudgetProposal* bp = budget.FindProposal((*it1).first); if(bp && (nProp == 0 || (*it1).first == nProp)){ pfrom->PushMessage("mprop", ((*it1).second)); } it1++; } std::map::iterator it2 = mapSeenMasternodeBudgetVotes.begin(); while(it2 != mapSeenMasternodeBudgetVotes.end()){ CBudgetProposal* bp = budget.FindProposal((*it2).second.nProposalHash); if(bp && (nProp == 0 || (*it1).first == nProp)){ pfrom->PushMessage("mvote", ((*it2).second)); } it2++; } std::map::iterator it3 = mapSeenFinalizedBudgets.begin(); while(it3 != mapSeenFinalizedBudgets.end()){ CFinalizedBudget* bp = budget.FindFinalizedBudget((*it3).first); if(bp && (nProp == 0 || (*it1).first == nProp)){ pfrom->PushMessage("fbs", ((*it3).second)); } it3++; } std::map::iterator it4 = mapSeenFinalizedBudgetVotes.begin(); while(it4 != mapSeenFinalizedBudgetVotes.end()){ CFinalizedBudget* bp = budget.FindFinalizedBudget((*it4).second.nBudgetHash); if(bp && (nProp == 0 || (*it1).first == nProp)){ pfrom->PushMessage("fbvote", ((*it4).second)); } it4++; } } bool CBudgetManager::UpdateProposal(CBudgetVote& vote, CNode* pfrom) { LOCK(cs); if(!mapProposals.count(vote.nProposalHash)){ if(pfrom){ LogPrintf("Unknown proposal %d, Asking for source proposal\n", vote.nProposalHash.ToString().c_str()); mapOrphanMasternodeBudgetVotes[vote.nProposalHash] = vote; if(!askedForSourceProposalOrBudget.count(vote.nProposalHash)){ pfrom->PushMessage("mnvs", vote.nProposalHash); askedForSourceProposalOrBudget[vote.nProposalHash] = GetTime(); } } return false; } mapProposals[vote.nProposalHash].AddOrUpdateVote(vote); return true; } bool CBudgetManager::UpdateFinalizedBudget(CFinalizedBudgetVote& vote, CNode* pfrom) { LOCK(cs); if(!mapFinalizedBudgets.count(vote.nBudgetHash)){ if(pfrom){ LogPrintf("Unknown Finalized Proposal %s, Asking for source proposal\n", vote.nBudgetHash.ToString().c_str()); mapOrphanFinalizedBudgetVotes[vote.nBudgetHash] = vote; if(!askedForSourceProposalOrBudget.count(vote.nBudgetHash)){ pfrom->PushMessage("mnvs", vote.nBudgetHash); askedForSourceProposalOrBudget[vote.nBudgetHash] = GetTime(); } } return false; } mapFinalizedBudgets[vote.nBudgetHash].AddOrUpdateVote(vote); return true; } CBudgetProposal::CBudgetProposal() { vin = CTxIn(); strProposalName = "unknown"; nBlockStart = 0; nBlockEnd = 0; nAmount = 0; nTime = 0; } CBudgetProposal::CBudgetProposal(CTxIn vinIn, std::string strProposalNameIn, std::string strURLIn, int nBlockStartIn, int nBlockEndIn, CScript addressIn, CAmount nAmountIn) { vin = vinIn; strProposalName = strProposalNameIn; strURL = strURLIn; nBlockStart = nBlockStartIn; nBlockEnd = nBlockEndIn; address = addressIn; nAmount = nAmountIn; nTime = 0; } CBudgetProposal::CBudgetProposal(const CBudgetProposal& other) { vin = other.vin; strProposalName = other.strProposalName; strURL = other.strURL; nBlockStart = other.nBlockStart; nBlockEnd = other.nBlockEnd; address = other.address; nAmount = other.nAmount; nTime = other.nTime; } bool CBudgetProposal::IsValid(std::string& strError) { if(GetYeas()+GetNays() < -(mnodeman.CountEnabled()/10)){ strError = "Active removal"; return false; } //if proposal doesn't gain traction within 2 weeks, remove it // nTime not being saved correctly // if(nTime + (60*60*24*2) < GetAdjustedTime()) { // if(GetYeas()-GetNays() < (mnodeman.CountEnabled()/10)) { // strError = "Not enough support"; // return false; // } // } //can only pay out 10% of the possible coins (min value of coins) if(nAmount > budget.GetTotalBudget(nBlockStart)) { strError = "Payment more than max"; return false; } CBlockIndex* pindexPrev = chainActive.Tip(); if(pindexPrev == NULL) {strError = "Tip is NULL"; return true;} if(GetBlockEnd() < pindexPrev->nHeight - GetBudgetPaymentCycleBlocks()/2 ) return false; return true; } void CBudgetProposal::AddOrUpdateVote(CBudgetVote& vote) { LOCK(cs); uint256 hash = vote.vin.prevout.GetHash(); mapVotes[hash] = vote; } // If masternode voted for a proposal, but is now invalid -- remove the vote void CBudgetProposal::CleanAndRemove() { std::map::iterator it = mapVotes.begin(); while(it != mapVotes.end()) { if ((*it).second.SignatureValid()) { ++it; } else { mapSeenMasternodeBudgetVotes.erase((*it).first); mapVotes.erase(it++); } } } double CBudgetProposal::GetRatio() { int yeas = 0; int nays = 0; std::map::iterator it = mapVotes.begin(); while(it != mapVotes.end()) { if ((*it).second.nVote == VOTE_YES) yeas++; if ((*it).second.nVote == VOTE_NO) nays++; ++it; } if(yeas+nays == 0) return 0.0f; return ((double)(yeas) / (double)(yeas+nays)); } int CBudgetProposal::GetYeas() { int ret = 0; std::map::iterator it = mapVotes.begin(); while(it != mapVotes.end()){ if ((*it).second.nVote == VOTE_YES) ret++; ++it; } return ret; } int CBudgetProposal::GetNays() { int ret = 0; std::map::iterator it = mapVotes.begin(); while(it != mapVotes.end()){ if ((*it).second.nVote == VOTE_NO) ret++; ++it; } return ret; } int CBudgetProposal::GetAbstains() { int ret = 0; std::map::iterator it = mapVotes.begin(); while(it != mapVotes.end()){ if ((*it).second.nVote == VOTE_ABSTAIN) ret++; ++it; } return ret; } int CBudgetProposal::GetBlockStartCycle() { //end block is half way through the next cycle (so the proposal will be removed much after the payment is sent) return (nBlockStart-(nBlockStart % GetBudgetPaymentCycleBlocks())); } int CBudgetProposal::GetBlockCurrentCycle() { CBlockIndex* pindexPrev = chainActive.Tip(); if(pindexPrev == NULL) return -1; if(pindexPrev->nHeight >= GetBlockEndCycle()) return -1; return (pindexPrev->nHeight-(pindexPrev->nHeight % GetBudgetPaymentCycleBlocks())); } int CBudgetProposal::GetBlockEndCycle() { //end block is half way through the next cycle (so the proposal will be removed much after the payment is sent) return nBlockEnd-(GetBudgetPaymentCycleBlocks()/2); } int CBudgetProposal::GetTotalPaymentCount() { return (GetBlockEndCycle()-GetBlockStartCycle())/GetBudgetPaymentCycleBlocks(); } int CBudgetProposal::GetRemainingPaymentCount() { return (GetBlockEndCycle()-GetBlockCurrentCycle())/GetBudgetPaymentCycleBlocks(); } CBudgetProposalBroadcast::CBudgetProposalBroadcast() { vin = CTxIn(); strProposalName = "unknown"; strURL = ""; nBlockStart = 0; nBlockEnd = 0; nAmount = 0; nTime = 0; } CBudgetProposalBroadcast::CBudgetProposalBroadcast(const CBudgetProposal& other) { vin = other.vin; strProposalName = other.strProposalName; strURL = other.strURL; nBlockStart = other.nBlockStart; nBlockEnd = other.nBlockEnd; address = other.address; nAmount = other.nAmount; } CBudgetProposalBroadcast::CBudgetProposalBroadcast(CTxIn vinIn, std::string strProposalNameIn, std::string strURLIn, int nPaymentCount, CScript addressIn, CAmount nAmountIn, int nBlockStartIn) { vin = vinIn; strProposalName = strProposalNameIn; strURL = strURLIn; nBlockStart = nBlockStartIn; int nCycleStart = (nBlockStart-(nBlockStart % GetBudgetPaymentCycleBlocks())); //calculate the end of the cycle for this vote, add half a cycle (vote will be deleted after that block) nBlockEnd = nCycleStart + (GetBudgetPaymentCycleBlocks()*nPaymentCount) + GetBudgetPaymentCycleBlocks()/2; address = addressIn; nAmount = nAmountIn; } bool CBudgetProposalBroadcast::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode) { // Choose coins to use CPubKey pubKeyCollateralAddress; CKey keyCollateralAddress; std::string errorMessage; std::string strMessage = vin.prevout.ToStringShort() + strProposalName + strURL + boost::lexical_cast(nBlockStart) + boost::lexical_cast(nBlockEnd) + address.ToString() + boost::lexical_cast(nAmount); if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchSig, keyMasternode)) return(" Error upon calling SignMessage"); if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, errorMessage)) return(" Error upon calling VerifyMessage"); return true; } void CBudgetProposalBroadcast::Relay() { CInv inv(MSG_BUDGET_PROPOSAL, GetHash()); vector vInv; vInv.push_back(inv); LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes){ pnode->PushMessage("inv", vInv); } } bool CBudgetProposalBroadcast::SignatureValid() { std::string errorMessage; std::string strMessage = vin.prevout.ToStringShort() + strProposalName + strURL + boost::lexical_cast(nBlockStart) + boost::lexical_cast(nBlockEnd) + address.ToString() + boost::lexical_cast(nAmount); CMasternode* pmn = mnodeman.Find(vin); if(pmn == NULL) { LogPrintf("CBudgetProposalBroadcast::SignatureValid() - Unknown Masternode - %s\n", vin.ToString().c_str()); return false; } if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchSig, strMessage, errorMessage)) { LogPrintf("CBudgetProposalBroadcast::SignatureValid() - Verify message failed\n"); return false; } return true; } CBudgetVote::CBudgetVote() { vin = CTxIn(); nProposalHash = 0; nVote = VOTE_ABSTAIN; nTime = 0; } CBudgetVote::CBudgetVote(CTxIn vinIn, uint256 nProposalHashIn, int nVoteIn) { vin = vinIn; nProposalHash = nProposalHashIn; nVote = nVoteIn; nTime = GetAdjustedTime(); } void CBudgetVote::Relay() { CInv inv(MSG_BUDGET_VOTE, GetHash()); vector vInv; vInv.push_back(inv); LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes){ pnode->PushMessage("inv", vInv); } } bool CBudgetVote::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode) { // Choose coins to use CPubKey pubKeyCollateralAddress; CKey keyCollateralAddress; std::string errorMessage; std::string strMessage = vin.prevout.ToStringShort() + nProposalHash.ToString() + boost::lexical_cast(nVote) + boost::lexical_cast(nTime); if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchSig, keyMasternode)) return(" Error upon calling SignMessage"); if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, errorMessage)) return(" Error upon calling VerifyMessage"); return true; } bool CBudgetVote::SignatureValid() { std::string errorMessage; std::string strMessage = vin.prevout.ToStringShort() + nProposalHash.ToString() + boost::lexical_cast(nVote) + boost::lexical_cast(nTime); CMasternode* pmn = mnodeman.Find(vin); if(pmn == NULL) { LogPrintf("CBudgetVote::SignatureValid() - Unknown Masternode - %s\n", vin.ToString().c_str()); return false; } if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchSig, strMessage, errorMessage)) { LogPrintf("CBudgetVote::SignatureValid() - Verify message failed\n"); return false; } return true; } CFinalizedBudget::CFinalizedBudget() { vin = CTxIn(); strBudgetName = ""; nBlockStart = 0; vecProposals.clear(); mapVotes.clear(); } CFinalizedBudget::CFinalizedBudget(const CFinalizedBudget& other) { vin = other.vin; strBudgetName = other.strBudgetName; nBlockStart = other.nBlockStart; vecProposals = other.vecProposals; mapVotes = other.mapVotes; } void CFinalizedBudget::AddOrUpdateVote(CFinalizedBudgetVote& vote) { LOCK(cs); uint256 hash = vote.vin.prevout.GetHash(); mapVotes[hash] = vote; } //evaluate if we should vote for this. Masternode only void CFinalizedBudget::AutoCheck() { if(!fMasterNode || fAutoChecked) return; if(Params().NetworkID() == CBaseChainParams::MAIN){ if(rand() % 100 > 5) return; //do this 1 in 20 blocks -- spread out the voting activity on mainnet } fAutoChecked = true; //we only need to check this once if(strBudgetMode == "auto") //only vote for exact matches { std::vector props1 = budget.GetBudget(); if(props1.size() == 0) { LogPrintf("CFinalizedBudget::AutoCheck - Can't get Budget, aborting\n"); return; } for(unsigned int i = 0; i < vecProposals.size(); i++){ if(i > props1.size()-1) { LogPrintf("CFinalizedBudget::AutoCheck - Vector size mismatch, aborting\n"); return; } if(vecProposals[i].nProposalHash != props1[i]->GetHash()){ LogPrintf("CFinalizedBudget::AutoCheck - item #%d doesn't match %s %s\n", i, vecProposals[i].nProposalHash.ToString().c_str(), props1[i]->GetHash().ToString().c_str()); return; } if(vecProposals[i].payee != props1[i]->GetPayee()){ LogPrintf("CFinalizedBudget::AutoCheck - item #%d payee doesn't match %s %s\n", i, vecProposals[i].payee.ToString().c_str(), props1[i]->GetPayee().ToString().c_str()); return; } if(vecProposals[i].nAmount != props1[i]->GetAmount()){ LogPrintf("CFinalizedBudget::AutoCheck - item #%d payee doesn't match %s %s\n", i, vecProposals[i].payee.ToString().c_str(), props1[i]->GetPayee().ToString().c_str()); return; } LogPrintf("CFinalizedBudget::AutoCheck - Finalized Budget Matches! Submitting Vote.\n"); SubmitVote(); } } // Feature : Masternodes can delegate finalized budgets to a 3rd party simply by adding this option to the configuration else if (strBudgetMode == vin.prevout.ToStringShort()) { SubmitVote(); } } // If masternode voted for a proposal, but is now invalid -- remove the vote void CFinalizedBudget::CleanAndRemove() { std::map::iterator it = mapVotes.begin(); while(it != mapVotes.end()) { if ((*it).second.SignatureValid()) { ++it; } else { mapSeenFinalizedBudgetVotes.erase((*it).first); mapVotes.erase(it++); } } } int64_t CFinalizedBudget::GetTotalPayout() { int64_t ret = 0; for(unsigned int i = 0; i < vecProposals.size(); i++){ ret += vecProposals[i].nAmount; } return ret; } std::string CFinalizedBudget::GetProposals() { std::string ret = ""; BOOST_FOREACH(CTxBudgetPayment& payment, vecProposals){ CBudgetProposal* prop = budget.FindProposal(payment.nProposalHash); std::string token = payment.nProposalHash.ToString(); if(prop) token = prop->GetName(); if(ret == "") {ret = token;} else {ret += "," + token;} } return ret; } std::string CFinalizedBudget::GetStatus() { std::string retBadHashes = ""; std::string retBadPayeeOrAmount = ""; for(int nBlockHeight = GetBlockStart(); nBlockHeight <= GetBlockEnd(); nBlockHeight++) { CTxBudgetPayment prop1; if(!GetProposalByBlock(nBlockHeight, prop1)){ LogPrintf("CFinalizedBudget::GetStatus - Couldn't find budget payment for block %lld\n", nBlockHeight); continue; } CBudgetProposal* prop2 = budget.FindProposal(prop1.nProposalHash); if(!prop2){ if(retBadHashes == ""){ retBadHashes = "Unknown proposal hash! Check this proposal before voting" + prop1.nProposalHash.ToString(); } else { retBadHashes += "," + prop1.nProposalHash.ToString(); } } else { if(prop2->GetPayee() != prop1.payee || prop2->GetAmount() != prop1.nAmount) { if(retBadPayeeOrAmount == ""){ retBadPayeeOrAmount = "Budget payee/nAmount doesn't match our proposal! " + prop1.nProposalHash.ToString(); } else { retBadPayeeOrAmount += "," + prop1.nProposalHash.ToString(); } } } } if(retBadHashes == "" && retBadPayeeOrAmount == "") return "OK"; return retBadHashes + retBadPayeeOrAmount; } bool CFinalizedBudget::IsValid() { //must be the correct block for payment to happen (once a month) if(nBlockStart % GetBudgetPaymentCycleBlocks() != 0) return false; if(GetBlockEnd() - nBlockStart > 100) return false; if(vecProposals.size() > 100) return false; if(strBudgetName == "") return false; if(nBlockStart == 0) return false; //can only pay out 10% of the possible coins (min value of coins) if(GetTotalPayout() > budget.GetTotalBudget(nBlockStart)) return false; //TODO: if N cycles old, invalid, invalid CBlockIndex* pindexPrev = chainActive.Tip(); if(pindexPrev == NULL) return true; if(nBlockStart < pindexPrev->nHeight) return false; if(GetBlockEnd() < pindexPrev->nHeight - GetBudgetPaymentCycleBlocks()/2 ) return false; return true; } bool CFinalizedBudget::IsTransactionValid(const CTransaction& txNew, int nBlockHeight) { /* BOOST_FOREACH(CMasternodePayee& payee, vecPayments) { bool found = false; BOOST_FOREACH(CTxOut out, txNew.vout) { if(payee.scriptPubKey == out.scriptPubKey && payee.nValue == out.nValue) found = true; } if(payee.nVotes >= MNPAYMENTS_SIGNATURES_REQUIRED && !found){ CTxDestination address1; ExtractDestination(payee.scriptPubKey, address1); CBitcoinAddress address2(address1); LogPrintf("CMasternodePayments::IsTransactionValid - Missing required payment - %s:%d\n", address2.ToString().c_str(), payee.nValue); return false; } }*/ return true; } void CFinalizedBudget::SubmitVote() { CPubKey pubKeyMasternode; CKey keyMasternode; std::string errorMessage; if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, keyMasternode, pubKeyMasternode)){ LogPrintf("CFinalizedBudget::SubmitVote - Error upon calling SetKey\n"); return; } CFinalizedBudgetVote vote(activeMasternode.vin, GetHash()); if(!vote.Sign(keyMasternode, pubKeyMasternode)){ LogPrintf("CFinalizedBudget::SubmitVote - Failure to sign."); return; } mapSeenFinalizedBudgetVotes.insert(make_pair(vote.GetHash(), vote)); vote.Relay(); budget.UpdateFinalizedBudget(vote, NULL); } CFinalizedBudgetBroadcast::CFinalizedBudgetBroadcast() { vin = CTxIn(); strBudgetName = ""; nBlockStart = 0; vecProposals.clear(); mapVotes.clear(); vchSig.clear(); } CFinalizedBudgetBroadcast::CFinalizedBudgetBroadcast(const CFinalizedBudget& other) { vin = other.vin; strBudgetName = other.strBudgetName; nBlockStart = other.nBlockStart; BOOST_FOREACH(CTxBudgetPayment out, other.vecProposals) vecProposals.push_back(out); mapVotes = other.mapVotes; } CFinalizedBudgetBroadcast::CFinalizedBudgetBroadcast(CTxIn& vinIn, std::string strBudgetNameIn, int nBlockStartIn, std::vector vecProposalsIn) { vin = vinIn; printf("%s\n", vin.ToString().c_str()); strBudgetName = strBudgetNameIn; nBlockStart = nBlockStartIn; BOOST_FOREACH(CTxBudgetPayment out, vecProposalsIn) vecProposals.push_back(out); mapVotes.clear(); } void CFinalizedBudgetBroadcast::Relay() { CInv inv(MSG_BUDGET_FINALIZED, GetHash()); vector vInv; vInv.push_back(inv); LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes){ pnode->PushMessage("inv", vInv); } } bool CFinalizedBudgetBroadcast::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode) { // Choose coins to use CPubKey pubKeyCollateralAddress; CKey keyCollateralAddress; std::string errorMessage; std::string strMessage = vin.prevout.ToStringShort() + strBudgetName + boost::lexical_cast(nBlockStart); BOOST_FOREACH(CTxBudgetPayment& payment, vecProposals) strMessage += payment.nProposalHash.ToString().c_str(); if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchSig, keyMasternode)) return(" Error upon calling SignMessage"); if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, errorMessage)) return(" Error upon calling VerifyMessage"); return true; } bool CFinalizedBudgetBroadcast::SignatureValid() { std::string errorMessage; std::string strMessage = vin.prevout.ToStringShort() + strBudgetName + boost::lexical_cast(nBlockStart); BOOST_FOREACH(CTxBudgetPayment& payment, vecProposals) strMessage += payment.nProposalHash.ToString().c_str(); CMasternode* pmn = mnodeman.Find(vin); if(pmn == NULL) { LogPrintf("CFinalizedBudgetBroadcast::SignatureValid() - Unknown Masternode\n"); return false; } if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchSig, strMessage, errorMessage)) { LogPrintf("CFinalizedBudgetBroadcast::SignatureValid() - Verify message failed\n"); return false; } return true; } CFinalizedBudgetVote::CFinalizedBudgetVote() { vin = CTxIn(); nBudgetHash = 0; nTime = 0; vchSig.clear(); } CFinalizedBudgetVote::CFinalizedBudgetVote(CTxIn vinIn, uint256 nBudgetHashIn) { vin = vinIn; nBudgetHash = nBudgetHashIn; nTime = GetAdjustedTime(); vchSig.clear(); } void CFinalizedBudgetVote::Relay() { CInv inv(MSG_BUDGET_FINALIZED_VOTE, GetHash()); vector vInv; vInv.push_back(inv); LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes){ pnode->PushMessage("inv", vInv); } } bool CFinalizedBudgetVote::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode) { // Choose coins to use CPubKey pubKeyCollateralAddress; CKey keyCollateralAddress; std::string errorMessage; std::string strMessage = vin.prevout.ToStringShort() + nBudgetHash.ToString() + boost::lexical_cast(nTime); if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchSig, keyMasternode)) return(" Error upon calling SignMessage"); if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, errorMessage)) return(" Error upon calling VerifyMessage"); return true; } bool CFinalizedBudgetVote::SignatureValid() { std::string errorMessage; std::string strMessage = vin.prevout.ToStringShort() + nBudgetHash.ToString() + boost::lexical_cast(nTime); CMasternode* pmn = mnodeman.Find(vin); if(pmn == NULL) { LogPrintf("CFinalizedBudgetVote::SignatureValid() - Unknown Masternode\n"); return false; } if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchSig, strMessage, errorMessage)) { LogPrintf("CFinalizedBudgetVote::SignatureValid() - Verify message failed\n"); return false; } return true; }