From 1c8573202ce2223d3c6d47710bc64c5ea1a9549a Mon Sep 17 00:00:00 2001 From: IgorDurovic Date: Mon, 23 Apr 2018 16:43:57 -0600 Subject: [PATCH 01/14] updated .gitignore and added helper methods for determining fees --- src/qt/createproposaldialog.cpp | 8 +++++ src/voteproposal.cpp | 19 ++++++++++++ src/voteproposal.h | 5 +++ src/voteproposalmanager.cpp | 55 +++++++++++++++++++++++++++------ src/voteproposalmanager.h | 11 +++++++ 5 files changed, 88 insertions(+), 10 deletions(-) diff --git a/src/qt/createproposaldialog.cpp b/src/qt/createproposaldialog.cpp index 0f0fde106..dd49237b1 100644 --- a/src/qt/createproposaldialog.cpp +++ b/src/qt/createproposaldialog.cpp @@ -5,6 +5,7 @@ #include "walletmodel.h" #include "voteproposal.h" #include "voteobject.h" +#include "../voteproposal.h" #include #include @@ -79,6 +80,13 @@ void CreateProposalDialog::on_button_CreateProposal_clicked() //Create the actual proposal this->proposal = new CVoteProposal(strName.toStdString(), nStartHeight, nCheckSpan, strAbstract.toStdString(), location); + if(!this->proposal->IsValid()) { + QMessageBox msg; + msg.setText(tr("Proposal is invalid. Check to see if the span is greater than" +std::to_string(CVoteProposal::MAX_SPAN) + + " or if the start height is greater than " + std::to_string(CVoteProposal::GetMaxStartHeight()))); + return; + } + //Set proposal hash in dialog uint256 hashProposal = proposal->GetHash(); QString strHash = QString::fromStdString(hashProposal.GetHex()); diff --git a/src/voteproposal.cpp b/src/voteproposal.cpp index 6ff2e2ee0..05d514960 100644 --- a/src/voteproposal.cpp +++ b/src/voteproposal.cpp @@ -6,11 +6,30 @@ using namespace std; +static unsigned int CVoteProposal::GetMaxStartHeight() +{ + return pindexBest->nHeight + MAX_DISTANCE_TO_START; +} + uint256 CVoteProposal::GetHash() const { return SerializeHash(*this); } +bool CVoteProposal::IsValid() const +{ + if(nStartHeight > CVoteProposal::GetMaxStartHeight()) { + return error("%s: The distance from the current block to the start height of the proposal exceeds the" + "maximum", __func__); + } + + if(nCheckSpan > MAX_SPAN) { + return error("%s: The span of the proposal exceeds the maximum", __func__); + } + + return true; +} + /** * The vote proposal is serialized and added to a CTransaction as a data object via OP_RETURN transaction output. * The transaction is marked as a proposal by marking the first 4 bytes as "PROP" in ASCII diff --git a/src/voteproposal.h b/src/voteproposal.h index 88c575f28..fa4b85107 100644 --- a/src/voteproposal.h +++ b/src/voteproposal.h @@ -42,6 +42,10 @@ class CVoteProposal public: // the amount of HYP burnt when a proposal is made static const int64 FEE = 5 * COIN; + static const unsigned int MAX_SPAN = -1; //TODO: set + static const unsigned int MAX_DISTANCE_TO_START = -1; //TODO: set + + static unsigned int GetMaxStartHeight(); void SetNull() { @@ -80,6 +84,7 @@ class CVoteProposal READWRITE(bitLocation); ) + bool IsValid() const; bool ConstructTransaction (CTransaction& tx) const; int GetShift() const { return bitLocation.GetShift(); } uint8_t GetBitCount() const { return bitLocation.GetBitCount(); } diff --git a/src/voteproposalmanager.cpp b/src/voteproposalmanager.cpp index c65186925..546a00e2f 100644 --- a/src/voteproposalmanager.cpp +++ b/src/voteproposalmanager.cpp @@ -94,19 +94,54 @@ map CVoteProposalManager::GetActive(int nHeight) return mapActive; } +namespace +{ + //returns the maximum number of proposals overlapping at any point within the given range + int GetMaxOverlap(const vector& vProposals, const unsigned int& nStart, const unsigned int& nEnd) + { + int nMaxOverlapQuantity; + vector vOverlapCounter(nEnd - nStart + 1); + + for(auto proposalData: vProposals) { + if(proposalData.nHeightEnd < nStart) continue; + if(proposalData.nHeightStart > nEnd) continue; + + vOverlapCounter.at(proposalData.nHeightStart - nStart)++; + vOverlapCounter.at(proposalData.nHeightEnd - nStart)++; + } + + int nCurValueCounter = 0; + for(int count: vOverlapCounter) { + nCurValueCounter += count; + nMaxOverlapQuantity = max(nMaxOverlapQuantity, nCurValueCounter); + } + + return nMaxOverlapQuantity; + } + + //returns a vector of proposals that overlap with the given range + vector GetOverlappingProposals(const map& mapProposalData, + const int& nStart, const int& nEnd) + { + vector vConflictingTime; + for (auto it : mapProposalData) { + CProposalMetaData data = it.second; + if ((int)data.nHeightEnd < nStart) + continue; + if ((int)data.nHeightStart > nEnd) + continue; + vConflictingTime.emplace_back(data); + } + } +} + bool CVoteProposalManager::GetNextLocation(int nBitCount, int nStartHeight, int nCheckSpan, VoteLocation& location) { //Conflicts for block range - vector vConflictingTime; - for (auto it : mapProposalData) { - CProposalMetaData data = it.second; - int nEndHeight = nStartHeight + nCheckSpan; - if ((int)data.nHeightEnd < nStartHeight) - continue; - if ((int)data.nHeightStart > nEndHeight) - continue; - vConflictingTime.emplace_back(data); - } + vector vConflictingTime = GetOverlappingProposals(mapProposalData, nStartHeight, nStartHeight + nCheckSpan - 1); + + //Maximum number of conflicts for block range + int nMaxConflict = GetMaxOverlap(vConflictingTime, nStartHeight, nStartHeight + nCheckSpan - 1); //Find an open location for the new proposal, return left most bits if (vConflictingTime.empty()) { diff --git a/src/voteproposalmanager.h b/src/voteproposalmanager.h index 99d92b1ab..0943e52b4 100644 --- a/src/voteproposalmanager.h +++ b/src/voteproposalmanager.h @@ -10,10 +10,16 @@ class CVoteProposal; +//1. calculate fee for proposal +//2. fee is a function of bits used and range with a restriction on start time +//3. the input to the fee function is determined by most overlapped section of range + struct CProposalMetaData { uint256 hash; VoteLocation location; + + //this range must be inclusive unsigned int nHeightStart; unsigned int nHeightEnd; }; @@ -23,6 +29,11 @@ class CVoteProposalManager private: std::map mapProposalData; public: + static const int MAX_SPAN = -1; //TODO: set + static const int MAX_DISTANCE_TO_START = -1; //TODO: set + + static bool isProposalValid() + bool Add(const CVoteProposal& proposal); void Remove(const uint256& hashProposal); std::map GetActive(int nHeight); From 583902c32da4e8967fbbe9966de32e742b8568db Mon Sep 17 00:00:00 2001 From: Igor Durovic Date: Mon, 23 Apr 2018 20:08:55 -0600 Subject: [PATCH 02/14] first version dynamic fee calculation completed --- src/main.cpp | 12 +++- src/qt/createproposaldialog.cpp | 10 +-- src/voteproposal.cpp | 22 ++++--- src/voteproposal.h | 7 +-- src/voteproposalmanager.cpp | 105 ++++++++++++++++++++++++++++---- src/voteproposalmanager.h | 5 +- src/wallet.cpp | 23 +++++-- 7 files changed, 140 insertions(+), 44 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 1bd9460d3..621046378 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1654,8 +1654,18 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) //Track vote proposals if (tx.IsProposal()) { + CVoteProposal proposal; + if(!ProposalFromTransaction(tx, proposal)) { + return error("Proposal was not successfully extracted from transaction. This shouldn't happen."); + } + + unsigned int nFee; + if(!proposalManager.GetFee(proposal, nFee)) { + return error("Fee for proposal was not able to be calculated. This may mean the proposal is not valid."); + } + //Needs to have the proper fee or else it will not be counted - if (nTxValueIn - nTxValueOut >= CVoteProposal::FEE - MIN_TXOUT_AMOUNT) + if (nTxValueIn - nTxValueOut >= nFee - MIN_TXOUT_AMOUNT) vQueuedProposals.push_back(hashTx); } } diff --git a/src/qt/createproposaldialog.cpp b/src/qt/createproposaldialog.cpp index dd49237b1..84261e5c6 100644 --- a/src/qt/createproposaldialog.cpp +++ b/src/qt/createproposaldialog.cpp @@ -5,7 +5,6 @@ #include "walletmodel.h" #include "voteproposal.h" #include "voteobject.h" -#include "../voteproposal.h" #include #include @@ -80,13 +79,6 @@ void CreateProposalDialog::on_button_CreateProposal_clicked() //Create the actual proposal this->proposal = new CVoteProposal(strName.toStdString(), nStartHeight, nCheckSpan, strAbstract.toStdString(), location); - if(!this->proposal->IsValid()) { - QMessageBox msg; - msg.setText(tr("Proposal is invalid. Check to see if the span is greater than" +std::to_string(CVoteProposal::MAX_SPAN) + - " or if the start height is greater than " + std::to_string(CVoteProposal::GetMaxStartHeight()))); - return; - } - //Set proposal hash in dialog uint256 hashProposal = proposal->GetHash(); QString strHash = QString::fromStdString(hashProposal.GetHex()); @@ -120,7 +112,7 @@ void CreateProposalDialog::Clear() ui->lineEdit_Length->clear(); ui->lineEdit_Name->clear(); ui->lineEdit_StartBlock->clear(); - ui->label_Fee_result->setText(QString::fromStdString(FormatMoney(CVoteProposal::FEE))); + ui->label_Fee_result->setText(QString::fromStdString(FormatMoney(CVoteProposal::BASE_FEE))); ui->label_Hash_result->setText("(Automatically Generated)"); ui->label_Location_result->setText("(Automatically Generated)"); ui->label_Location_result->setText("(Automatically Generated)"); diff --git a/src/voteproposal.cpp b/src/voteproposal.cpp index 05d514960..21818dd81 100644 --- a/src/voteproposal.cpp +++ b/src/voteproposal.cpp @@ -6,11 +6,6 @@ using namespace std; -static unsigned int CVoteProposal::GetMaxStartHeight() -{ - return pindexBest->nHeight + MAX_DISTANCE_TO_START; -} - uint256 CVoteProposal::GetHash() const { return SerializeHash(*this); @@ -18,13 +13,20 @@ uint256 CVoteProposal::GetHash() const bool CVoteProposal::IsValid() const { - if(nStartHeight > CVoteProposal::GetMaxStartHeight()) { - return error("%s: The distance from the current block to the start height of the proposal exceeds the" - "maximum", __func__); + if (strName.empty() || strName.size() > MAX_CHAR_NAME) { + return error("Name needs to be between 1 and %d characters long", MAX_CHAR_NAME); + } + + if (strDescription.empty() || strDescription.size() > MAX_CHAR_ABSTRACT) { + return error("Abstract needs to be between 1 and %d characters long", MAX_CHAR_ABSTRACT); + } + + if (nStartHeight <= nBestHeight || nStartHeight > nBestHeight + MAX_BLOCKS_IN_FUTURE) { + return error("Start height needs to be greater than current height (%d) and less than %d.", nBestHeight, (nStartHeight + MAX_BLOCKS_IN_FUTURE)); } - if(nCheckSpan > MAX_SPAN) { - return error("%s: The span of the proposal exceeds the maximum", __func__); + if (!nCheckSpan || nCheckSpan > MAX_CHECKSPAN) { + return error("Voting length needs to be between 1 and %d blocks", MAX_CHECKSPAN); } return true; diff --git a/src/voteproposal.h b/src/voteproposal.h index fa4b85107..a98469e74 100644 --- a/src/voteproposal.h +++ b/src/voteproposal.h @@ -15,6 +15,7 @@ #define MAX_CHAR_ABSTRACT 30 #define MAX_BLOCKS_IN_FUTURE 28800 #define MAX_CHECKSPAN 28800 +#define MAX_BITCOUNT 28 //TODO: update #define MOST_RECENT_VERSION 1 @@ -41,11 +42,7 @@ class CVoteProposal std::string strDescription; public: // the amount of HYP burnt when a proposal is made - static const int64 FEE = 5 * COIN; - static const unsigned int MAX_SPAN = -1; //TODO: set - static const unsigned int MAX_DISTANCE_TO_START = -1; //TODO: set - - static unsigned int GetMaxStartHeight(); + static const int64 BASE_FEE = 5 * COIN; void SetNull() { diff --git a/src/voteproposalmanager.cpp b/src/voteproposalmanager.cpp index 546a00e2f..da60e9fea 100644 --- a/src/voteproposalmanager.cpp +++ b/src/voteproposalmanager.cpp @@ -96,27 +96,86 @@ map CVoteProposalManager::GetActive(int nHeight) namespace { + //An Event is either the beginning or end of a vote proposal span. + //This struct is used for GetMaxOverlap + struct Event + { + bool start; + int position; + int bitCount; + + //default constructor + Event() {} + + Event(bool start, int position, int bitCount = 0) + { + this->start = start; + this->position = position; + this->bitCount = bitCount; + } + + static bool Compare(const Event& lhs, const Event& rhs) + { + return lhs.position < rhs.position; + } + }; + //returns the maximum number of proposals overlapping at any point within the given range - int GetMaxOverlap(const vector& vProposals, const unsigned int& nStart, const unsigned int& nEnd) + /*unsigned int GetMaxOverlap(const vector& vProposals, const unsigned int& nStart, const unsigned int& nEnd) { - int nMaxOverlapQuantity; - vector vOverlapCounter(nEnd - nStart + 1); + int nMaxOverlapQuantity = -1; + vector vEvents(2 * vProposals.size()); for(auto proposalData: vProposals) { if(proposalData.nHeightEnd < nStart) continue; if(proposalData.nHeightStart > nEnd) continue; - vOverlapCounter.at(proposalData.nHeightStart - nStart)++; - vOverlapCounter.at(proposalData.nHeightEnd - nStart)++; + vEvents.emplace_back(Event(true, proposalData.nHeightStart)); + vEvents.emplace_back(Event(false, proposalData.nHeightEnd)); } + sort(vEvents.begin(), vEvents.end(), Event::Compare); + int nCurValueCounter = 0; - for(int count: vOverlapCounter) { - nCurValueCounter += count; + for(Event event: vEvents) { + nCurValueCounter += event.start ? 1 : -1; nMaxOverlapQuantity = max(nMaxOverlapQuantity, nCurValueCounter); } - return nMaxOverlapQuantity; + return (unsigned int)nMaxOverlapQuantity; + }*/ + + long GetResourceUsageHeuristic(const vector& vProposals, const CVoteProposal& proposal) + { + long nHeuristic = 0; + unsigned int nStart = proposal.GetStartHeight(); + unsigned int nEnd = proposal.GetStartHeight() + proposal.GetCheckSpan() - 1; + vector vEvents(2 * vProposals.size()); + + for(auto proposalData: vProposals) { + if(proposalData.nHeightEnd < nStart) continue; + if(proposalData.nHeightStart > nEnd) continue; + + Event startEvent(true, proposalData.nHeightStart, proposalData.location.GetBitCount()); + Event endEvent(false, proposalData.nHeightEnd, proposalData.location.GetBitCount()); + + vEvents.emplace_back(startEvent); + vEvents.emplace_back(endEvent); + } + + sort(vEvents.begin(), vEvents.end(), Event::Compare); + + int nCurValueCounter = 0; + for(unsigned int i = 0; i < vEvents.size() - 1; i++) { + Event curEvent = vEvents.at(i); + Event nextEvent = vEvents.at(i + 1); + int gap = nextEvent.position - curEvent.position; + + nCurValueCounter += curEvent.start ? curEvent.bitCount : -1 * curEvent.bitCount; + nHeuristic += (100000 * ((long) proposal.GetBitCount())) / (MAX_BITCOUNT - nCurValueCounter) * gap; + } + + return nHeuristic; } //returns a vector of proposals that overlap with the given range @@ -135,14 +194,38 @@ namespace } } +//TODO: test fee output for different inputs. Might change this to grow exponentially +bool CVoteProposalManager::GetFee(const CVoteProposal& proposal, unsigned int& nFee) +{ + if(!proposal.IsValid()){ + return error("Proposal is not valid"); + } + + //set the boundaries of the voting interval + unsigned int nStartHeight = proposal.GetStartHeight(); + unsigned int nEndHeight = nStartHeight + proposal.GetCheckSpan() - 1; + + //get conflicting proposals + vector vConflictingTime = GetOverlappingProposals(mapProposalData, nStartHeight, nEndHeight); + + //determine the maximum number of overlapping proposals at any point in the voting interval + long nHeuristic = GetResourceUsageHeuristic(vConflictingTime, proposal) / 100000; + + //TODO: this will need to be tested + nFee = (unsigned int)(nHeuristic * CVoteProposal::BASE_FEE); + + if(nFee < 0) { + return error("Fee should not be negative"); + } + + return true; +} + bool CVoteProposalManager::GetNextLocation(int nBitCount, int nStartHeight, int nCheckSpan, VoteLocation& location) { //Conflicts for block range vector vConflictingTime = GetOverlappingProposals(mapProposalData, nStartHeight, nStartHeight + nCheckSpan - 1); - //Maximum number of conflicts for block range - int nMaxConflict = GetMaxOverlap(vConflictingTime, nStartHeight, nStartHeight + nCheckSpan - 1); - //Find an open location for the new proposal, return left most bits if (vConflictingTime.empty()) { location.nMostSignificantBit = 27; diff --git a/src/voteproposalmanager.h b/src/voteproposalmanager.h index 0943e52b4..cf6ef1ddc 100644 --- a/src/voteproposalmanager.h +++ b/src/voteproposalmanager.h @@ -29,14 +29,11 @@ class CVoteProposalManager private: std::map mapProposalData; public: - static const int MAX_SPAN = -1; //TODO: set - static const int MAX_DISTANCE_TO_START = -1; //TODO: set - - static bool isProposalValid() bool Add(const CVoteProposal& proposal); void Remove(const uint256& hashProposal); std::map GetActive(int nHeight); + bool GetFee(const CVoteProposal& proposal, unsigned int& nFee); bool GetNextLocation(int nBitCount, int nStartHeight, int nCheckSpan, VoteLocation& location); std::map GetAllProposals() const { return mapProposalData; }; bool CheckProposal (const CVoteProposal& proposal); diff --git a/src/wallet.cpp b/src/wallet.cpp index c23f7decc..33272facd 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1927,7 +1927,18 @@ bool CWallet::FinalizeProposal(CTransaction& txProposal) //! Choose coins to use set > setCoins; int64 nValueIn = 0; - if (!SelectCoins(5 * COIN, GetTime(), setCoins, nValueIn, NULL) || nValueIn < CVoteProposal::FEE) + + CVoteProposal proposal; + if(!ProposalFromTransaction(txProposal, proposal)) { + return error("Proposal was not successfully extracted from transaction. This shouldn't happen."); + } + + unsigned int nFee; + if(!proposalManager.GetFee(proposal, nFee)) { + return error("Fee for proposal was not able to be calculated. This may mean the proposal is not valid."); + } + + if (!SelectCoins(5 * COIN, GetTime(), setCoins, nValueIn, NULL) || nValueIn < nFee) return error("Failed to select coins to spend"); //! Select one of the addresses to send the change to, and add inputs to the proposal tx @@ -1940,8 +1951,8 @@ bool CWallet::FinalizeProposal(CTransaction& txProposal) } //! Add change output - if (nValueIn > CVoteProposal::FEE + CENT) { - CTxOut out(nValueIn - CVoteProposal::FEE, scriptChange); + if (nValueIn > nFee + CENT) { + CTxOut out(nValueIn - nFee, scriptChange); txProposal.vout.push_back(out); } @@ -2556,7 +2567,11 @@ bool CWallet::SendProposal(const CVoteProposal& proposal, uint256& txid) printf("*** after available coins\n"); - int64 nFee = CVoteProposal::FEE; + unsigned int nFee; + if(!proposalManager.GetFee(proposal, nFee)) { + return error("Fee for proposal was not able to be calculated. This may mean the proposal is not valid."); + } + int64 nValueIn = 0; set > setCoins; From 719e4d3de8ddb2afae52b3825169db02b6b8735b Mon Sep 17 00:00:00 2001 From: Igor Durovic Date: Wed, 25 Apr 2018 19:00:53 -0600 Subject: [PATCH 03/14] fixed off by one bug in heuristic estimate --- src/qt/createproposaldialog.cpp | 2 +- src/voteproposalmanager.cpp | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/qt/createproposaldialog.cpp b/src/qt/createproposaldialog.cpp index 84261e5c6..466c3a665 100644 --- a/src/qt/createproposaldialog.cpp +++ b/src/qt/createproposaldialog.cpp @@ -112,7 +112,7 @@ void CreateProposalDialog::Clear() ui->lineEdit_Length->clear(); ui->lineEdit_Name->clear(); ui->lineEdit_StartBlock->clear(); - ui->label_Fee_result->setText(QString::fromStdString(FormatMoney(CVoteProposal::BASE_FEE))); + ui->label_Fee_result->setText(QString::fromStdString(FormatMoney(CVoteProposal::BASE_FEE))); // TODO: fee is dynamic ui->label_Hash_result->setText("(Automatically Generated)"); ui->label_Location_result->setText("(Automatically Generated)"); ui->label_Location_result->setText("(Automatically Generated)"); diff --git a/src/voteproposalmanager.cpp b/src/voteproposalmanager.cpp index da60e9fea..becf726fa 100644 --- a/src/voteproposalmanager.cpp +++ b/src/voteproposalmanager.cpp @@ -148,8 +148,9 @@ namespace long GetResourceUsageHeuristic(const vector& vProposals, const CVoteProposal& proposal) { long nHeuristic = 0; - unsigned int nStart = proposal.GetStartHeight(); - unsigned int nEnd = proposal.GetStartHeight() + proposal.GetCheckSpan() - 1; + int nStart = proposal.GetStartHeight(); + int nEnd = proposal.GetStartHeight() + proposal.GetCheckSpan(); + vector vEvents(2 * vProposals.size()); for(auto proposalData: vProposals) { @@ -157,7 +158,7 @@ namespace if(proposalData.nHeightStart > nEnd) continue; Event startEvent(true, proposalData.nHeightStart, proposalData.location.GetBitCount()); - Event endEvent(false, proposalData.nHeightEnd, proposalData.location.GetBitCount()); + Event endEvent(false, proposalData.nHeightEnd + 1, proposalData.location.GetBitCount()); vEvents.emplace_back(startEvent); vEvents.emplace_back(endEvent); @@ -169,9 +170,13 @@ namespace for(unsigned int i = 0; i < vEvents.size() - 1; i++) { Event curEvent = vEvents.at(i); Event nextEvent = vEvents.at(i + 1); - int gap = nextEvent.position - curEvent.position; nCurValueCounter += curEvent.start ? curEvent.bitCount : -1 * curEvent.bitCount; + + if(nextEvent.position <= nStart) continue; + if(curEvent.position > nEnd) break; + + int gap = min(nEnd, nextEvent.position) - max(nStart, curEvent.position); nHeuristic += (100000 * ((long) proposal.GetBitCount())) / (MAX_BITCOUNT - nCurValueCounter) * gap; } From 4ff57756a86050dc0ff0da2c9b9c0e7c732f203c Mon Sep 17 00:00:00 2001 From: Igor Durovic Date: Sat, 28 Apr 2018 14:41:17 -0600 Subject: [PATCH 04/14] updated constructor of vote location to accept max fee instead of vote location --- src/qt/createproposaldialog.cpp | 13 ++----------- src/rpcblockchain.cpp | 19 +++++++------------ src/voteproposal.h | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/qt/createproposaldialog.cpp b/src/qt/createproposaldialog.cpp index 466c3a665..9bf0c72a1 100644 --- a/src/qt/createproposaldialog.cpp +++ b/src/qt/createproposaldialog.cpp @@ -66,18 +66,9 @@ void CreateProposalDialog::on_button_CreateProposal_clicked() QString strSize = QString::number(nBitCount); ui->label_Size_result->setText(strSize); - //Set bit location in dialog - VoteLocation location; - if (!proposalManager.GetNextLocation(nBitCount, nStartHeight, nCheckSpan, location)) { - QMessageBox msg; - msg.setText(tr("Failed to get next location from the proposal manager")); - msg.exec(); - return; - } - ui->label_Location_result->setText(QString::number(location.nLeastSignificantBit)); - + //TODO: max fee //Create the actual proposal - this->proposal = new CVoteProposal(strName.toStdString(), nStartHeight, nCheckSpan, strAbstract.toStdString(), location); + this->proposal = new CVoteProposal(strName.toStdString(), nStartHeight, nCheckSpan, strAbstract.toStdString(), 0); //Set proposal hash in dialog uint256 hashProposal = proposal->GetHash(); diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 86f1213a8..3ecb179ee 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -309,26 +309,22 @@ Value createproposal(const Array& params, bool fHelp) { if (fHelp || params.size() != 6) throw runtime_error( - "createproposal \n\n\n\n\n\n\n" + "createproposal \n\n\n\n\n\n" "Returns new VoteProposal object with specified parameters\n"); // name of issue string strName = params[0].get_str(); - // check version for existing proposals Shift - uint8_t nShift = params[1].get_int(); // start time - will be changed to int StartHeight. unix time stamp - int64 nStartTime = params[2].get_int(); + int64 nStartTime = params[1].get_int(); // number of blocks with votes to count - int nCheckSpan = params[3].get_int(); + int nCheckSpan = params[2].get_int(); // cardinal items to vote on - convert to uint8 CheckSpan - uint8_t nBits = params[4].get_int(); + uint8_t nBits = params[3].get_int(); // description of issue - will go in different tx - std::string strDescription = params[5].get_str(); - - // the bit location object of the proposal - VoteLocation location(nShift + nBits - 1, nShift); + std::string strDescription = params[4].get_str(); Object results; - CVoteProposal proposal(strName, nStartTime, nCheckSpan, strDescription, location); + //TODO: max fee + CVoteProposal proposal(strName, nStartTime, nCheckSpan, strDescription, 0); //! Add the constructed proposal to a partial transaction CTransaction tx; @@ -340,7 +336,6 @@ Value createproposal(const Array& params, bool fHelp) results.emplace_back(Pair("proposal_hash", hashProposal.GetHex().c_str())); results.emplace_back(Pair("name", strName)); - results.emplace_back(Pair("shift", nShift)); results.emplace_back(Pair("start_block", (boost::int64_t)nStartTime)); results.emplace_back(Pair("check_span", nCheckSpan)); results.emplace_back(Pair("bit_count", nBits)); diff --git a/src/voteproposal.h b/src/voteproposal.h index a98469e74..77e2386e4 100644 --- a/src/voteproposal.h +++ b/src/voteproposal.h @@ -60,6 +60,19 @@ class CVoteProposal SetNull(); } + CVoteProposal(std::string strName, unsigned int nStartHeight, unsigned int nCheckSpan, std::string strDescription, + int nMaxFee, int nVersion = MOST_RECENT_VERSION) + { + this->nVersion = nVersion; + this->strName = strName; + this->nStartHeight = nStartHeight; + this->nCheckSpan = nCheckSpan; + this->strDescription = strDescription; + + //VoteLocation will be set when the proposal is accepted by the network and the dynamic fee is determined + } + + //DEPRECATED: USED FOR TESTING CVoteProposal(std::string strName, unsigned int nStartHeight, unsigned int nCheckSpan, std::string strDescription, VoteLocation location, int nVersion = MOST_RECENT_VERSION) { @@ -92,6 +105,7 @@ class CVoteProposal VoteLocation GetLocation() const { return bitLocation; } uint256 GetHash() const; + void SetLocation(VoteLocation location) { this->bitLocation = location; } }; bool ProposalFromTransaction(const CTransaction& tx, CVoteProposal& proposal); From 48d4a5222149218f5a1c87119758b47fb59e728e Mon Sep 17 00:00:00 2001 From: Igor Durovic Date: Sat, 28 Apr 2018 14:53:58 -0600 Subject: [PATCH 05/14] updated UI to reflect changes in vote proposal class --- src/qt/createproposaldialog.cpp | 3 --- src/qt/forms/createproposaldialog.ui | 38 ++++++++-------------------- 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/src/qt/createproposaldialog.cpp b/src/qt/createproposaldialog.cpp index 9bf0c72a1..78a49cf90 100644 --- a/src/qt/createproposaldialog.cpp +++ b/src/qt/createproposaldialog.cpp @@ -103,9 +103,6 @@ void CreateProposalDialog::Clear() ui->lineEdit_Length->clear(); ui->lineEdit_Name->clear(); ui->lineEdit_StartBlock->clear(); - ui->label_Fee_result->setText(QString::fromStdString(FormatMoney(CVoteProposal::BASE_FEE))); // TODO: fee is dynamic ui->label_Hash_result->setText("(Automatically Generated)"); - ui->label_Location_result->setText("(Automatically Generated)"); - ui->label_Location_result->setText("(Automatically Generated)"); ui->button_SendProposal->setEnabled(false); } diff --git a/src/qt/forms/createproposaldialog.ui b/src/qt/forms/createproposaldialog.ui index eb5ae1b68..18e31522c 100644 --- a/src/qt/forms/createproposaldialog.ui +++ b/src/qt/forms/createproposaldialog.ui @@ -57,61 +57,43 @@ - + - Proposal Hash: + Max Fee: - - - (Automatically Generated) - - + - - + + - Bit Location: + Proposal Hash: - - + + (Automatically Generated) - + Bit Size: - + (Automatically Generated) - - - - Fee: - - - - - - - (Automatically Generated) - - - From e49ef0e4ef1e67f094c27e1eb8b560bcdaf60236 Mon Sep 17 00:00:00 2001 From: Igor Durovic Date: Sat, 28 Apr 2018 15:04:39 -0600 Subject: [PATCH 06/14] span validation check at proposal creation --- src/qt/createproposaldialog.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/qt/createproposaldialog.cpp b/src/qt/createproposaldialog.cpp index 78a49cf90..fcfed1c50 100644 --- a/src/qt/createproposaldialog.cpp +++ b/src/qt/createproposaldialog.cpp @@ -66,6 +66,14 @@ void CreateProposalDialog::on_button_CreateProposal_clicked() QString strSize = QString::number(nBitCount); ui->label_Size_result->setText(strSize); + VoteLocation location; + if(!proposalManager.GetNextLocation(nBitCount, nStartHeight, nCheckSpan, location)) { + QMessageBox msg; + msg.setText(tr("The specified voting span is already full. Try a different start and span.").arg(MAX_CHECKSPAN)); + msg.exec(); + return; + } + //TODO: max fee //Create the actual proposal this->proposal = new CVoteProposal(strName.toStdString(), nStartHeight, nCheckSpan, strAbstract.toStdString(), 0); From b8589d8f1323633f6f94aa625a592aeb9db37981 Mon Sep 17 00:00:00 2001 From: Igor Durovic Date: Sat, 28 Apr 2018 16:51:25 -0600 Subject: [PATCH 07/14] base dynamic fee functionality added to createnewblock and deterministic ordering algo complete --- src/qt/createproposaldialog.cpp | 12 ++++++++++-- src/voteproposal.h | 7 +++++++ src/voteproposalmanager.cpp | 26 +++++++++++++++++++++++++- src/voteproposalmanager.h | 5 ++++- src/wallet.cpp | 5 +---- 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/qt/createproposaldialog.cpp b/src/qt/createproposaldialog.cpp index fcfed1c50..5e798903f 100644 --- a/src/qt/createproposaldialog.cpp +++ b/src/qt/createproposaldialog.cpp @@ -5,6 +5,7 @@ #include "walletmodel.h" #include "voteproposal.h" #include "voteobject.h" +#include "../voteproposal.h" #include #include @@ -61,6 +62,14 @@ void CreateProposalDialog::on_button_CreateProposal_clicked() return; } + int nMaxFee = ui->lineEdit_Max_Fee->text().toInt(); + if (nMaxFee < CVoteProposal::BASE_FEE) { + QMessageBox msg; + msg.setText(tr("Max Fee must be greater than or equal to %1").arg(CVoteProposal::BASE_FEE)); + msg.exec(); + return; + } + //Right now only supporting 2 bit votes int nBitCount = 2; QString strSize = QString::number(nBitCount); @@ -74,9 +83,8 @@ void CreateProposalDialog::on_button_CreateProposal_clicked() return; } - //TODO: max fee //Create the actual proposal - this->proposal = new CVoteProposal(strName.toStdString(), nStartHeight, nCheckSpan, strAbstract.toStdString(), 0); + this->proposal = new CVoteProposal(strName.toStdString(), nStartHeight, nCheckSpan, strAbstract.toStdString(), nMaxFee); //Set proposal hash in dialog uint256 hashProposal = proposal->GetHash(); diff --git a/src/voteproposal.h b/src/voteproposal.h index 77e2386e4..a46de86c7 100644 --- a/src/voteproposal.h +++ b/src/voteproposal.h @@ -26,6 +26,9 @@ class CVoteProposal // proposal version int nVersion; + // proposal max fee provided by creator of proposal + int nMaxFee; + // what to call the proposal std::string strName; @@ -47,6 +50,7 @@ class CVoteProposal void SetNull() { nVersion = 0; + nMaxFee = 0; strName = ""; nStartHeight = 0; nCheckSpan = 0; @@ -68,6 +72,7 @@ class CVoteProposal this->nStartHeight = nStartHeight; this->nCheckSpan = nCheckSpan; this->strDescription = strDescription; + this->nMaxFee = nMaxFee; //VoteLocation will be set when the proposal is accepted by the network and the dynamic fee is determined } @@ -87,6 +92,7 @@ class CVoteProposal IMPLEMENT_SERIALIZE ( READWRITE(nVersion); + READWRITE(nMaxFee); READWRITE(strName); READWRITE(nStartHeight); READWRITE(nCheckSpan); @@ -103,6 +109,7 @@ class CVoteProposal std::string GetDescription() const { return strDescription; } unsigned int GetStartHeight() const { return nStartHeight; } VoteLocation GetLocation() const { return bitLocation; } + int GetMaxFee() const { return nMaxFee; } uint256 GetHash() const; void SetLocation(VoteLocation location) { this->bitLocation = location; } diff --git a/src/voteproposalmanager.cpp b/src/voteproposalmanager.cpp index becf726fa..02fb88434 100644 --- a/src/voteproposalmanager.cpp +++ b/src/voteproposalmanager.cpp @@ -200,7 +200,7 @@ namespace } //TODO: test fee output for different inputs. Might change this to grow exponentially -bool CVoteProposalManager::GetFee(const CVoteProposal& proposal, unsigned int& nFee) +bool CVoteProposalManager::GetFee(const CVoteProposal& proposal, int& nFee) { if(!proposal.IsValid()){ return error("Proposal is not valid"); @@ -226,6 +226,30 @@ bool CVoteProposalManager::GetFee(const CVoteProposal& proposal, unsigned int& n return true; } +bool CVoteProposalManager::GetDeterministicOrdering(const uint256 &proofhash, std::vector &vProposalTransactions, + std::vector &vOrderedProposalTransactions) +{ + int nMask = 0x000FFFFF; + int nSegmentSize = 20; + int nSegmentOffset = 0; + while(!vProposalTransactions.empty()) { + uint256 nFormattedMask = nMask << (nSegmentOffset * nSegmentSize); + int segment = (int)((proofhash & nFormattedMask) >> (nSegmentOffset * nSegmentOffset)).Get64(); + int index = (int)(segment % vProposalTransactions.size()); + + if(segment < 0 || index < 0) { + return error("Generated index is invalid"); + } + + vOrderedProposalTransactions.emplace_back(vProposalTransactions.at(index)); + vProposalTransactions.erase(vProposalTransactions.begin() + index); + + nSegmentOffset = (nSegmentOffset + nSegmentSize) % 256; + } + + return true; +} + bool CVoteProposalManager::GetNextLocation(int nBitCount, int nStartHeight, int nCheckSpan, VoteLocation& location) { //Conflicts for block range diff --git a/src/voteproposalmanager.h b/src/voteproposalmanager.h index cf6ef1ddc..d0d366d01 100644 --- a/src/voteproposalmanager.h +++ b/src/voteproposalmanager.h @@ -9,6 +9,7 @@ #include "voteobject.h" class CVoteProposal; +class CTransaction; //1. calculate fee for proposal //2. fee is a function of bits used and range with a restriction on start time @@ -33,7 +34,9 @@ class CVoteProposalManager bool Add(const CVoteProposal& proposal); void Remove(const uint256& hashProposal); std::map GetActive(int nHeight); - bool GetFee(const CVoteProposal& proposal, unsigned int& nFee); + bool GetFee(const CVoteProposal& proposal, int& nFee); + bool GetDeterministicOrdering(const uint256& proofhash, std::vector& vProposalTransactions, + std::vector& vOrderedProposalTransactions); bool GetNextLocation(int nBitCount, int nStartHeight, int nCheckSpan, VoteLocation& location); std::map GetAllProposals() const { return mapProposalData; }; bool CheckProposal (const CVoteProposal& proposal); diff --git a/src/wallet.cpp b/src/wallet.cpp index 33272facd..8c0fe751a 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -2567,10 +2567,7 @@ bool CWallet::SendProposal(const CVoteProposal& proposal, uint256& txid) printf("*** after available coins\n"); - unsigned int nFee; - if(!proposalManager.GetFee(proposal, nFee)) { - return error("Fee for proposal was not able to be calculated. This may mean the proposal is not valid."); - } + int nFee = proposal.GetMaxFee(); int64 nValueIn = 0; From e3a2df404a8feb2ecf208cb5b2bfd81cf0d32dc0 Mon Sep 17 00:00:00 2001 From: Igor Durovic Date: Sat, 28 Apr 2018 16:59:34 -0600 Subject: [PATCH 08/14] fixed compile errors and removed deprecated usages of CVoteProposal::GetFee() --- src/main.cpp | 5 +---- src/qt/forms/createproposaldialog.ui | 4 ++-- src/wallet.cpp | 5 +---- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 621046378..b19f4f047 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1659,10 +1659,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) return error("Proposal was not successfully extracted from transaction. This shouldn't happen."); } - unsigned int nFee; - if(!proposalManager.GetFee(proposal, nFee)) { - return error("Fee for proposal was not able to be calculated. This may mean the proposal is not valid."); - } + int nFee = CVoteProposal::BASE_FEE; //Needs to have the proper fee or else it will not be counted if (nTxValueIn - nTxValueOut >= nFee - MIN_TXOUT_AMOUNT) diff --git a/src/qt/forms/createproposaldialog.ui b/src/qt/forms/createproposaldialog.ui index 18e31522c..7695328ce 100644 --- a/src/qt/forms/createproposaldialog.ui +++ b/src/qt/forms/createproposaldialog.ui @@ -57,14 +57,14 @@ - + Max Fee: - + diff --git a/src/wallet.cpp b/src/wallet.cpp index 8c0fe751a..da7886c72 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1933,10 +1933,7 @@ bool CWallet::FinalizeProposal(CTransaction& txProposal) return error("Proposal was not successfully extracted from transaction. This shouldn't happen."); } - unsigned int nFee; - if(!proposalManager.GetFee(proposal, nFee)) { - return error("Fee for proposal was not able to be calculated. This may mean the proposal is not valid."); - } + int nFee = CVoteProposal::BASE_FEE; if (!SelectCoins(5 * COIN, GetTime(), setCoins, nValueIn, NULL) || nValueIn < nFee) return error("Failed to select coins to spend"); From 755d22c7ec1af1a88845b5d7a3596e73df8fa2e0 Mon Sep 17 00:00:00 2001 From: Igor Durovic Date: Sat, 28 Apr 2018 18:15:59 -0600 Subject: [PATCH 09/14] created method for constructing refund transactions from proposals --- src/qt/createproposaldialog.cpp | 3 ++- src/rpcblockchain.cpp | 4 ++-- src/voteproposal.h | 10 +++++++++- src/voteproposalmanager.cpp | 19 +++++++++++++++++++ src/voteproposalmanager.h | 6 ++++++ 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/qt/createproposaldialog.cpp b/src/qt/createproposaldialog.cpp index 5e798903f..792b67ae7 100644 --- a/src/qt/createproposaldialog.cpp +++ b/src/qt/createproposaldialog.cpp @@ -83,8 +83,9 @@ void CreateProposalDialog::on_button_CreateProposal_clicked() return; } + //TODO: refund address //Create the actual proposal - this->proposal = new CVoteProposal(strName.toStdString(), nStartHeight, nCheckSpan, strAbstract.toStdString(), nMaxFee); + this->proposal = new CVoteProposal(strName.toStdString(), nStartHeight, nCheckSpan, strAbstract.toStdString(), nMaxFee, ""); //Set proposal hash in dialog uint256 hashProposal = proposal->GetHash(); diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 3ecb179ee..0ced015ee 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -323,8 +323,8 @@ Value createproposal(const Array& params, bool fHelp) std::string strDescription = params[4].get_str(); Object results; - //TODO: max fee - CVoteProposal proposal(strName, nStartTime, nCheckSpan, strDescription, 0); + //TODO: max fee & refund address + CVoteProposal proposal(strName, nStartTime, nCheckSpan, strDescription, 0, ""); //! Add the constructed proposal to a partial transaction CTransaction tx; diff --git a/src/voteproposal.h b/src/voteproposal.h index a46de86c7..48fc2bc42 100644 --- a/src/voteproposal.h +++ b/src/voteproposal.h @@ -43,6 +43,9 @@ class CVoteProposal // description of the proposal; may link to additional transactions std::string strDescription; + + // refund address + std::string strRefundAddress; public: // the amount of HYP burnt when a proposal is made static const int64 BASE_FEE = 5 * COIN; @@ -55,6 +58,7 @@ class CVoteProposal nStartHeight = 0; nCheckSpan = 0; strDescription = ""; + strRefundAddress = ""; } bool IsNull () const { return strName.empty(); } @@ -65,7 +69,7 @@ class CVoteProposal } CVoteProposal(std::string strName, unsigned int nStartHeight, unsigned int nCheckSpan, std::string strDescription, - int nMaxFee, int nVersion = MOST_RECENT_VERSION) + int nMaxFee, std::string strRefundAddress, int nVersion = MOST_RECENT_VERSION) { this->nVersion = nVersion; this->strName = strName; @@ -73,6 +77,7 @@ class CVoteProposal this->nCheckSpan = nCheckSpan; this->strDescription = strDescription; this->nMaxFee = nMaxFee; + this->strRefundAddress = strRefundAddress; //VoteLocation will be set when the proposal is accepted by the network and the dynamic fee is determined } @@ -81,6 +86,7 @@ class CVoteProposal CVoteProposal(std::string strName, unsigned int nStartHeight, unsigned int nCheckSpan, std::string strDescription, VoteLocation location, int nVersion = MOST_RECENT_VERSION) { + SetNull(); this->nVersion = nVersion; this->strName = strName; this->nStartHeight = nStartHeight; @@ -98,6 +104,7 @@ class CVoteProposal READWRITE(nCheckSpan); READWRITE(strDescription); READWRITE(bitLocation); + READWRITE(strRefundAddress); ) bool IsValid() const; @@ -110,6 +117,7 @@ class CVoteProposal unsigned int GetStartHeight() const { return nStartHeight; } VoteLocation GetLocation() const { return bitLocation; } int GetMaxFee() const { return nMaxFee; } + std::string GetRefundAddress() const { return strRefundAddress; } uint256 GetHash() const; void SetLocation(VoteLocation location) { this->bitLocation = location; } diff --git a/src/voteproposalmanager.cpp b/src/voteproposalmanager.cpp index 02fb88434..b81d89290 100644 --- a/src/voteproposalmanager.cpp +++ b/src/voteproposalmanager.cpp @@ -4,6 +4,7 @@ #include "voteproposalmanager.h" #include "voteproposal.h" +#include "base58.h" using namespace std; @@ -250,6 +251,24 @@ bool CVoteProposalManager::GetDeterministicOrdering(const uint256 &proofhash, st return true; } +bool CVoteProposalManager::GetRefundTransaction(const CVoteProposal &proposal, const int& nRequiredFee, const int& nTxFee, + const bool bProposalAccepted, CTransaction &txRefund) +{ + CBitcoinAddress refundAddress; + if(!refundAddress.SetString(proposal.GetRefundAddress())) { + return error("Refund Address of proposal is not valid"); + } + + txRefund.vin.resize(1); + txRefund.vin[0].prevout.SetNull(); + + txRefund.vout.resize(1); + txRefund.vout[0].scriptPubKey.SetDestination(refundAddress.Get()); + txRefund.vout[0].nValue = bProposalAccepted ? proposal.GetMaxFee() - nRequiredFee - nTxFee : proposal.GetMaxFee() - nTxFee; + + return true; +} + bool CVoteProposalManager::GetNextLocation(int nBitCount, int nStartHeight, int nCheckSpan, VoteLocation& location) { //Conflicts for block range diff --git a/src/voteproposalmanager.h b/src/voteproposalmanager.h index d0d366d01..a2f47ae16 100644 --- a/src/voteproposalmanager.h +++ b/src/voteproposalmanager.h @@ -10,6 +10,7 @@ class CVoteProposal; class CTransaction; +class CBitcoinAddress; //1. calculate fee for proposal //2. fee is a function of bits used and range with a restriction on start time @@ -34,10 +35,15 @@ class CVoteProposalManager bool Add(const CVoteProposal& proposal); void Remove(const uint256& hashProposal); std::map GetActive(int nHeight); + + //methods used in dynamic fee calculation bool GetFee(const CVoteProposal& proposal, int& nFee); bool GetDeterministicOrdering(const uint256& proofhash, std::vector& vProposalTransactions, std::vector& vOrderedProposalTransactions); bool GetNextLocation(int nBitCount, int nStartHeight, int nCheckSpan, VoteLocation& location); + bool GetRefundTransaction(const CVoteProposal &proposal, const int& nRequiredFee, const int& nTxFee, + const bool bProposalAccepted, CTransaction &txRefund); + std::map GetAllProposals() const { return mapProposalData; }; bool CheckProposal (const CVoteProposal& proposal); }; From d9abc226ecec50cfc4580cfca639ca052ecfe368 Mon Sep 17 00:00:00 2001 From: Igor Durovic Date: Sat, 28 Apr 2018 20:13:47 -0600 Subject: [PATCH 10/14] updated ui and rpc call for creating proposal --- src/bitcoinrpc.cpp | 2 +- src/qt/createproposaldialog.cpp | 16 ++++++++++++---- src/qt/forms/createproposaldialog.ui | 18 ++++++++++++++---- src/rpcblockchain.cpp | 16 +++++++++++++--- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 97b662895..2cdd2adc6 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1240,7 +1240,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector 1) ConvertTo(params[1]); if (strMethod == "createproposal" && n > 2) ConvertTo(params[2]); if (strMethod == "createproposal" && n > 3) ConvertTo(params[3]); - if (strMethod == "createproposal" && n > 4) ConvertTo(params[4]); + if (strMethod == "createproposal" && n > 5) ConvertTo(params[5]); if (strMethod == "setvote" && n > 1) ConvertTo(params[1]); return params; } diff --git a/src/qt/createproposaldialog.cpp b/src/qt/createproposaldialog.cpp index 792b67ae7..a4219234e 100644 --- a/src/qt/createproposaldialog.cpp +++ b/src/qt/createproposaldialog.cpp @@ -5,7 +5,7 @@ #include "walletmodel.h" #include "voteproposal.h" #include "voteobject.h" -#include "../voteproposal.h" +#include "base58.h" #include #include @@ -70,6 +70,15 @@ void CreateProposalDialog::on_button_CreateProposal_clicked() return; } + std::string strRefundAddress = ui->lineEdit_Refund_Address->text().toStdString(); + CBitcoinAddress address; + if (strRefundAddress.empty() || !address.SetString(strRefundAddress)) { + QMessageBox msg; + msg.setText(tr("The provided refund address is invalid")); + msg.exec(); + return; + } + //Right now only supporting 2 bit votes int nBitCount = 2; QString strSize = QString::number(nBitCount); @@ -78,14 +87,13 @@ void CreateProposalDialog::on_button_CreateProposal_clicked() VoteLocation location; if(!proposalManager.GetNextLocation(nBitCount, nStartHeight, nCheckSpan, location)) { QMessageBox msg; - msg.setText(tr("The specified voting span is already full. Try a different start and span.").arg(MAX_CHECKSPAN)); + msg.setText(tr("The specified voting span is already full. Try a different start and span.")); msg.exec(); return; } - //TODO: refund address //Create the actual proposal - this->proposal = new CVoteProposal(strName.toStdString(), nStartHeight, nCheckSpan, strAbstract.toStdString(), nMaxFee, ""); + this->proposal = new CVoteProposal(strName.toStdString(), nStartHeight, nCheckSpan, strAbstract.toStdString(), nMaxFee, strRefundAddress); //Set proposal hash in dialog uint256 hashProposal = proposal->GetHash(); diff --git a/src/qt/forms/createproposaldialog.ui b/src/qt/forms/createproposaldialog.ui index 7695328ce..8be5e5780 100644 --- a/src/qt/forms/createproposaldialog.ui +++ b/src/qt/forms/createproposaldialog.ui @@ -66,34 +66,44 @@ - + Proposal Hash: - + (Automatically Generated) - + Bit Size: - + (Automatically Generated) + + + + Refund Address: + + + + + + diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 0ced015ee..a9d861082 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -309,22 +309,32 @@ Value createproposal(const Array& params, bool fHelp) { if (fHelp || params.size() != 6) throw runtime_error( - "createproposal \n\n\n\n\n\n" + "createproposal \n\n\n\n\n\n\n Date: Mon, 7 May 2018 21:30:07 -0600 Subject: [PATCH 11/14] Added refund outputs to coinbase tx and added refund checks to ConnectInputs() --- src/main.cpp | 15 +++++++ src/voteproposalmanager.cpp | 90 +++++++++++++++++++++++++++++++++---- src/voteproposalmanager.h | 5 ++- 3 files changed, 99 insertions(+), 11 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b19f4f047..1183ea0a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1468,7 +1468,9 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, uint64 nCoinAge; if (!GetCoinAge(txdb, nCoinAge)) return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str()); + int64 nStakeReward = GetValueOut() - nValueIn; + if (nStakeReward > GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime, pindexBlock->nHeight) - GetMinFee() + MIN_TX_FEE) return DoS(100, error("ConnectInputs() : %s stake reward exceeded", GetHash().ToString().substr(0,10).c_str())); } @@ -1490,6 +1492,19 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, return DoS(100, error("ConnectInputs() : nFees out of range")); } } + else { + CBlock block; + if (!block.ReadFromDisk(pindexBlock)) + return error("ConnectInputs() : ReadFromDisk for connect failed"); + + vector vtxProposals; + if (!proposalManager.GetDeterministicOrdering(pindexBlock->hashProofOfStake, block.vtx, vtxProposals)) + return error("ConnectInputs() : Fetching deterministic ordering of tx proposals failed"); + + if (!proposalManager.CheckRefundTransaction(vtxProposals, *this)) { + return error("ConnectInputs() : Invalid refund outputs in coinbase transaction"); + } + } return true; } diff --git a/src/voteproposalmanager.cpp b/src/voteproposalmanager.cpp index b81d89290..4d247a318 100644 --- a/src/voteproposalmanager.cpp +++ b/src/voteproposalmanager.cpp @@ -152,8 +152,11 @@ namespace int nStart = proposal.GetStartHeight(); int nEnd = proposal.GetStartHeight() + proposal.GetCheckSpan(); + // An event is defined as either a proposal interval start or a proposal interval ending. vector vEvents(2 * vProposals.size()); + // For each proposal in vProposals that overlaps with the given proposal, create a start and end event then + // add it to vEvents. This vector will be used to determine the number of overlapping voting intervals efficiently. for(auto proposalData: vProposals) { if(proposalData.nHeightEnd < nStart) continue; if(proposalData.nHeightStart > nEnd) continue; @@ -165,8 +168,10 @@ namespace vEvents.emplace_back(endEvent); } + // sort the events so that those that happen earlier appear first in the vector sort(vEvents.begin(), vEvents.end(), Event::Compare); + // iterate through events in sorted order and keep a running counter of how many bits are consumed int nCurValueCounter = 0; for(unsigned int i = 0; i < vEvents.size() - 1; i++) { Event curEvent = vEvents.at(i); @@ -174,10 +179,14 @@ namespace nCurValueCounter += curEvent.start ? curEvent.bitCount : -1 * curEvent.bitCount; + // only start the counter when we have entered the voting intervals of the given proposal if(nextEvent.position <= nStart) continue; if(curEvent.position > nEnd) break; + // the number of bits used is guaranteed to be constant for every block between these two events int gap = min(nEnd, nextEvent.position) - max(nStart, curEvent.position); + + // TODO: heuristic updated; this is subject to change nHeuristic += (100000 * ((long) proposal.GetBitCount())) / (MAX_BITCOUNT - nCurValueCounter) * gap; } @@ -251,20 +260,83 @@ bool CVoteProposalManager::GetDeterministicOrdering(const uint256 &proofhash, st return true; } -bool CVoteProposalManager::GetRefundTransaction(const CVoteProposal &proposal, const int& nRequiredFee, const int& nTxFee, - const bool bProposalAccepted, CTransaction &txRefund) +//TODO: MAX TRANSACTION SIZE CHECK +bool CVoteProposalManager::AddRefundToCoinBase(const CVoteProposal &proposal, const int &nRequiredFee, const int &nTxFee, + const bool bProposalAccepted, CTransaction &txCoinBase) { + if (!txCoinBase.IsCoinBase()) { + return error("AddRefundToCoinBase() : Given transaction is the a coinbase transaction."); + } + CBitcoinAddress refundAddress; - if(!refundAddress.SetString(proposal.GetRefundAddress())) { - return error("Refund Address of proposal is not valid"); + if (!refundAddress.SetString(proposal.GetRefundAddress())) { + return error("AddRefundToCoinBase() : Refund Address of proposal is not valid"); + } + + CTxOut refundTxOut; + refundTxOut.nValue = bProposalAccepted ? proposal.GetMaxFee() - nRequiredFee - nTxFee : proposal.GetMaxFee() - nTxFee; + refundTxOut.scriptPubKey.SetDestination(refundAddress.Get()); + txCoinBase.vout.emplace_back(refundTxOut); + + return true; +} + +//TODO: REFACTOR AND COMMENT +bool CVoteProposalManager::CheckRefundTransaction(const std::vector &vOrderedTxProposals, + const CTransaction &txCoinBase) +{ + if (!txCoinBase.IsCoinBase()) { + return error("CheckRefundTransaction() : Given transaction is not a coinbase."); } - txRefund.vin.resize(1); - txRefund.vin[0].prevout.SetNull(); + CTransaction txExpectedCoinBase; - txRefund.vout.resize(1); - txRefund.vout[0].scriptPubKey.SetDestination(refundAddress.Get()); - txRefund.vout[0].nValue = bProposalAccepted ? proposal.GetMaxFee() - nRequiredFee - nTxFee : proposal.GetMaxFee() - nTxFee; + for(auto txProposal: vOrderedTxProposals) { + // output variables + int nRequiredFee; + CVoteProposal proposal; + VoteLocation location; + + // Skip this txProposal if a proposal object cannot be extracted from it + if(!ProposalFromTransaction(txProposal, proposal)) { + return error("CheckRefundTransaction() : Proposal was not able to be extracted from transaction."); + } + + // input variables + int nTxFee = (int)MIN_TX_FEE; //TODO: MAKE THIS HIGHER + int nBitCount = proposal.GetBitCount(); + int nStartHeight = proposal.GetStartHeight(); + int nCheckSpan = proposal.GetCheckSpan(); + + // If a valid voting location cannot be found then create an unaccepted proposal refund + if(!GetNextLocation(nBitCount, nStartHeight, nCheckSpan, location)) { + AddRefundToCoinBase(proposal, nRequiredFee, nTxFee, false, txExpectedCoinBase); + } else { + // If a fee cannot be calculated then skip this proposal without creating a refund tx + proposal.SetLocation(location); + if (!GetFee(proposal, nRequiredFee)) { + return error("CheckRefundTransaction() : Calculating fee for proposal failed."); + } + + // If the maximum fee provided by the proposal creator is less than the required fee + // then create an unaccepted proposal refund + if (nRequiredFee > proposal.GetMaxFee()) { + AddRefundToCoinBase(proposal, nRequiredFee, nTxFee, false, txExpectedCoinBase); + } else { + AddRefundToCoinBase(proposal, nRequiredFee, nTxFee, true, txExpectedCoinBase); + } + } + } + + for(int i = 0; i < txCoinBase.vout.size(); i++) { + if(txCoinBase.vout.at(i).scriptPubKey.GetID().GetHex() != txExpectedCoinBase.vout.at(i).scriptPubKey.GetID().GetHex()) { + return error("CheckRefundTransaction() : The scriptPubKey of the refund transaction isn't what it should be."); + } + + if(txCoinBase.vout.at(i).nValue != txExpectedCoinBase.vout.at(i).nValue) { + return error("CheckRefundTransaction() : The value of the refund isn't what it should be."); + } + } return true; } diff --git a/src/voteproposalmanager.h b/src/voteproposalmanager.h index a2f47ae16..1e4f3c7d5 100644 --- a/src/voteproposalmanager.h +++ b/src/voteproposalmanager.h @@ -41,8 +41,9 @@ class CVoteProposalManager bool GetDeterministicOrdering(const uint256& proofhash, std::vector& vProposalTransactions, std::vector& vOrderedProposalTransactions); bool GetNextLocation(int nBitCount, int nStartHeight, int nCheckSpan, VoteLocation& location); - bool GetRefundTransaction(const CVoteProposal &proposal, const int& nRequiredFee, const int& nTxFee, - const bool bProposalAccepted, CTransaction &txRefund); + bool AddRefundToCoinBase(const CVoteProposal &proposal, const int &nRequiredFee, const int &nTxFee, + const bool bProposalAccepted, CTransaction &txCoinBase); + bool CheckRefundTransaction(const std::vector &vOrderedTxProposals, const CTransaction &txCoinBase); std::map GetAllProposals() const { return mapProposalData; }; bool CheckProposal (const CVoteProposal& proposal); From a64e32de5b4d2b1657e2e628392ee9220a9ed04d Mon Sep 17 00:00:00 2001 From: Igor Durovic Date: Wed, 9 May 2018 21:05:47 -0600 Subject: [PATCH 12/14] added fork check conditionals to proposal code and proposal consensus rules to connectblock --- src/main.cpp | 51 +++++++++++++++++++++-------------- src/voteproposalmanager.cpp | 54 +++++++++++++++++++++++++++++++++---- src/voteproposalmanager.h | 2 ++ 3 files changed, 82 insertions(+), 25 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 1183ea0a6..7070d28ec 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1492,7 +1492,9 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, return DoS(100, error("ConnectInputs() : nFees out of range")); } } - else { + + // If the given transaction is coinbase then check for correct refund outputs + else if (VOTING_START >= pindexBlock->nHeight){ CBlock block; if (!block.ReadFromDisk(pindexBlock)) return error("ConnectInputs() : ReadFromDisk for connect failed"); @@ -1612,7 +1614,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) else nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size()); - vector vQueuedProposals; + vector vQueuedTxProposals; map mapQueuedChanges; int64 nFees = 0; int64 nValueIn = 0; @@ -1674,11 +1676,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) return error("Proposal was not successfully extracted from transaction. This shouldn't happen."); } - int nFee = CVoteProposal::BASE_FEE; - - //Needs to have the proper fee or else it will not be counted - if (nTxValueIn - nTxValueOut >= nFee - MIN_TXOUT_AMOUNT) - vQueuedProposals.push_back(hashTx); + vQueuedTxProposals.push_back(tx); } } @@ -1697,21 +1695,34 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) if (fJustCheck) return true; + //TODO: LOG ERRORS OR TERMINATE METHOD AND RETURN ERROR? + vector vOrderedTxProposals; + if(!proposalManager.GetDeterministicOrdering(pindex->pprev->hashProofOfStake, vQueuedTxProposals, vOrderedTxProposals)) { + printf("ConnectBlock() : encountered error when determining deterministic ordering of proposals."); + } + + vector vAcceptedTxProposals; + if(!proposalManager.GetAcceptedTxProposals(vtx[0], vOrderedTxProposals, vAcceptedTxProposals)) { + printf("ConnectBlock() : encountered error when extracting accepted proposals from coinbase"); + } + // Keep track of any vote proposals that were added to the blockchain CVoteDB voteDB; - if (vQueuedProposals.size()) { - for (const CTransaction& tx : vtx) { - uint256 txid = tx.GetHash(); - if (count(vQueuedProposals.begin(), vQueuedProposals.end(), txid)) { - CVoteProposal proposal; - if (ProposalFromTransaction(tx, proposal)) { - mapProposals[txid] = proposal.GetHash(); - if (!voteDB.WriteProposal(txid, proposal)) - printf("%s : failed to record proposal to db\n", __func__); - else if (!proposalManager.Add(proposal)) - printf("%s: failed to add proposal %s to manager\n", __func__, txid.GetHex().c_str()); - } - } + for (const CTransaction& txProposal: vAcceptedTxProposals) { + + // if the proposal isn't able to be extracted from transaction then skip it + CVoteProposal proposal; + if(!ProposalFromTransaction(txProposal, proposal)) { + printf("Proposal was not successfully extracted from transaction. This shouldn't happen."); + continue; + } + + // store proposal hash in mapProposals and store proposal on disk for later use + mapProposals[txProposal.GetHash()] = proposal.GetHash(); + if (!voteDB.WriteProposal(txProposal.GetHash(), proposal)) { + printf("%s : failed to record proposal to db\n", __func__); + } else if (!proposalManager.Add(proposal)) { + printf("%s: failed to add proposal %s to manager\n", __func__, txProposal.GetHash().GetHex().c_str()); } } diff --git a/src/voteproposalmanager.cpp b/src/voteproposalmanager.cpp index 4d247a318..a15443bfc 100644 --- a/src/voteproposalmanager.cpp +++ b/src/voteproposalmanager.cpp @@ -297,13 +297,13 @@ bool CVoteProposalManager::CheckRefundTransaction(const std::vector& vOrderedTxProposals, + std::vector& vAcceptedTxProposals) +{ + if (!txCoinBase.IsCoinBase()) { + return error("GetAcceptedTxProposals() : Given transaction is not a coinbase."); + } + + vAcceptedTxProposals.clear(); + + for(auto txProposal: vOrderedTxProposals) { + // output variables + int nRequiredFee; + CVoteProposal proposal; + VoteLocation location; + + // return error if a proposal object cannot be extracted from the tx + if(!ProposalFromTransaction(txProposal, proposal)) { + return error("GetAcceptedTxProposals() : Proposal was not able to be extracted from transaction."); + } + + // input variables + int nBitCount = proposal.GetBitCount(); + int nStartHeight = proposal.GetStartHeight(); + int nCheckSpan = proposal.GetCheckSpan(); + + if(GetNextLocation(nBitCount, nStartHeight, nCheckSpan, location)){ + // If a fee cannot be calculated then return error + proposal.SetLocation(location); + if (!GetFee(proposal, nRequiredFee)) { + return error("GetAcceptedTxProposals() : Calculating fee for proposal failed."); + } + + // If the max fee provided by the txProposal exceeds the required fee the accept the tx as a valid proposal + if (nRequiredFee >= proposal.GetMaxFee()) { + vAcceptedTxProposals.emplace_back(txProposal); + } } } diff --git a/src/voteproposalmanager.h b/src/voteproposalmanager.h index 1e4f3c7d5..c3f70d9d5 100644 --- a/src/voteproposalmanager.h +++ b/src/voteproposalmanager.h @@ -44,6 +44,8 @@ class CVoteProposalManager bool AddRefundToCoinBase(const CVoteProposal &proposal, const int &nRequiredFee, const int &nTxFee, const bool bProposalAccepted, CTransaction &txCoinBase); bool CheckRefundTransaction(const std::vector &vOrderedTxProposals, const CTransaction &txCoinBase); + bool GetAcceptedTxProposals(const CTransaction& txCoinBase, const std::vector& vOrderedTxProposals, + std::vector& vAcceptedTxProposals); std::map GetAllProposals() const { return mapProposalData; }; bool CheckProposal (const CVoteProposal& proposal); From 7131af99e70be3caf8e6bbaa2d406df75af6bc4c Mon Sep 17 00:00:00 2001 From: Igor Durovic Date: Fri, 11 May 2018 20:58:02 -0600 Subject: [PATCH 13/14] finished up dynamic fee process. still testing (debug print statements in code) --- src/main.cpp | 2 +- src/script.cpp | 22 ++++++++++++++++++---- src/voteproposal.h | 2 ++ src/voteproposalmanager.cpp | 28 +++++++++++++++++++++++++++- src/voteproposalmanager.h | 1 + src/wallet.cpp | 12 +++++++++--- 6 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 7070d28ec..96e65ae84 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1494,7 +1494,7 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, } // If the given transaction is coinbase then check for correct refund outputs - else if (VOTING_START >= pindexBlock->nHeight){ + else if (pindexBlock->nHeight >= VOTING_START){ CBlock block; if (!block.ReadFromDisk(pindexBlock)) return error("ConnectInputs() : ReadFromDisk for connect failed"); diff --git a/src/script.cpp b/src/script.cpp index 76641feba..1cb29df90 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1326,12 +1326,16 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector vchSig; - if (!key.Sign(hash, vchSig)) + if (!key.Sign(hash, vchSig)) { + printf("signing failed :( *****************"); return false; + } vchSig.push_back((unsigned char)nHashType); scriptSigRet << vchSig; @@ -1371,12 +1375,17 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash switch (whichTypeRet) { case TX_NONSTANDARD: + printf("****************** tx is not standard"); return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); + printf("***************** tx is pubkey: %s", keyID.ToString()); + printf("\n"); return Sign1(keyID, keystore, hash, nHashType, scriptSigRet); case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); + printf("***************** tx is pubkeyhash: %s", uint160(vSolutions[0])); + printf("\n"); if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) return false; else @@ -1387,13 +1396,16 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash } return true; case TX_SCRIPTHASH: + printf("***************** tx is scripthash"); return keystore.GetCScript(uint160(vSolutions[0]), scriptSigRet); case TX_MULTISIG: + printf("***************** tx is multisig"); scriptSigRet << OP_0; // workaround CHECKMULTISIG bug return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet)); case TX_NULL_DATA: - return true; + printf("**************** tx is null data"); + return true; } return false; } @@ -1613,8 +1625,10 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransa uint256 hash = SignatureHash(fromPubKey, txTo, nIn, nHashType); txnouttype whichType; - if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType)) + if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType)) { + printf("******************** solver failed :("); return false; + } if (whichType == TX_SCRIPTHASH) { diff --git a/src/voteproposal.h b/src/voteproposal.h index 48fc2bc42..13ad31f21 100644 --- a/src/voteproposal.h +++ b/src/voteproposal.h @@ -78,6 +78,8 @@ class CVoteProposal this->strDescription = strDescription; this->nMaxFee = nMaxFee; this->strRefundAddress = strRefundAddress; + //VoteLocation location; + this->bitLocation = VoteLocation(); //VoteLocation will be set when the proposal is accepted by the network and the dynamic fee is determined } diff --git a/src/voteproposalmanager.cpp b/src/voteproposalmanager.cpp index a15443bfc..d3d144087 100644 --- a/src/voteproposalmanager.cpp +++ b/src/voteproposalmanager.cpp @@ -206,6 +206,8 @@ namespace continue; vConflictingTime.emplace_back(data); } + + return vConflictingTime; } } @@ -420,4 +422,28 @@ bool CVoteProposalManager::GetNextLocation(int nBitCount, int nStartHeight, int } } return false; -} \ No newline at end of file +} + +bool CVoteProposalManager::GetRefundOutputSize(const CTransaction& txProposal, int& nSize) const +{ + + CTransaction txEmpty; + unsigned int nBaseSize = ::GetSerializeSize(txEmpty, SER_NETWORK, PROTOCOL_VERSION); + + if (!txProposal.IsProposal()) { + return error("GetRefundOutputSize() : Given transaction must be a proposal."); + } + + CVoteProposal proposal; + if (!ProposalFromTransaction(txProposal, proposal)) { + return error("GetRefundOutputSize() : Failed to extract proposal from transaction."); + } + + // Every refund output should increase the size of the coinbase tx by the same amount. + // 0, 0, and false are just filler values. + proposalManager.AddRefundToCoinBase(proposal, 0, 0, false, txEmpty); + + nSize = ::GetSerializeSize(txEmpty, SER_NETWORK, PROTOCOL_VERSION) - nBaseSize; + + return true; +} diff --git a/src/voteproposalmanager.h b/src/voteproposalmanager.h index c3f70d9d5..0e67b988d 100644 --- a/src/voteproposalmanager.h +++ b/src/voteproposalmanager.h @@ -46,6 +46,7 @@ class CVoteProposalManager bool CheckRefundTransaction(const std::vector &vOrderedTxProposals, const CTransaction &txCoinBase); bool GetAcceptedTxProposals(const CTransaction& txCoinBase, const std::vector& vOrderedTxProposals, std::vector& vAcceptedTxProposals); + bool GetRefundOutputSize(const CTransaction& txProposal, int& nSize) const; std::map GetAllProposals() const { return mapProposalData; }; bool CheckProposal (const CVoteProposal& proposal); diff --git a/src/wallet.cpp b/src/wallet.cpp index da7886c72..5d3f862d1 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -2586,8 +2586,10 @@ bool CWallet::SendProposal(const CVoteProposal& proposal, uint256& txid) //!Lookup the address of one of the inputs and return the change to that address uint256 hashBlock; CTransaction txPrev; - if(!::GetTransaction(wtx.vin[0].prevout.hash, txPrev, hashBlock)) + if(!::GetTransaction(wtx.vin[0].prevout.hash, txPrev, hashBlock)) { + printf("failed to select coins"); return error("%s: Failed to select coins", __func__); + } CScript scriptReturn = txPrev.vout[wtx.vin[0].prevout.n].scriptPubKey; CTxOut out(nChange, scriptReturn); @@ -2599,14 +2601,18 @@ bool CWallet::SendProposal(const CVoteProposal& proposal, uint256& txid) //! Sign the transaction int nIn = 0; for (const pair& coin : setCoins) { - if (!SignSignature(*this, *coin.first, wtx, nIn++)) + if (!SignSignature(*this, *coin.first, wtx, nIn++)) { + printf("could not sign transaction"); return false; + } } //! Broadcast the transaction to the network CReserveKey reserveKey = CReserveKey(this); - if (!CommitTransaction(wtx, reserveKey)) + if (!CommitTransaction(wtx, reserveKey)) { + printf("failed to commit transaction"); return error("%s: Failed to commit transaction", __func__); + } txid = wtx.GetHash(); From 2a8827670ffcf1c9d7015364f870f7280aef5ba2 Mon Sep 17 00:00:00 2001 From: Igor Durovic Date: Sat, 12 May 2018 18:28:22 -0600 Subject: [PATCH 14/14] removed debug statements and added voting tests --- src/main.cpp | 12 +- src/miner.cpp | 51 ++++++++ src/script.cpp | 11 -- src/test/voting_tests.cpp | 241 ++++++++++++++++++++++++++++++------ src/voteproposal.cpp | 8 +- src/voteproposalmanager.cpp | 20 +-- src/voteproposalmanager.h | 2 +- src/wallet.cpp | 3 - 8 files changed, 278 insertions(+), 70 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 96e65ae84..88d029dae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -333,11 +333,19 @@ bool CTransaction::IsProposal() const for (unsigned int i = 0; i < vout.size(); i++) { CScript scriptPubKey = vout[i].scriptPubKey; if (scriptPubKey.IsDataCarrier()) { - if (scriptPubKey.size() >= 5) { + if (scriptPubKey.size() > 0x4c) { + // "PROP" in ascii + if (scriptPubKey.at(3) == 0x70 && scriptPubKey.at(4) == 0x72 && scriptPubKey.at(5) == 0x6f && + scriptPubKey.at(6) == 0x70) { + return true; + } + } + else if(scriptPubKey.size() > 6) { // "PROP" in ascii if (scriptPubKey.at(2) == 0x70 && scriptPubKey.at(3) == 0x72 && scriptPubKey.at(4) == 0x6f && - scriptPubKey.at(5) == 0x70) + scriptPubKey.at(5) == 0x70) { return true; + } } } } diff --git a/src/miner.cpp b/src/miner.cpp index 5ee773e54..083f0a4a9 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -405,6 +405,57 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) } } + // TODO: ADD BLOCK HEIGHT FOR VOTING HARD FORK + // TODO: MIGHT NEED TO CHECK BLOCK SIZE CONSTRAINTS + // TODO: PUT THIS IN ANOTHER METHOD TO IMPROVE MODULARIZATION AND CODE REUSE + if(pindexPrev->nHeight >= VOTING_START) { + std::vector vProposalTransactions; + for(CTransaction tx: pblock->vtx) { + if(tx.IsProposal()) { + vProposalTransactions.emplace_back(tx); + } + } + + std::vector vOrderedProposalTransactions; + proposalManager.GetDeterministicOrdering(pindexPrev->hashProofOfStake, vProposalTransactions, + vOrderedProposalTransactions); + for (CTransaction txProposal: vOrderedProposalTransactions) { + // output variables + int nRequiredFee; + CVoteProposal proposal; + VoteLocation location; + CTransaction txCoinBase = pblock->vtx[0]; + + // Skip this txProposal if a proposal object cannot be extracted from it + if (!ProposalFromTransaction(txProposal, proposal)) { + continue; + } + + // input variables + int nTxFee = CVoteProposal::BASE_FEE; //TODO: DETERMINE THE BEST VALUE FROM TRIALS + int nBitCount = proposal.GetBitCount(); + int nStartHeight = proposal.GetStartHeight(); + int nCheckSpan = proposal.GetCheckSpan(); + + // If a valid voting location cannot be found then create an unaccepted proposal refund + if (!proposalManager.GetNextLocation(nBitCount, nStartHeight, nCheckSpan, location)) { + proposalManager.AddRefundToCoinBase(proposal, nRequiredFee, nTxFee, false, txCoinBase); + } else { + // If a fee cannot be calculated then skip this proposal without creating a refund tx + proposal.SetLocation(location); + if (!proposalManager.GetFee(proposal, nRequiredFee)) continue; + + // If the maximum fee provided by the proposal creator is less than the required fee + // then create an unaccepted proposal refund + if (nRequiredFee > proposal.GetMaxFee()) { + proposalManager.AddRefundToCoinBase(proposal, nRequiredFee, nTxFee, false, txCoinBase); + } else { + proposalManager.AddRefundToCoinBase(proposal, nRequiredFee, nTxFee, true, txCoinBase); + } + } + } + } + nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; diff --git a/src/script.cpp b/src/script.cpp index 1cb29df90..bc573f829 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1327,13 +1327,11 @@ bool Sign1(const CKeyID& address, const CKeyStore& keystore, uint256 hash, int n { CKey key; if (!keystore.GetKey(address, key)) { - printf("could not find the right key ******************"); return false; } vector vchSig; if (!key.Sign(hash, vchSig)) { - printf("signing failed :( *****************"); return false; } vchSig.push_back((unsigned char)nHashType); @@ -1375,17 +1373,12 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash switch (whichTypeRet) { case TX_NONSTANDARD: - printf("****************** tx is not standard"); return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); - printf("***************** tx is pubkey: %s", keyID.ToString()); - printf("\n"); return Sign1(keyID, keystore, hash, nHashType, scriptSigRet); case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); - printf("***************** tx is pubkeyhash: %s", uint160(vSolutions[0])); - printf("\n"); if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) return false; else @@ -1396,15 +1389,12 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash } return true; case TX_SCRIPTHASH: - printf("***************** tx is scripthash"); return keystore.GetCScript(uint160(vSolutions[0]), scriptSigRet); case TX_MULTISIG: - printf("***************** tx is multisig"); scriptSigRet << OP_0; // workaround CHECKMULTISIG bug return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet)); case TX_NULL_DATA: - printf("**************** tx is null data"); return true; } return false; @@ -1626,7 +1616,6 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransa txnouttype whichType; if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType)) { - printf("******************** solver failed :("); return false; } diff --git a/src/test/voting_tests.cpp b/src/test/voting_tests.cpp index 1291dfe50..1c4ce5551 100644 --- a/src/test/voting_tests.cpp +++ b/src/test/voting_tests.cpp @@ -1,10 +1,10 @@ #include #include #include -#include "json/json_spirit_writer_template.h" +#include "../json/json_spirit_writer_template.h" -#include "main.h" -#include "wallet.h" +#include "../main.h" +#include "../wallet.h" #include "../voteproposal.h" #include "../voteobject.h" #include "../votetally.h" @@ -16,57 +16,68 @@ using namespace std; BOOST_AUTO_TEST_SUITE(voting_tests) +std::string CONSTRUCT_ERROR = "failed to construct tx"; +std::string NOT_PROPOSAL_ERROR = "Transaction is not a proposal"; +std::string COULD_NOT_DESERIALIZE_ERROR = "Failed to deserialize"; +std::string BIT_LOCATION_ERROR = "location of proposal after it is deserialized isn't equal to its original value"; +std::string BIT_COUNT_ERROR = "bitcount of proposal after it is deserialized isn't equal to its original value"; +std::string SHIFT_ERROR = "shift of proposal after it is deserialized isn't equal to its original value"; +std::string START_HEIGHT_ERROR = "start height of proposal after it is deserialized isn't equal to its original value"; +std::string SPAN_ERROR = "check span of proposal after it is deserialized isn't equal to its original value"; +std::string DESCRIPTION_ERROR = "description of proposal after it is deserialized isn't equal to its original value"; +std::string NAME_ERROR = "name of proposal after it is deserialized isn't equal to its original value"; +std::string HASH_ERROR = "hash of proposal after it is deserialized isn't equal to its original value"; +std::string REFUND_ADDRESS_ERROR = "refund address is not equal to its original value after deserialization"; +std::string MAX_FEE_ERROR = "the max fee of the proposal after it is deserialized isn't equal to its original value"; +std::string DIFFERENT_ORDER_ERROR = "the deterministic ordering should be the same for both vectors but isn't"; +std::string SAME_ORDER_ERROR = "the deterministic orderings of the same vector for two different proofhashes are the same"; +std::string CREATE_ORDER_ERROR = "failed to construct deterministic ordering of proposals"; +std::string REFUND_OUT_ERROR = "the refund outputs in the coinbase tx were not constructed correctly"; + // name of issue -std::string strName = "proposal1"; +std::string strName = "ppppppppp"; // check version for existing proposals Shift uint8_t nShift = 20; // start time - will be changed to int StartHeight. unix time stamp -int64 nStartTime = 10000000; +int64 nStartHeight = 1; // number of blocks with votes to count int nCheckSpan = 1000; // cardinal items to vote on - convert to uint8 CheckSpan uint8_t nCardinals = 2; // description of issue - will go in different tx -std::string strDescription = "test_description"; +std::string strDescription = "ppppppppppppppppppppppppppp"; BOOST_AUTO_TEST_CASE(proposal_serialization) { std::cout << "testing proposal serialization\n"; CVoteProposalManager manager; - VoteLocation location; - manager.GetNextLocation(nCardinals, nStartTime, nCheckSpan, location); - CVoteProposal proposal(strName, nStartTime, nCheckSpan, strDescription, location); + manager.GetNextLocation(nCardinals, nStartHeight, nCheckSpan, location); + CVoteProposal proposal(strName, nStartHeight, nCheckSpan, strDescription, 5000000, "kxpSiwFkHJdNnYFojNDAvP3iiYyw3GH6ZL"); manager.Add(proposal); //! Add the constructed proposal to a partial transaction CTransaction tx; - BOOST_CHECK_MESSAGE(proposal.ConstructTransaction(tx), "failed to construct tx"); + BOOST_CHECK_MESSAGE(proposal.ConstructTransaction(tx), CONSTRUCT_ERROR); - BOOST_CHECK_MESSAGE(tx.IsProposal(), "Transaction is not a proposal!"); + BOOST_CHECK_MESSAGE(tx.IsProposal(), NOT_PROPOSAL_ERROR); CVoteProposal proposal2; - BOOST_CHECK_MESSAGE(ProposalFromTransaction(tx, proposal2), "Failed to deserialize"); - - //CHECKS TO DETERMINE IF DESERIALIZATION WORKS AS INTENDED - BOOST_CHECK_MESSAGE(proposal2.GetHash() == proposal.GetHash(), "hash of proposal after it is deserialized isn't equal" - "to its original value"); - BOOST_CHECK_MESSAGE(proposal2.GetLocation().nLeastSignificantBit == proposal.GetLocation().nLeastSignificantBit - && proposal2.GetLocation().nMostSignificantBit == proposal.GetLocation().nMostSignificantBit, - "location of proposal after it is deserialized isn't equal to its original value"); - BOOST_CHECK_MESSAGE(proposal2.GetBitCount() == proposal.GetBitCount(), "bitcount of proposal after it is deserialized isn't equal" - "to its original value"); - BOOST_CHECK_MESSAGE(proposal2.GetShift() == proposal.GetShift(), "shift of proposal after it is deserialized isn't equal" - "to its original value"); - BOOST_CHECK_MESSAGE(proposal2.GetStartHeight() == proposal.GetStartHeight(), "start height of proposal after it is deserialized isn't equal" - "to its original value"); - BOOST_CHECK_MESSAGE(proposal2.GetCheckSpan() == proposal.GetCheckSpan(), "check span of proposal after it is deserialized isn't equal" - "to its original value"); - BOOST_CHECK_MESSAGE(proposal2.GetDescription() == proposal.GetDescription(), "description of proposal after it is deserialized isn't equal" - "to its original value"); - BOOST_CHECK_MESSAGE(proposal2.GetName() == proposal.GetName(), "name of proposal after it is deserialized isn't equal" - "to its original value"); + BOOST_CHECK_MESSAGE(ProposalFromTransaction(tx, proposal2), COULD_NOT_DESERIALIZE_ERROR); + + //////////////////////////////////////////////////////////// + //CHECKS TO DETERMINE IF DESERIALIZATION WORKS AS INTENDED// + //////////////////////////////////////////////////////////// + BOOST_CHECK_MESSAGE(proposal2.GetHash() == proposal.GetHash(), HASH_ERROR); + BOOST_CHECK_MESSAGE(proposal2.GetRefundAddress() == proposal.GetRefundAddress(), REFUND_ADDRESS_ERROR); + BOOST_CHECK_MESSAGE(proposal2.GetMaxFee() == proposal.GetMaxFee(), MAX_FEE_ERROR); + BOOST_CHECK_MESSAGE(proposal2.GetBitCount() == proposal.GetBitCount(), BIT_COUNT_ERROR); + BOOST_CHECK_MESSAGE(proposal2.GetShift() == proposal.GetShift(), SHIFT_ERROR); + BOOST_CHECK_MESSAGE(proposal2.GetStartHeight() == proposal.GetStartHeight(), START_HEIGHT_ERROR); + BOOST_CHECK_MESSAGE(proposal2.GetCheckSpan() == proposal.GetCheckSpan(), SPAN_ERROR); + BOOST_CHECK_MESSAGE(proposal2.GetDescription() == proposal.GetDescription(), DESCRIPTION_ERROR); + BOOST_CHECK_MESSAGE(proposal2.GetName() == proposal.GetName(), NAME_ERROR); //! Create a tx that can be used as an input in the actual proposal tx CTransaction txFunding; @@ -162,28 +173,28 @@ BOOST_AUTO_TEST_CASE(vote_charset) CVoteProposalManager manager; VoteLocation location1; - manager.GetNextLocation(nCardinals, nStartTime, nCheckSpan, location1); - CVoteProposal proposal1(strName, nStartTime, nCheckSpan, strDescription, location1); + manager.GetNextLocation(nCardinals, nStartHeight, nCheckSpan, location1); + CVoteProposal proposal1(strName, nStartHeight, nCheckSpan, strDescription, location1); manager.Add(proposal1); VoteLocation location2; - manager.GetNextLocation(nCardinals, nStartTime, nCheckSpan, location2); - CVoteProposal proposal2(strName, nStartTime, nCheckSpan, strDescription, location2); + manager.GetNextLocation(nCardinals, nStartHeight, nCheckSpan, location2); + CVoteProposal proposal2(strName, nStartHeight, nCheckSpan, strDescription, location2); manager.Add(proposal2); VoteLocation location3; - manager.GetNextLocation(nCardinals, nStartTime, nCheckSpan, location3); - CVoteProposal proposal3(strName, nStartTime, nCheckSpan, strDescription, location3); + manager.GetNextLocation(nCardinals, nStartHeight, nCheckSpan, location3); + CVoteProposal proposal3(strName, nStartHeight, nCheckSpan, strDescription, location3); manager.Add(proposal3); VoteLocation location4; - manager.GetNextLocation(nCardinals, nStartTime, nCheckSpan, location4); - CVoteProposal proposal4(strName, nStartTime, nCheckSpan, strDescription, location4); + manager.GetNextLocation(nCardinals, nStartHeight, nCheckSpan, location4); + CVoteProposal proposal4(strName, nStartHeight, nCheckSpan, strDescription, location4); manager.Add(proposal4); VoteLocation location5; - manager.GetNextLocation(nCardinals, nStartTime, nCheckSpan, location5); - CVoteProposal proposal5(strName, nStartTime, nCheckSpan, strDescription, location5); + manager.GetNextLocation(nCardinals, nStartHeight, nCheckSpan, location5); + CVoteProposal proposal5(strName, nStartHeight, nCheckSpan, strDescription, location5); manager.Add(proposal5); uint32_t nVersion = 0x50000000; @@ -220,4 +231,152 @@ BOOST_AUTO_TEST_CASE(vote_charset) BOOST_CHECK_MESSAGE(nVersion == correctResult, "votes were not combined correctly"); } +BOOST_AUTO_TEST_CASE(vote_deterministic_ordering) { + + std::cout << "testing deterministic ordering of proposal transactions" << endl; + + CVoteProposalManager manager; + + std::string strName = "prop"; + std::string strDescription = "this is the description"; + std::string strRefundAddress = "kxpSiwFkHJdNnYFojNDAvP3iiYyw3GH6ZL"; + int nMaxFee = 5000000; + + CVoteProposal proposal1(strName, nStartHeight, nCheckSpan, strDescription + "1", nMaxFee, strRefundAddress); + CVoteProposal proposal2(strName, nStartHeight, nCheckSpan, strDescription + "2", nMaxFee, strRefundAddress); + CVoteProposal proposal3(strName, nStartHeight, nCheckSpan, strDescription + "3", nMaxFee, strRefundAddress); + CVoteProposal proposal4(strName, nStartHeight, nCheckSpan, strDescription + "4", nMaxFee, strRefundAddress); + CVoteProposal proposal5(strName, nStartHeight, nCheckSpan, strDescription + "5", nMaxFee, strRefundAddress); + + CTransaction txProposal1; + BOOST_CHECK_MESSAGE(proposal1.ConstructTransaction(txProposal1), CONSTRUCT_ERROR); + + CTransaction txProposal2; + BOOST_CHECK_MESSAGE(proposal2.ConstructTransaction(txProposal2), CONSTRUCT_ERROR); + + CTransaction txProposal3; + BOOST_CHECK_MESSAGE(proposal3.ConstructTransaction(txProposal3), CONSTRUCT_ERROR); + + CTransaction txProposal4; + BOOST_CHECK_MESSAGE(proposal4.ConstructTransaction(txProposal4), CONSTRUCT_ERROR); + + CTransaction txProposal5; + BOOST_CHECK_MESSAGE(proposal5.ConstructTransaction(txProposal5), CONSTRUCT_ERROR); + + vector vTxProposals = {txProposal1, txProposal2, txProposal3, txProposal4, txProposal5}; + + vector vOrderedTxProposals1; + BOOST_CHECK_MESSAGE(proposalManager.GetDeterministicOrdering(uint256("0xba04fd9cd0e9487f1a713e80828055284c04e362167ceb5f75db70153c613736"), + vTxProposals, vOrderedTxProposals1), CREATE_ORDER_ERROR); + + vector vOrderedTxProposals2; + BOOST_CHECK_MESSAGE(proposalManager.GetDeterministicOrdering(uint256("0xba04fd9cd0e9487f1a713e80828055284c04e362167ceb5f75db70153c613736"), + vTxProposals, vOrderedTxProposals2), CREATE_ORDER_ERROR); + + // verify that both orderings of tx proposals are the same + for(int i = 0; i < vTxProposals.size(); i++) { + BOOST_CHECK_MESSAGE(vOrderedTxProposals1.at(i).GetHash() == vOrderedTxProposals2.at(i).GetHash(), + DIFFERENT_ORDER_ERROR); + } + + vector vDifferentOrderedTxProposals; + BOOST_CHECK_MESSAGE(proposalManager.GetDeterministicOrdering(uint256("0xd7ec1705209f0212de02409cea9a9ca565571ae2977241f690dac11a7889d3f4"), + vTxProposals, vDifferentOrderedTxProposals), CREATE_ORDER_ERROR); + + // verify that the ordering of the tx proposals is different for a new proof hash + bool bSameOrder = true; + for(int i = 0; i < vTxProposals.size(); i++) { + bSameOrder = bSameOrder && (vOrderedTxProposals1.at(i).GetHash() == vDifferentOrderedTxProposals.at(i).GetHash()); + } + + BOOST_CHECK_MESSAGE(!bSameOrder, SAME_ORDER_ERROR); +} + +BOOST_AUTO_TEST_CASE(vote_refund_process) { + std::cout << "testing refund process for proposal requests" << endl; + + CVoteProposalManager manager; + + std::string strName = "prop"; + std::string strDescription = "this is the description"; + std::string strRefundAddress = "kxpSiwFkHJdNnYFojNDAvP3iiYyw3GH6ZL"; + int nMaxFee1 = 5000000; + int nMaxFee2 = 938049809; + int nMaxFee3 = 3; + int nMaxFee4 = 1000000000; + int nMaxFee5 = 6000000; + + CTransaction txCoinBase; + txCoinBase.vin.resize(1); + txCoinBase.vin[0].prevout.SetNull(); + txCoinBase.vout.resize(1); + + CVoteProposal proposal1(strName, nStartHeight, nCheckSpan, strDescription, nMaxFee1, strRefundAddress); + CVoteProposal proposal2(strName, nStartHeight, nCheckSpan, strDescription, nMaxFee2, strRefundAddress); + CVoteProposal proposal3(strName, nStartHeight, nCheckSpan, strDescription, nMaxFee3, strRefundAddress); + CVoteProposal proposal4(strName, nStartHeight, nCheckSpan, strDescription, nMaxFee4, strRefundAddress); + CVoteProposal proposal5(strName, nStartHeight, nCheckSpan, strDescription, nMaxFee5, strRefundAddress); + + CTransaction txProposal1; + BOOST_CHECK_MESSAGE(proposal1.ConstructTransaction(txProposal1), CONSTRUCT_ERROR); + + CTransaction txProposal2; + BOOST_CHECK_MESSAGE(proposal2.ConstructTransaction(txProposal2), CONSTRUCT_ERROR); + + CTransaction txProposal3; + BOOST_CHECK_MESSAGE(proposal3.ConstructTransaction(txProposal3), CONSTRUCT_ERROR); + + CTransaction txProposal4; + BOOST_CHECK_MESSAGE(proposal4.ConstructTransaction(txProposal4), CONSTRUCT_ERROR); + + CTransaction txProposal5; + BOOST_CHECK_MESSAGE(proposal5.ConstructTransaction(txProposal5), CONSTRUCT_ERROR); + + vector vTxProposals = {txProposal1, txProposal2, txProposal3, txProposal4, txProposal5}; + + vector vOrderedTxProposals; + BOOST_CHECK_MESSAGE(proposalManager.GetDeterministicOrdering(uint256("0xba04fd9cd0e9487f1a713e80828055284c04e362167ceb5f75db70153c613736"), + vTxProposals, vOrderedTxProposals), CREATE_ORDER_ERROR); + + for (CTransaction txProposal: vOrderedTxProposals) { + // output variables + int nRequiredFee; + CVoteProposal proposal; + VoteLocation location; + + // Skip this txProposal if a proposal object cannot be extracted from it + BOOST_CHECK_MESSAGE(ProposalFromTransaction(txProposal, proposal), CONSTRUCT_ERROR); + proposalManager.Add(proposal); + + // input variables + int nTxFee = CVoteProposal::BASE_FEE; + int nBitCount = proposal.GetBitCount(); + int nStartHeight = proposal.GetStartHeight(); + int nCheckSpan = proposal.GetCheckSpan(); + + // If a valid voting location cannot be found then create an unaccepted proposal refund + if (!proposalManager.GetNextLocation(nBitCount, nStartHeight, nCheckSpan, location)) { + proposalManager.AddRefundToCoinBase(proposal, nRequiredFee, nTxFee, false, txCoinBase); + } else { + BOOST_CHECK(true); + // If a fee cannot be calculated then skip this proposal without creating a refund tx + proposal.SetLocation(location); + BOOST_CHECK_MESSAGE(proposalManager.GetFee(proposal, nRequiredFee), "could not calculate fee."); + + BOOST_CHECK(true); + // If the maximum fee provided by the proposal creator is less than the required fee + // then create an unaccepted proposal refund + if (nRequiredFee > proposal.GetMaxFee()) { + BOOST_CHECK(true); + proposalManager.AddRefundToCoinBase(proposal, nRequiredFee, nTxFee, false, txCoinBase); + } else { + BOOST_CHECK(true); + proposalManager.AddRefundToCoinBase(proposal, nRequiredFee, nTxFee, true, txCoinBase); + } + } + } + + BOOST_CHECK_MESSAGE(proposalManager.CheckRefundTransaction(vOrderedTxProposals, txCoinBase), REFUND_OUT_ERROR); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/voteproposal.cpp b/src/voteproposal.cpp index 21818dd81..2dab43cb8 100644 --- a/src/voteproposal.cpp +++ b/src/voteproposal.cpp @@ -21,10 +21,6 @@ bool CVoteProposal::IsValid() const return error("Abstract needs to be between 1 and %d characters long", MAX_CHAR_ABSTRACT); } - if (nStartHeight <= nBestHeight || nStartHeight > nBestHeight + MAX_BLOCKS_IN_FUTURE) { - return error("Start height needs to be greater than current height (%d) and less than %d.", nBestHeight, (nStartHeight + MAX_BLOCKS_IN_FUTURE)); - } - if (!nCheckSpan || nCheckSpan > MAX_CHECKSPAN) { return error("Voting length needs to be between 1 and %d blocks", MAX_CHECKSPAN); } @@ -84,7 +80,9 @@ bool ProposalFromTransaction(const CTransaction& tx, CVoteProposal& proposal) vector vchProposal; CScript scriptProposal = tx.vout[0].scriptPubKey; - vchProposal.insert(vchProposal.end(), scriptProposal.begin() + 6, scriptProposal.end()); + + vchProposal.insert(vchProposal.end(), scriptProposal.begin() + (scriptProposal.size() > 0x04c ? 7 : 6), scriptProposal.end()); + CDataStream ss(vchProposal, SER_NETWORK, 0); try { diff --git a/src/voteproposalmanager.cpp b/src/voteproposalmanager.cpp index d3d144087..ade4dae8c 100644 --- a/src/voteproposalmanager.cpp +++ b/src/voteproposalmanager.cpp @@ -238,15 +238,14 @@ bool CVoteProposalManager::GetFee(const CVoteProposal& proposal, int& nFee) return true; } -bool CVoteProposalManager::GetDeterministicOrdering(const uint256 &proofhash, std::vector &vProposalTransactions, +bool CVoteProposalManager::GetDeterministicOrdering(const uint256 &proofhash, std::vector vProposalTransactions, std::vector &vOrderedProposalTransactions) { - int nMask = 0x000FFFFF; - int nSegmentSize = 20; + uint256 nMask(0x000FFFFF); int nSegmentOffset = 0; while(!vProposalTransactions.empty()) { - uint256 nFormattedMask = nMask << (nSegmentOffset * nSegmentSize); - int segment = (int)((proofhash & nFormattedMask) >> (nSegmentOffset * nSegmentOffset)).Get64(); + uint256 nFormattedMask = nMask << (nSegmentOffset); + int segment = (int)((proofhash & nFormattedMask) >> (nSegmentOffset)).Get64(); int index = (int)(segment % vProposalTransactions.size()); if(segment < 0 || index < 0) { @@ -254,9 +253,9 @@ bool CVoteProposalManager::GetDeterministicOrdering(const uint256 &proofhash, st } vOrderedProposalTransactions.emplace_back(vProposalTransactions.at(index)); - vProposalTransactions.erase(vProposalTransactions.begin() + index); - nSegmentOffset = (nSegmentOffset + nSegmentSize) % 256; + vProposalTransactions.erase(vProposalTransactions.begin() + index); + nSegmentOffset = (nSegmentOffset + segment) % 235; } return true; @@ -292,6 +291,9 @@ bool CVoteProposalManager::CheckRefundTransaction(const std::vector& vProposalTransactions, + bool GetDeterministicOrdering(const uint256& proofhash, std::vector vProposalTransactions, std::vector& vOrderedProposalTransactions); bool GetNextLocation(int nBitCount, int nStartHeight, int nCheckSpan, VoteLocation& location); bool AddRefundToCoinBase(const CVoteProposal &proposal, const int &nRequiredFee, const int &nTxFee, diff --git a/src/wallet.cpp b/src/wallet.cpp index 5d3f862d1..f56cc967f 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -2587,7 +2587,6 @@ bool CWallet::SendProposal(const CVoteProposal& proposal, uint256& txid) uint256 hashBlock; CTransaction txPrev; if(!::GetTransaction(wtx.vin[0].prevout.hash, txPrev, hashBlock)) { - printf("failed to select coins"); return error("%s: Failed to select coins", __func__); } @@ -2602,7 +2601,6 @@ bool CWallet::SendProposal(const CVoteProposal& proposal, uint256& txid) int nIn = 0; for (const pair& coin : setCoins) { if (!SignSignature(*this, *coin.first, wtx, nIn++)) { - printf("could not sign transaction"); return false; } } @@ -2610,7 +2608,6 @@ bool CWallet::SendProposal(const CVoteProposal& proposal, uint256& txid) //! Broadcast the transaction to the network CReserveKey reserveKey = CReserveKey(this); if (!CommitTransaction(wtx, reserveKey)) { - printf("failed to commit transaction"); return error("%s: Failed to commit transaction", __func__); }