// 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. #ifndef MASTERNODE_BUDGET_H #define MASTERNODE_BUDGET_H #include "main.h" #include "sync.h" #include "net.h" #include "key.h" #include "util.h" #include "base58.h" #include "masternode.h" #include using namespace std; class CBudgetManager; class CFinalizedBudgetBroadcast; class CFinalizedBudget; class CFinalizedBudgetVote; class CBudgetProposal; class CBudgetProposalBroadcast; class CBudgetVote; class CTxBudgetPayment; #define VOTE_ABSTAIN 0 #define VOTE_YES 1 #define VOTE_NO 2 #define VOTE_PROP_INC 15 //how many "votes" to count for a new proposal extern std::map mapSeenMasternodeBudgetProposals; extern std::map mapSeenMasternodeBudgetVotes; extern std::map mapSeenFinalizedBudgets; extern std::map mapSeenFinalizedBudgetVotes; extern CBudgetManager budget; void DumpBudgets(); //Amount of blocks in a months period of time (using 2.6 minutes per) int GetBudgetPaymentCycleBlocks(); void SubmitFinalBudget(); /** Save Budget Manager (budget.dat) */ class CBudgetDB { private: boost::filesystem::path pathDB; std::string strMagicMessage; public: enum ReadResult { Ok, FileError, HashReadError, IncorrectHash, IncorrectMagicMessage, IncorrectMagicNumber, IncorrectFormat }; CBudgetDB(); bool Write(const CBudgetManager &objToSave); ReadResult Read(CBudgetManager& objToLoad); }; // // Budget Manager : Contains all proposals for the budget // class CBudgetManager { private: // critical section to protect the inner data structures mutable CCriticalSection cs; public: // keep track of the scanning errors I've seen map mapProposals; map mapFinalizedBudgets; CBudgetManager() { mapProposals.clear(); mapFinalizedBudgets.clear(); } int sizeFinalized() {return (int)mapFinalizedBudgets.size();} int sizeProposals() {return (int)mapProposals.size();} void Sync(CNode* node, uint256 nProp); void Calculate(); void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); void NewBlock(); CBudgetProposal *FindProposal(const std::string &strProposalName); CBudgetProposal *FindProposal(uint256 nHash); CFinalizedBudget *FindFinalizedBudget(uint256 nHash); std::pair GetVotes(std::string strProposalName); void CleanUp(); int64_t GetTotalBudget(int nHeight); std::vector GetBudget(); std::vector GetAllProposals(); std::vector GetFinalizedBudgets(); bool IsBudgetPaymentBlock(int nBlockHeight); void AddProposal(CBudgetProposal& prop); void AddFinalizedBudget(CFinalizedBudget& prop); bool UpdateProposal(CBudgetVote& vote, CNode* pfrom); bool UpdateFinalizedBudget(CFinalizedBudgetVote& vote, CNode* pfrom); bool PropExists(uint256 nHash); bool IsTransactionValid(const CTransaction& txNew, int nBlockHeight); std::string GetRequiredPaymentsString(int64_t nBlockHeight); void FillBlockPayee(CMutableTransaction& txNew, int64_t nFees); //Have masternodes resign proposals with masternodes that have went inactive void ResignInvalidProposals(); void CheckSignatureValidity(); void Clear(){ LogPrintf("Budget object cleared\n"); mapProposals.clear(); mapFinalizedBudgets.clear(); } void CheckAndRemove(); std::string ToString() {return "not implemented";} ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(mapSeenMasternodeBudgetProposals); READWRITE(mapSeenMasternodeBudgetVotes); READWRITE(mapSeenFinalizedBudgets); READWRITE(mapSeenFinalizedBudgetVotes); READWRITE(mapProposals); READWRITE(mapFinalizedBudgets); } }; class CTxBudgetPayment { public: uint256 nProposalHash; CScript payee; int64_t nAmount; CTxBudgetPayment() { payee = CScript(); nAmount = 0; nProposalHash = 0; } ADD_SERIALIZE_METHODS; //for saving to the serialized db template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(payee); READWRITE(nAmount); READWRITE(nProposalHash); } }; // // Finalized Budget : Contains the suggested proposals to pay on a given block // class CFinalizedBudget { private: // critical section to protect the inner data structures mutable CCriticalSection cs; bool fAutoChecked; //If it matches what we see, we'll auto vote for it (masternode only) public: std::string strBudgetName; CTxIn vin; int nBlockStart; std::vector vecProposals; map mapVotes; CFinalizedBudget(); CFinalizedBudget(const CFinalizedBudget& other); void CleanAndRemove(); void AddOrUpdateVote(CFinalizedBudgetVote& vote); double GetScore(); bool HasMinimumRequiredSupport(); bool IsValid(); std::string GetName() {return strBudgetName; } std::string GetProposals(); int GetBlockStart() {return nBlockStart;} int GetBlockEnd() {return nBlockStart + (int)(vecProposals.size()-1);} std::string GetSubmittedBy() {return vin.prevout.ToStringShort();} int GetVoteCount() {return (int)mapVotes.size();} bool IsTransactionValid(const CTransaction& txNew, int nBlockHeight); bool GetProposalByBlock(int64_t nBlockHeight, CTxBudgetPayment& payment) { int i = nBlockHeight - GetBlockStart(); if(i < 0) return false; if(i > (int)vecProposals.size()-1) return false; payment = vecProposals[i]; return true; } bool GetPayeeAndAmount(int64_t nBlockHeight, CScript& payee, int64_t& nAmount) { int i = nBlockHeight - GetBlockStart(); if(i < 0) return false; if(i > (int)vecProposals.size()-1) return false; payee = vecProposals[i].payee; nAmount = vecProposals[i].nAmount; return true; } //check to see if we should vote on this void AutoCheck(); //total dash paid out by this budget int64_t GetTotalPayout(); //vote on this finalized budget as a masternode void SubmitVote(); //checks the hashes to make sure we know about them string GetStatus(); uint256 GetHash(){ /* vin is not included on purpose - Any masternode can make a proposal and the hashes should match regardless of who made it. - Someone could hyjack a new proposal by changing the vin and the signature check will fail. However, the network will still propagate the correct version and the incorrect one will be rejected. */ CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); ss << strBudgetName; ss << nBlockStart; ss << vecProposals; uint256 h1 = ss.GetHash(); return h1; } ADD_SERIALIZE_METHODS; //for saving to the serialized db template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(LIMITED_STRING(strBudgetName, 20)); READWRITE(vin); READWRITE(nBlockStart); READWRITE(vecProposals); READWRITE(mapVotes); } }; // FinalizedBudget are cast then sent to peers with this object, which leaves the votes out class CFinalizedBudgetBroadcast : public CFinalizedBudget { private: std::vector vchSig; public: bool fInvalid; CFinalizedBudgetBroadcast(); CFinalizedBudgetBroadcast(const CFinalizedBudget& other); CFinalizedBudgetBroadcast(CTxIn& vinIn, std::string strBudgetNameIn, int nBlockStartIn, std::vector vecProposalsIn); bool Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode); bool SignatureValid(); void Relay(); ADD_SERIALIZE_METHODS; //for propagating messages template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { //for syncing with other clients READWRITE(LIMITED_STRING(strBudgetName, 20)); READWRITE(vin); READWRITE(nBlockStart); READWRITE(vecProposals); READWRITE(vchSig); } }; // // CFinalizedBudgetVote - Allow a masternode node to vote and broadcast throughout the network // class CFinalizedBudgetVote { public: CTxIn vin; uint256 nBudgetHash; int64_t nTime; std::vector vchSig; CFinalizedBudgetVote(); CFinalizedBudgetVote(CTxIn vinIn, uint256 nBudgetHashIn); bool Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode); bool SignatureValid(); void Relay(); uint256 GetHash(){ CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); ss << vin; ss << nBudgetHash; ss << nTime; return ss.GetHash(); } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(vin); READWRITE(nBudgetHash); READWRITE(nTime); READWRITE(vchSig); } }; // // Budget Proposal : Contains the masternode votes for each budget // class CBudgetProposal { private: // critical section to protect the inner data structures mutable CCriticalSection cs; int64_t nAlloted; public: std::string strProposalName; /* json object with name, short-description, long-description, pdf-url and any other info This allows the proposal website to stay 100% decentralized */ std::string strURL; CTxIn vin; int nBlockStart; int nBlockEnd; int64_t nAmount; CScript address; int64_t nTime; map mapVotes; //cache object CBudgetProposal(); CBudgetProposal(const CBudgetProposal& other); CBudgetProposal(CTxIn vinIn, std::string strProposalNameIn, std::string strURLIn, int nBlockStartIn, int nBlockEndIn, CScript addressIn, CAmount nAmountIn); void Calculate(); void AddOrUpdateVote(CBudgetVote& vote); bool HasMinimumRequiredSupport(); std::pair GetVotes(); bool IsValid(std::string& strError); std::string GetName() {return strProposalName; } std::string GetURL() {return strURL; } int GetBlockStart() {return nBlockStart;} int GetBlockEnd() {return nBlockEnd;} CScript GetPayee() {return address;} int GetTotalPaymentCount(); int GetRemainingPaymentCount(); int GetBlockStartCycle(); int GetBlockCurrentCycle(); int GetBlockEndCycle(); double GetRatio(); int GetYeas(); int GetNays(); int GetAbstains(); int64_t GetAmount() {return nAmount;} void SetAllotted(int64_t nAllotedIn) {nAlloted = nAllotedIn;} int64_t GetAllotted() {return nAlloted;} std::string GetVoteCommand() { //c4 mnbudget vote one http://www.one.com/one.json 100 1000 xx9FwiqeRbuxBn5Sh3SNeoxmgpwQNSuMC4 1000 yes int nPayments = GetTotalPaymentCount(); CTxDestination address1; ExtractDestination(address, address1); CBitcoinAddress address2(address1); std::string strCommand = "dash-cli mnbudget vote " + strProposalName + " " + strURL + " " + boost::lexical_cast(nPayments); strCommand += " " + boost::lexical_cast(nBlockStart) + " " + address2.ToString() + " " + boost::lexical_cast(nAmount/COIN) + " yes|no"; return strCommand; } void CleanAndRemove(); uint256 GetHash(){ /* vin is not included on purpose - Any masternode can make a proposal and the hashes should match regardless of who made it. - Someone could hyjack a new proposal by changing the vin and the signature check will fail. However, the network will still propagate the correct version and the incorrect one will be rejected. */ CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); ss << strProposalName; ss << strURL; ss << nBlockStart; ss << nBlockEnd; ss << nAmount; ss << address; uint256 h1 = ss.GetHash(); return h1; } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { //for syncing with other clients READWRITE(LIMITED_STRING(strProposalName, 20)); READWRITE(LIMITED_STRING(strURL, 64)); READWRITE(vin); READWRITE(nBlockStart); READWRITE(nBlockEnd); READWRITE(nAmount); READWRITE(address); READWRITE(nTime); //for saving to the serialized db READWRITE(mapVotes); } }; // Proposals are cast then sent to peers with this object, which leaves the votes out class CBudgetProposalBroadcast : public CBudgetProposal { private: std::vector vchSig; public: bool fInvalid; CBudgetProposalBroadcast(); CBudgetProposalBroadcast(const CBudgetProposal& other); CBudgetProposalBroadcast(CTxIn vinIn, std::string strProposalNameIn, std::string strURL, int nPaymentCount, CScript addressIn, CAmount nAmountIn, int nBlockStartIn); bool Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode); bool SignatureValid(); void Relay(); ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { //for syncing with other clients READWRITE(LIMITED_STRING(strProposalName, 20)); READWRITE(LIMITED_STRING(strURL, 64)); READWRITE(vin); READWRITE(nBlockStart); READWRITE(nBlockEnd); READWRITE(nAmount); READWRITE(address); READWRITE(vchSig); } }; // // CBudgetVote - Allow a masternode node to vote and broadcast throughout the network // class CBudgetVote { public: CTxIn vin; uint256 nProposalHash; int nVote; int64_t nTime; std::vector vchSig; CBudgetVote(); CBudgetVote(CTxIn vin, uint256 nProposalHash, int nVoteIn); bool Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode); bool SignatureValid(); void Relay(); std::string GetVoteString() { std::string ret = "ABSTAIN"; if(nVote == VOTE_YES) ret = "YES"; if(nVote == VOTE_NO) ret = "NO"; return ret; } uint256 GetHash(){ CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); ss << vin; ss << nProposalHash; ss << nVote; ss << nTime; return ss.GetHash(); } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(vin); READWRITE(nProposalHash); READWRITE(nVote); READWRITE(nTime); READWRITE(vchSig); } }; #endif