Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/bitcoinrpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1240,7 +1240,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
if (strMethod == "createproposal" && n > 1) ConvertTo<int>(params[1]);
if (strMethod == "createproposal" && n > 2) ConvertTo<int>(params[2]);
if (strMethod == "createproposal" && n > 3) ConvertTo<int>(params[3]);
if (strMethod == "createproposal" && n > 4) ConvertTo<int>(params[4]);
if (strMethod == "createproposal" && n > 5) ConvertTo<int>(params[5]);
if (strMethod == "setvote" && n > 1) ConvertTo<int>(params[1]);
return params;
}
Expand Down
79 changes: 60 additions & 19 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
}
Expand Down Expand Up @@ -1468,7 +1476,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()));
}
Expand All @@ -1491,6 +1501,21 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs,
}
}

// If the given transaction is coinbase then check for correct refund outputs
else if (pindexBlock->nHeight >= VOTING_START){
CBlock block;
if (!block.ReadFromDisk(pindexBlock))
return error("ConnectInputs() : ReadFromDisk for connect failed");

vector<CTransaction> 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;
}

Expand Down Expand Up @@ -1597,7 +1622,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<uint256> vQueuedProposals;
vector<CTransaction> vQueuedTxProposals;
map<uint256, CTxIndex> mapQueuedChanges;
int64 nFees = 0;
int64 nValueIn = 0;
Expand Down Expand Up @@ -1654,9 +1679,12 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)

//Track vote proposals
if (tx.IsProposal()) {
//Needs to have the proper fee or else it will not be counted
if (nTxValueIn - nTxValueOut >= CVoteProposal::FEE - MIN_TXOUT_AMOUNT)
vQueuedProposals.push_back(hashTx);
CVoteProposal proposal;
if(!ProposalFromTransaction(tx, proposal)) {
return error("Proposal was not successfully extracted from transaction. This shouldn't happen.");
}

vQueuedTxProposals.push_back(tx);
}
}

Expand All @@ -1675,21 +1703,34 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
if (fJustCheck)
return true;

//TODO: LOG ERRORS OR TERMINATE METHOD AND RETURN ERROR?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably also want this code only to be activated if the height is above the voting height.

vector<CTransaction> vOrderedTxProposals;
if(!proposalManager.GetDeterministicOrdering(pindex->pprev->hashProofOfStake, vQueuedTxProposals, vOrderedTxProposals)) {
printf("ConnectBlock() : encountered error when determining deterministic ordering of proposals.");
}

vector<CTransaction> 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());
}
}

Expand Down
51 changes: 51 additions & 0 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CTransaction> vProposalTransactions;
for(CTransaction tx: pblock->vtx) {
if(tx.IsProposal()) {
vProposalTransactions.emplace_back(tx);
}
}

std::vector <CTransaction> 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;

Expand Down
29 changes: 21 additions & 8 deletions src/qt/createproposaldialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "walletmodel.h"
#include "voteproposal.h"
#include "voteobject.h"
#include "base58.h"
#include <QLineEdit>
#include <QMessageBox>

Expand Down Expand Up @@ -61,23 +62,38 @@ 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;
}

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);
ui->label_Size_result->setText(strSize);

//Set bit location in dialog
VoteLocation location;
if (!proposalManager.GetNextLocation(nBitCount, nStartHeight, nCheckSpan, location)) {
if(!proposalManager.GetNextLocation(nBitCount, nStartHeight, nCheckSpan, location)) {
QMessageBox msg;
msg.setText(tr("Failed to get next location from the proposal manager"));
msg.setText(tr("The specified voting span is already full. Try a different start and span."));
msg.exec();
return;
}
ui->label_Location_result->setText(QString::number(location.nLeastSignificantBit));

//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(), nMaxFee, strRefundAddress);

//Set proposal hash in dialog
uint256 hashProposal = proposal->GetHash();
Expand Down Expand Up @@ -112,9 +128,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::FEE)));
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);
}
38 changes: 15 additions & 23 deletions src/qt/forms/createproposaldialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -57,60 +57,52 @@
<widget class="QLineEdit" name="lineEdit_Length"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_Hash">
<widget class="QLabel" name="label_Max_Fee">
<property name="text">
<string>Proposal Hash:</string>
<string>Max Fee:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_Hash_result">
<property name="text">
<string>(Automatically Generated)</string>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit_Max_Fee"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_Location">
<item row="8" column="0">
<widget class="QLabel" name="label_Hash">
<property name="text">
<string>Bit Location:</string>
<string>Proposal Hash:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_Location_result">
<item row="8" column="1">
<widget class="QLabel" name="label_Hash_result">
<property name="text">
<string>(Automatically Generated)</string>
</property>
</widget>
</item>
<item row="6" column="0">
<item row="9" column="0">
<widget class="QLabel" name="label_Size">
<property name="text">
<string>Bit Size:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="9" column="1">
<widget class="QLabel" name="label_Size_result">
<property name="text">
<string>(Automatically Generated)</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_Fee">
<item row="5" column="0">
<widget class="QLabel" name="label_Refund_Address">
<property name="text">
<string>Fee:</string>
<string>Refund Address:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_Fee_result">
<property name="text">
<string>(Automatically Generated)</string>
</property>
</widget>
<item row="5" column="1">
<widget class="QLineEdit" name="lineEdit_Refund_Address"/>
</item>
</layout>
</item>
Expand Down
27 changes: 16 additions & 11 deletions src/rpcblockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,26 +309,32 @@ Value createproposal(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 6)
throw runtime_error(
"createproposal \n<strName>\n<nShift>\n<nStartBlock>\n<nCheckSpan>\n<nBits>\n<strDescription>\n"
"createproposal \n<strName>\n<nStartBlock>\n<nCheckSpan>\n<nBits>\n<strDescription>\n<nMaxFee>\n<strRefundAddress\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();
std::string strDescription = params[4].get_str();

// the maximum fee the proposal creator is willing to pay
int nMaxFee = params[5].get_int();

// the bit location object of the proposal
VoteLocation location(nShift + nBits - 1, nShift);
// the address that the left over fee should be sent to
std::string strRefundAddress = params[6].get_str();

Object results;
CVoteProposal proposal(strName, nStartTime, nCheckSpan, strDescription, location);
CVoteProposal proposal(strName, nStartTime, nCheckSpan, strDescription, nMaxFee, strRefundAddress);

//! Add the constructed proposal to a partial transaction
CTransaction tx;
Expand All @@ -340,7 +346,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));
Expand Down
Loading