From 07fcc55cd6ded445e09c937687780be18ac8475a Mon Sep 17 00:00:00 2001 From: Press Tab Date: Sun, 4 Oct 2015 14:44:50 -0600 Subject: [PATCH] MultiSend --- RATECoin-qt.pro | 3 + src/bitcoinrpc.cpp | 1 + src/bitcoinrpc.h | 1 + src/main.cpp | 5 + src/qt/bitcoingui.cpp | 109 +++++++++++---- src/qt/bitcoingui.h | 7 + src/qt/charitydialog.cpp | 208 ++++++++++++++++++++++++++++ src/qt/charitydialog.h | 36 +++++ src/qt/forms/charitydialog.ui | 241 ++++++++++++++++++++++++++++++++ src/rpcwallet.cpp | 250 ++++++++++++++++++++++++++++++++++ src/wallet.cpp | 77 +++++++++++ src/wallet.h | 26 +++- src/walletdb.cpp | 24 ++++ src/walletdb.h | 61 +++++++++ 14 files changed, 1020 insertions(+), 29 deletions(-) create mode 100644 src/qt/charitydialog.cpp create mode 100644 src/qt/charitydialog.h create mode 100644 src/qt/forms/charitydialog.ui diff --git a/RATECoin-qt.pro b/RATECoin-qt.pro index 097b8b2..56ede5f 100644 --- a/RATECoin-qt.pro +++ b/RATECoin-qt.pro @@ -254,6 +254,7 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/transactionview.h \ src/qt/walletmodel.h \ src/bitcoinrpc.h \ + src/qt/charitydialog.h \ src/qt/overviewpage.h \ src/qt/csvmodelwriter.h \ src/crypter.h \ @@ -351,6 +352,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/notificator.cpp \ src/qt/qtipcserver.cpp \ src/qt/rpcconsole.cpp \ + src/qt/charitydialog.cpp \ src/noui.cpp \ src/kernel.cpp \ src/scrypt-arm.S \ @@ -380,6 +382,7 @@ FORMS += \ src/qt/forms/aboutdialog.ui \ src/qt/forms/editaddressdialog.ui \ src/qt/forms/transactiondescdialog.ui \ + src/qt/forms/charitydialog.ui \ src/qt/forms/overviewpage.ui \ src/qt/forms/sendcoinsentry.ui \ src/qt/forms/askpassphrasedialog.ui \ diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index a6aa23c..4e3c790 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -309,6 +309,7 @@ static const CRPCCommand vRPCCommands[] = { "setstakesplitthreshold", &setstakesplitthreshold, false, false}, { "getstakesplitthreshold", &getstakesplitthreshold, false, false}, { "coinlock", &coinlock, false, false}, + { "multisend", &multisend, false, false }, }; CRPCTable::CRPCTable() diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 8df2686..5d9de4e 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -202,6 +202,7 @@ extern json_spirit::Value getnewpubkey(const json_spirit::Array& params, bool fH extern json_spirit::Value setstakesplitthreshold(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getstakesplitthreshold(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value coinlock(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value multisend(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp); diff --git a/src/main.cpp b/src/main.cpp index b139442..221a8a0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2379,6 +2379,11 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) } printf("ProcessBlock: ACCEPTED\n"); + + // If turned on MultiSend will send a transaction (or more) on the 30th confirmation of a stake + if (pwalletMain->fMultiSend) + if (!pwalletMain->MultiSend() ) + printf("ERROR While trying to use MultiSend"); // ppcoin: if responsible for sync-checkpoint send it if (pfrom && !CSyncCheckpoint::strMasterPrivKey.empty()) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 65a4d18..6c6d078 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -11,6 +11,7 @@ #include "signverifymessagedialog.h" #include "optionsdialog.h" #include "aboutdialog.h" +#include "charitydialog.h" #include "clientmodel.h" #include "walletmodel.h" #include "editaddressdialog.h" @@ -125,11 +126,9 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): transactionsPage->setLayout(vbox); addressBookPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab); - receiveCoinsPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab); - sendCoinsPage = new SendCoinsDialog(this); - + stakeForCharityDialog = new StakeForCharityDialog(this); signVerifyMessageDialog = new SignVerifyMessageDialog(this); centralWidget = new QStackedWidget(this); @@ -138,6 +137,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): centralWidget->addWidget(addressBookPage); centralWidget->addWidget(receiveCoinsPage); centralWidget->addWidget(sendCoinsPage); + centralWidget->addWidget(stakeForCharityDialog); setCentralWidget(centralWidget); // Create status bar @@ -208,6 +208,8 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): connect(addressBookPage, SIGNAL(verifyMessage(QString)), this, SLOT(gotoVerifyMessageTab(QString))); // Clicking on "Sign Message" in the receive coins page sends you to the sign message tab connect(receiveCoinsPage, SIGNAL(signMessage(QString)), this, SLOT(gotoSignMessageTab(QString))); + // Clicking on stake for charity button in the address book sends you to the multisend page + connect(addressBookPage, SIGNAL(stakeForCharitySignal(QString)), this, SLOT(charityClicked(QString))); gotoOverviewPage(); } @@ -261,6 +263,12 @@ void BitcoinGUI::createActions() repairWalletAction = new QAction(QIcon(":/icons/options"), tr("&Repair Wallet..."), this); repairWalletAction->setStatusTip(tr("Fix wallet integrity and remove orphans")); + + charityAction = new QAction(QIcon(":/icons/about"), tr("&MultiSend"), this); + charityAction->setToolTip(tr("MultiSend Settings")); + charityAction->setCheckable(true); + charityAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_7)); + tabGroup->addAction(charityAction); connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage())); @@ -319,6 +327,8 @@ void BitcoinGUI::createActions() connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab())); connect(checkWalletAction, SIGNAL(triggered()), this, SLOT(checkWallet())); connect(repairWalletAction, SIGNAL(triggered()), this, SLOT(repairWallet())); + connect(charityAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(charityAction, SIGNAL(triggered()), this, SLOT(charityClicked())); /* zeewolf: Hot swappable wallet themes */ if (themesList.count()>0) @@ -366,6 +376,7 @@ void BitcoinGUI::createMenuBar() settings->addAction(unlockWalletAction); settings->addAction(checkWalletAction); settings->addAction(repairWalletAction); + settings->addAction(charityAction); settings->addSeparator(); settings->addAction(optionsAction); @@ -460,6 +471,7 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) receiveCoinsPage->setModel(walletModel->getAddressTableModel()); sendCoinsPage->setModel(walletModel); signVerifyMessageDialog->setModel(walletModel); + stakeForCharityDialog->setModel(walletModel); setEncryptionStatus(walletModel->getEncryptionStatus()); connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SLOT(setEncryptionStatus(int))); @@ -725,6 +737,8 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int .data(Qt::EditRole).toULongLong(); if(!clientModel->inInitialBlockDownload()) { + fMultiSendNotify = pwalletMain->fMultiSendNotify; + // On new transaction, make an info balloon // Unless the initial block download is in progress, to prevent balloon-spam QString date = ttm->index(start, TransactionTableModel::Date, parent) @@ -738,7 +752,7 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int .data(Qt::DecorationRole)); notificator->notify(Notificator::Information, - (amount)<0 ? tr("Sent transaction") : + (amount)<0 ? (fMultiSendNotify == true ? tr("Sent MultiSend transaction") : tr("Sent transaction") ): tr("Incoming transaction"), tr("Date: %1\n" "Amount: %2\n" @@ -748,6 +762,8 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int .arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true)) .arg(type) .arg(address), icon); + + pwalletMain->fMultiSendNotify = false; } } @@ -1090,52 +1106,89 @@ void BitcoinGUI::updateStakingIcon() uint64_t nMinWeight = 0, nMaxWeight = 0, nWeight = 0; if (pwalletMain) pwalletMain->GetStakeWeight(*pwalletMain, nMinWeight, nMaxWeight, nWeight); - - if (nLastCoinStakeSearchInterval && nWeight) + if(walletModel) + fMultiSend = pwalletMain->fMultiSend; + + uint64_t nNetworkWeight = GetPoSKernelPS(); + unsigned nEstimateTime = 0; + if(GetAdjustedTime() > FORK_TIME) + nEstimateTime = nTargetSpacing2 * nNetworkWeight / nWeight; + else + nEstimateTime = nTargetSpacing * nNetworkWeight / nWeight; + + if (pwalletMain && pwalletMain->IsLocked()) + { + labelStakingIcon->setToolTip(tr("Not minting because wallet is locked.
Network weight is %1.
MultiSend: %2").arg(nNetworkWeight).arg(fMultiSend ? tr("Active"):tr("Not Active"))); + labelStakingIcon->setEnabled(false); + labelStakingIcon->setPixmap(QIcon(":/icons/mining_inactive").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + } + else if (vNodes.empty()) + { + labelStakingIcon->setToolTip(tr("Not minting because wallet is offline.
Network weight is %1.
MultiSend: %2").arg(nNetworkWeight).arg(fMultiSend ? tr("Active"):tr("Not Active"))); + labelStakingIcon->setEnabled(false); + labelStakingIcon->setPixmap(QIcon(":/icons/mining_inactive").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + } + else if (IsInitialBlockDownload()) + { + labelStakingIcon->setToolTip(tr("Not minting because wallet is syncing.
Network weight is %1.
MultiSend: %2").arg(nNetworkWeight).arg(fMultiSend ? tr("Active"):tr("Not Active"))); + labelStakingIcon->setEnabled(false); + labelStakingIcon->setPixmap(QIcon(":/icons/mining_inactive").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + } + else if (!nWeight) { - uint64_t nNetworkWeight = GetPoSKernelPS(); - unsigned nEstimateTime = 0; - if(GetAdjustedTime() > FORK_TIME) - nEstimateTime = nTargetSpacing2 * nNetworkWeight / nWeight; - else - nEstimateTime = nTargetSpacing * nNetworkWeight / nWeight; + labelStakingIcon->setToolTip(tr("Not minting because you don't have mature coins.
Network weight is %1
MultiSend: %2").arg(nNetworkWeight).arg(fMultiSend ? tr("Active"):tr("Not Active"))); + labelStakingIcon->setEnabled(false); + labelStakingIcon->setPixmap(QIcon(":/icons/mining_inactive").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + } + else if (nLastCoinStakeSearchInterval) + { + uint64_t nAccuracyAdjustment = 1; // this is a manual adjustment param if needed to make more accurate + uint64_t nEstimateTime = 180 * nNetworkWeight / nWeight / nAccuracyAdjustment; + + uint64_t nRangeLow = nEstimateTime; + uint64_t nRangeHigh = nEstimateTime * 1.5; QString text; if (nEstimateTime < 60) { - text = tr("%n second(s)", "", nEstimateTime); + text = tr("%1 - %2 seconds").arg(nRangeLow).arg(nRangeHigh); } else if (nEstimateTime < 60*60) { - text = tr("%n minute(s)", "", nEstimateTime/60); + text = tr("%1 - %2 minutes").arg(nRangeLow / 60).arg(nRangeHigh / 60); } else if (nEstimateTime < 24*60*60) { - text = tr("%n hour(s)", "", nEstimateTime/(60*60)); + text = tr("%1 - %2 hours").arg(nRangeLow / (60*60)).arg(nRangeHigh / (60*60)); } else { - text = tr("%n day(s)", "", nEstimateTime/(60*60*24)); + text = tr("%1 - %2 days").arg(nRangeLow / (60*60*24)).arg(nRangeHigh / (60*60*24)); } labelStakingIcon->setPixmap(QIcon(":/icons/staking_on").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); - labelStakingIcon->setToolTip(tr("Staking.
Your weight is %1
Network weight is %2
Expected time to earn reward is %3").arg(nWeight).arg(nNetworkWeight).arg(text)); + labelStakingIcon->setEnabled(true); + labelStakingIcon->setToolTip(tr("Minting.
Your weight is %1.
Network weight is %2.
Estimated next stake in %3.
MultiSend: %4").arg(nWeight).arg(nNetworkWeight).arg(text).arg(fMultiSend ? tr("Active"):tr("Not Active"))); } else { - labelStakingIcon->setPixmap(QIcon(":/icons/staking_off").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); - if (pwalletMain && pwalletMain->IsLocked()) - labelStakingIcon->setToolTip(tr("Not staking because wallet is locked")); - else if (vNodes.empty()) - labelStakingIcon->setToolTip(tr("Not staking because wallet is offline")); - else if (IsInitialBlockDownload()) - labelStakingIcon->setToolTip(tr("Not staking because wallet is syncing")); - else if (!nWeight) - labelStakingIcon->setToolTip(tr("Not staking because you don't have mature coins")); - else - labelStakingIcon->setToolTip(tr("Not staking")); + labelStakingIcon->setToolTip(tr("Not minting.")); + labelStakingIcon->setEnabled(false); + labelStakingIcon->setPixmap(QIcon(":/icons/mining_inactive").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); } } +void BitcoinGUI::charityClicked(QString addr) +{ + charityAction->setChecked(true); + centralWidget->setCurrentWidget(stakeForCharityDialog); + + if(!addr.isEmpty()) + stakeForCharityDialog->setAddress(addr); + + exportAction->setEnabled(false); + disconnect(exportAction, SIGNAL(triggered()), 0, 0); +} + /* zeewolf: Hot swappable wallet themes */ void BitcoinGUI::changeTheme(QString theme) { diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index a0baf56..a6f13ce 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -14,6 +14,7 @@ class SendCoinsDialog; class SignVerifyMessageDialog; class Notificator; class RPCConsole; +class StakeForCharityDialog; QT_BEGIN_NAMESPACE class QLabel; @@ -65,6 +66,7 @@ class BitcoinGUI : public QMainWindow AddressBookPage *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; SignVerifyMessageDialog *signVerifyMessageDialog; + StakeForCharityDialog *stakeForCharityDialog; QLabel *labelEncryptionIcon; QLabel *labelStakingIcon; @@ -96,6 +98,7 @@ class BitcoinGUI : public QMainWindow QAction *openRPCConsoleAction; QAction *checkWalletAction; QAction *repairWalletAction; + QAction *charityAction; QSystemTrayIcon *trayIcon; Notificator *notificator; @@ -111,6 +114,9 @@ class BitcoinGUI : public QMainWindow QString themesDir; QAction *customActions[100]; /* Themes support */ + + bool fMultiSend; + bool fMultiSendNotify; /** Create the main UI actions. */ void createActions(); @@ -199,6 +205,7 @@ private slots: void toggleHidden(); void updateStakingIcon(); + void charityClicked(QString addr = ""); /** Load external QSS stylesheet */ void changeTheme(QString theme); diff --git a/src/qt/charitydialog.cpp b/src/qt/charitydialog.cpp new file mode 100644 index 0000000..d9f7ba6 --- /dev/null +++ b/src/qt/charitydialog.cpp @@ -0,0 +1,208 @@ +#include "charitydialog.h" +#include "ui_charitydialog.h" + +#include "walletmodel.h" +#include "base58.h" +#include "addressbookpage.h" +#include "init.h" + +#include +#include + +#include + +using namespace std; +using namespace boost; + +StakeForCharityDialog::StakeForCharityDialog(QWidget *parent) : + QWidget(parent), + ui(new Ui::StakeForCharityDialog), + model(0) +{ + ui->setupUi(this); +} + +StakeForCharityDialog::~StakeForCharityDialog() +{ + delete ui; +} + +void StakeForCharityDialog::setModel(WalletModel *model) +{ + this->model = model; +} + +void StakeForCharityDialog::setAddress(const QString &address) +{ + setAddress(address, ui->charityAddressEdit); +} + +void StakeForCharityDialog::setAddress(const QString &address, QLineEdit *addrEdit) +{ + addrEdit->setText(address); + addrEdit->setFocus(); +} + +void StakeForCharityDialog::on_addressBookButton_clicked() +{ + if (model && model->getAddressTableModel()) + { + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); + dlg.setModel(model->getAddressTableModel()); + if (dlg.exec()) + setAddress(dlg.getReturnValue(), ui->charityAddressEdit); + } +} + +void StakeForCharityDialog::on_viewButton_clicked() +{ + std::pair pMultiSend; + std::string strMultiSendPrint = ""; + if(pwalletMain->fMultiSend) + strMultiSendPrint += "MultiSend Active\n"; + else + strMultiSendPrint += "MultiSend Not Active\n"; + + for(int i = 0; i < (int)pwalletMain->vMultiSend.size(); i++) + { + pMultiSend = pwalletMain->vMultiSend[i]; + strMultiSendPrint += pMultiSend.first.c_str(); + strMultiSendPrint += " - "; + strMultiSendPrint += boost::lexical_cast(pMultiSend.second); + strMultiSendPrint += "% \n"; + } + + ui->message->setProperty("status", "ok"); + ui->message->style()->polish(ui->message); + ui->message->setText(QString(strMultiSendPrint.c_str())); + return; +} + + +void StakeForCharityDialog::on_addButton_clicked() +{ + bool fValidConversion = false; + + std::string strAddress = ui->charityAddressEdit->text().toStdString(); + if (!CBitcoinAddress(strAddress).IsValid()) + { + ui->message->setProperty("status", "error"); + ui->message->style()->polish(ui->message); + ui->message->setText(tr("The entered address:\n") + ui->charityAddressEdit->text() + tr(" is invalid.\nPlease check the address and try again.")); + ui->charityAddressEdit->setFocus(); + return; + } + + + int nCharityPercent = ui->charityPercentEdit->text().toInt(&fValidConversion, 10); + int nSumMultiSend = 0; + for(int i = 0; i < (int)pwalletMain->vMultiSend.size(); i++) + nSumMultiSend += pwalletMain->vMultiSend[i].second; + + if(nSumMultiSend + nCharityPercent > 100) + { + ui->message->setProperty("status", "error"); + ui->message->style()->polish(ui->message); + ui->message->setText(tr("The total amount of your MultiSend vector is over 100% of your stake reward\n")); + ui->charityAddressEdit->setFocus(); + return; + } + if (!fValidConversion || nCharityPercent > 100 || nCharityPercent <= 0) + { + ui->message->setProperty("status", "error"); + ui->message->style()->polish(ui->message); + ui->message->setText(tr("Please Enter 1 - 100 for percent.")); + ui->charityPercentEdit->setFocus(); + return; + } + + std::pair pMultiSend; + pMultiSend.first = strAddress; + pMultiSend.second = nCharityPercent; + pwalletMain->vMultiSend.push_back(pMultiSend); + + ui->message->setProperty("status", "ok"); + ui->message->style()->polish(ui->message); + + std::string strMultiSendPrint = ""; + for(int i = 0; i < (int)pwalletMain->vMultiSend.size(); i++) + { + pMultiSend = pwalletMain->vMultiSend[i]; + strMultiSendPrint += pMultiSend.first.c_str(); + strMultiSendPrint += " - "; + strMultiSendPrint += boost::lexical_cast(pMultiSend.second); + strMultiSendPrint += "% \n"; + } + CWalletDB walletdb(pwalletMain->strWalletFile); + walletdb.WriteMultiSend(pwalletMain->vMultiSend); + ui->message->setText(tr("MultiSend Vector\n") + QString(strMultiSendPrint.c_str())); + return; +} + +void StakeForCharityDialog::on_deleteButton_clicked() +{ + std::vector > vMultiSendTemp = pwalletMain->vMultiSend; + std::string strAddress = ui->charityAddressEdit->text().toStdString(); + bool fRemoved = false; + for(int i = 0; i < (int)pwalletMain->vMultiSend.size(); i++) + { + if(pwalletMain->vMultiSend[i].first == strAddress) + { + pwalletMain->vMultiSend.erase(pwalletMain->vMultiSend.begin() + i); + fRemoved = true; + } + } + + CWalletDB walletdb(pwalletMain->strWalletFile); + + if(!walletdb.EraseMultiSend(vMultiSendTemp)) + fRemoved = false; + if(!walletdb.WriteMultiSend(pwalletMain->vMultiSend)) + fRemoved = false; + + if(fRemoved) + ui->message->setText(tr("Removed ") + QString(strAddress.c_str())); + else + ui->message->setText(tr("Could not locate address\n")); + return; +} + +void StakeForCharityDialog::on_activateButton_clicked() +{ + std::string strRet = ""; + if(pwalletMain->vMultiSend.size() < 1) + strRet = "Unable to activate MultiSend, check MultiSend vector\n"; + else if(CBitcoinAddress(pwalletMain->vMultiSend[0].first).IsValid()) + { + pwalletMain->fMultiSend = true; + CWalletDB walletdb(pwalletMain->strWalletFile); + if(!walletdb.WriteMSettings(true, pwalletMain->nLastMultiSendHeight)) + strRet = "MultiSend activated but writing settings to DB failed"; + else + strRet = "MultiSend activated"; + } + else + strRet = "First Address Not Valid"; + + ui->message->setProperty("status", "ok"); + ui->message->style()->polish(ui->message); + ui->message->setText(tr(strRet.c_str())); + return; +} + +void StakeForCharityDialog::on_disableButton_clicked() +{ + std::string strRet = ""; + pwalletMain->fMultiSend = false; + CWalletDB walletdb(pwalletMain->strWalletFile); + if(!walletdb.WriteMSettings(false, pwalletMain->nLastMultiSendHeight)) + strRet = "MultiSend deactivated but writing settings to DB failed"; + else + strRet = "MultiSend deactivated"; + + ui->message->setProperty("status", ""); + ui->message->style()->polish(ui->message); + ui->message->setText(tr(strRet.c_str())); + return; +} + diff --git a/src/qt/charitydialog.h b/src/qt/charitydialog.h new file mode 100644 index 0000000..9b19fbc --- /dev/null +++ b/src/qt/charitydialog.h @@ -0,0 +1,36 @@ +#ifndef CHARITYDIALOG_H +#define CHARITYDIALOG_H + +#include + +namespace Ui { +class StakeForCharityDialog; +} +class WalletModel; +class QLineEdit; +class StakeForCharityDialog : public QWidget +{ + Q_OBJECT + +public: + explicit StakeForCharityDialog(QWidget *parent = 0); + ~StakeForCharityDialog(); + + void setModel(WalletModel *model); + void setAddress(const QString &address); + void setAddress(const QString &address, QLineEdit *addrEdit); +private slots: + void on_viewButton_clicked(); + void on_addButton_clicked(); + void on_deleteButton_clicked(); + void on_activateButton_clicked(); + void on_disableButton_clicked(); + void on_addressBookButton_clicked(); + + +private: + Ui::StakeForCharityDialog *ui; + WalletModel *model; +}; + +#endif // CHARITYDIALOG_H diff --git a/src/qt/forms/charitydialog.ui b/src/qt/forms/charitydialog.ui new file mode 100644 index 0000000..2d02814 --- /dev/null +++ b/src/qt/forms/charitydialog.ui @@ -0,0 +1,241 @@ + + + StakeForCharityDialog + + + Qt::NonModal + + + + 0 + 0 + 652 + 300 + + + + MultiSend + + + + + 170 + 120 + 131 + 20 + + + + Enter whole numbers 1 - 100 + + + Enter % to Give (1-100) + + + + + + 170 + 150 + 401 + 21 + + + + + + + Enter Address to Send to + + + + + + 30 + 0 + 451 + 121 + + + + 1 + + + MultiSend allows you to automatically send up to 100% of your stake to a list of other RateCoin address after it matures. +To Add: enter percentage to give and RateCoin address to add to the MultiSend vector. +To Delete: Enter address to delete and press delete. +MultiSend will not be activated unless you have clicked Activate + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + 240 + 200 + 381 + 91 + + + + + + + + + + 20 + 230 + 91 + 23 + + + + + + + Add to MultiSend Vector + + + Add + + + + + + 120 + 260 + 91 + 23 + + + + Deactivate MultiSend + + + Deactivate + + + + + + 570 + 150 + 28 + 21 + + + + Choose an address from the address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + false + + + + + + 30 + 120 + 121 + 20 + + + + Percentage of stake to send + + + Percentage: + + + + + + 30 + 150 + 111 + 16 + + + + Address to send portion of stake to + + + Address: + + + + + + 120 + 230 + 91 + 23 + + + + + + + Delete Address From MultiSend Vector + + + Delete + + + + + + 20 + 260 + 91 + 23 + + + + Activate MultiSend + + + Activate + + + + + + 60 + 200 + 121 + 23 + + + + + + + View MultiSend Vector + + + View MultiSend + + + + + + + + diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index f7d9316..2330b51 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1871,4 +1871,254 @@ Value coinlock(const Array& params, bool fHelp) return "Did not recognize command"; return result; +} + +//presstab HyperStake +Array printMultiSend() +{ + Array ret; + Object act; + act.push_back(Pair("MultiSend Activated?", pwalletMain->fMultiSend)); + ret.push_back(act); + if(pwalletMain->vDisabledAddresses.size() >= 1) + { + Object disAdd; + for(unsigned int i = 0; i < pwalletMain->vDisabledAddresses.size(); i++) + { + disAdd.push_back(Pair("Disabled From Sending", pwalletMain->vDisabledAddresses[i])); + } + ret.push_back(disAdd); + } + + ret.push_back("MultiSend Addresses to Send To:"); + Object vMS; + for(unsigned int i = 0; i < pwalletMain->vMultiSend.size(); i++) + { + vMS.push_back(Pair("Address " + boost::lexical_cast(i), pwalletMain->vMultiSend[i].first)); + vMS.push_back(Pair("Percent", pwalletMain->vMultiSend[i].second)); + } + ret.push_back(vMS); + return ret; +} + +//presstab HyperStake +Array printAddresses() +{ + std::vector vCoins; + pwalletMain->AvailableCoins(vCoins); + std::map mapAddresses; + + BOOST_FOREACH(const COutput& out, vCoins) + { + CTxDestination utxoAddress; + ExtractDestination(out.tx->vout[out.i].scriptPubKey, utxoAddress); + std::string strAdd = CBitcoinAddress(utxoAddress).ToString(); + + if(mapAddresses.find(strAdd) == mapAddresses.end()) //if strAdd is not already part of the map + { + mapAddresses[strAdd] = (double)out.tx->vout[out.i].nValue / (double)COIN; + } + else + { + mapAddresses[strAdd] += (double)out.tx->vout[out.i].nValue / (double)COIN; + } + } + Array ret; + for (map::const_iterator it = mapAddresses.begin(); it != mapAddresses.end(); ++it) + { + Object obj; + const std::string* strAdd = &(*it).first; + const double* nBalance = &(*it).second; + obj.push_back(Pair("Address ", *strAdd)); + obj.push_back(Pair("Balance ", *nBalance)); + ret.push_back(obj); + } + return ret; +} + +//presstab HyperStake +unsigned int sumMultiSend() +{ + unsigned int sum = 0; + for(unsigned int i = 0; i < pwalletMain->vMultiSend.size(); i++) + { + sum += pwalletMain->vMultiSend[i].second; + } + return sum; +} + +// presstab HyperStake +Value multisend(const Array ¶ms, bool fHelp) +{ + CWalletDB walletdb(pwalletMain->strWalletFile); + bool fFileBacked; + + //MultiSend Commands + if(params.size() == 1) + { + string strCommand = params[0].get_str(); + Object ret; + if(strCommand == "print") + { + return printMultiSend(); + } + else if(strCommand == "printaddress" || strCommand == "printaddresses") + { + return printAddresses(); + } + else if(strCommand == "clear") + { + LOCK(pwalletMain->cs_wallet); + { + fFileBacked = pwalletMain->fFileBacked; + string strRet; + if(fFileBacked) + { + if(walletdb.EraseMultiSend(pwalletMain->vMultiSend)) + strRet += "erased MultiSend vector from database & "; + + } + pwalletMain->vMultiSend.clear(); + pwalletMain->fMultiSend = false; + strRet += "cleared MultiSend vector from RAM"; + return strRet; + } + } + else if (strCommand == "enable" || strCommand == "activate" ) + { + if(pwalletMain->vMultiSend.size() < 1) + return "Unable to activate MultiSend, check MultiSend vector"; + if(CBitcoinAddress(pwalletMain->vMultiSend[0].first).IsValid()) + { + pwalletMain->fMultiSend = true; + if(!walletdb.WriteMSettings(true, pwalletMain->nLastMultiSendHeight)) + return "MultiSend activated but writing settings to DB failed"; + else + return "MultiSend activated"; + } + else + return "Unable to activate MultiSend, check MultiSend vector"; + } + else if (strCommand == "disable" || strCommand == "deactivate" ) + { + pwalletMain->fMultiSend = false; + if(!walletdb.WriteMSettings(false, pwalletMain->nLastMultiSendHeight)) + return "MultiSend deactivated but writing settings to DB failed"; + return "MultiSend deactivated"; + } + else if(strCommand == "enableall") + { + if(!walletdb.EraseMSDisabledAddresses(pwalletMain->vDisabledAddresses)) + return "failed to clear old vector from walletDB"; + else + { + pwalletMain->vDisabledAddresses.clear(); + return "all addresses will now send MultiSend transactions"; + } + } + } + if(params.size() == 2 && params[0].get_str() == "delete") + { + int del = boost::lexical_cast(params[1].get_str()); + if(!walletdb.EraseMultiSend(pwalletMain->vMultiSend)) + return "failed to delete old MultiSend vector from database"; + + pwalletMain->vMultiSend.erase(pwalletMain->vMultiSend.begin() + del); + + if(!walletdb.WriteMultiSend(pwalletMain->vMultiSend)) + return "walletdb WriteMultiSend failed!"; + return printMultiSend(); + } + if(params.size() == 2 && params[0].get_str() == "disable") + { + std::string disAddress = params[1].get_str(); + if(!CBitcoinAddress(disAddress).IsValid()) + return "address you want to disable is not valid"; + else + { + pwalletMain->vDisabledAddresses.push_back(disAddress); + if(!walletdb.EraseMSDisabledAddresses(pwalletMain->vDisabledAddresses)) + return "disabled address from sending, but failed to clear old vector from walletDB"; + if(!walletdb.WriteMSDisabledAddresses(pwalletMain->vDisabledAddresses)) + return "disabled address from sending, but failed to store it to walletDB"; + else + return "disabled address from sending MultiSend transactions"; + } + + } + //if no commands are used + if (fHelp || params.size() != 2) + throw runtime_error( + "multisend \n" + "****************************************************************\n" + "WHAT IS MULTISEND?\n" + "MultiSend is a rebuild of what used to be called Stake For Charity (s4c)\n" + "MultiSend allows a user to automatically send a percent of their stake reward to as many addresses as you would like\n" + "The MultiSend transaction is sent when the staked coins mature (30 confirmations)\n" + "The only current restriction is that you cannot choose to send more than 100% of your stake using MultiSend\n" + "****************************************************************\n" + "MULTISEND COMMANDS (usage: multisend )\n" + " print - displays the current MultiSend vector \n" + " clear - deletes the current MultiSend vector \n" + " enable/activate - activates the current MultiSend vector \n" + " disable/deactivate - disables the current MultiSend vector \n" + " delete
- deletes an address from the MultiSend vector \n" + " disable
- prevents a specific address from sending MultiSend transactions\n" + " enableall - enables all addresses to be eligible to send MultiSend transactions\n" + + "****************************************************************\n" + "TO CREATE OR ADD TO THE MULTISEND VECTOR:\n" + "multisend \n" + "This will add a new address to the MultiSend vector\n" + "Percent is a whole number 1 to 100.\n" + "****************************************************************\n" + ); + + //if the user is entering a new MultiSend item + string strAddress = params[0].get_str(); + CBitcoinAddress address(strAddress); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid RateCoin address"); + if (boost::lexical_cast(params[1].get_str()) < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid percentage"); + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); + + + unsigned int nPercent = boost::lexical_cast(params[1].get_str()); + + LOCK(pwalletMain->cs_wallet); + { + fFileBacked = pwalletMain->fFileBacked; + //Error if 0 is entered + if(nPercent == 0) + { + return "Sending 0% of stake is not valid"; + } + + //MultiSend can only send 100% of your stake + if (nPercent + sumMultiSend() > 100) + return "Failed to add to MultiSend vector, the sum of your MultiSend is greater than 100%"; + + for(unsigned int i = 0; i < pwalletMain->vMultiSend.size(); i++) + { + if(pwalletMain->vMultiSend[i].first == strAddress) + return "Failed to add to MultiSend vector, cannot use the same address twice"; + } + + if(fFileBacked) + walletdb.EraseMultiSend(pwalletMain->vMultiSend); + + std::pair newMultiSend; + newMultiSend.first = strAddress; + newMultiSend.second = nPercent; + pwalletMain->vMultiSend.push_back(newMultiSend); + + if(fFileBacked) + { + if(!walletdb.WriteMultiSend(pwalletMain->vMultiSend)) + return "walletdb WriteMultiSend failed!"; + } + } + return printMultiSend(); } \ No newline at end of file diff --git a/src/wallet.cpp b/src/wallet.cpp index 6ac596a..74c50b2 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -2524,3 +2524,80 @@ void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) const { for (std::map::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++) mapKeyBirth[it->first] = it->second->nTime - 7200; // block times can be 2h off } + +bool CWallet::MultiSend() +{ + if ( IsInitialBlockDownload() || IsLocked() ) + return false; + int64_t nAmount = 0; + + { + LOCK(cs_wallet); + std::vector vCoins; + AvailableCoins(vCoins); + + BOOST_FOREACH(const COutput& out, vCoins) + { + CTxDestination address; + if(!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) continue; + if (nBestHeight <= nLastMultiSendHeight ) + return false; + if (out.tx->IsCoinStake() && out.tx->GetBlocksToMaturity() == 0 && out.tx->GetDepthInMainChain() == nCoinbaseMaturity+20) + { + //Disabled Addresses won't send MultiSend transactions + if(vDisabledAddresses.size() > 0) + { + for(unsigned int i = 0; i < vDisabledAddresses.size(); i++) + { + if(vDisabledAddresses[i] == CBitcoinAddress(address).ToString()) + { + return false; + } + } + } + + // create new coin control, populate it with the selected utxo, create sending vector + CCoinControl* cControl = new CCoinControl(); + uint256 txhash = out.tx->GetHash(); + COutPoint outpt(txhash, out.i); + cControl->Select(outpt); + CWalletTx wtx; + cControl->fReturnChange = true; + CReserveKey keyChange(this); // this change address does not end up being used, because change is returned with coin control switch + int64_t nFeeRet = 0; + vector > vecSend; + + // loop through multisend vector and add amounts and addresses to the sending vector + for(unsigned int i = 0; i < vMultiSend.size(); i++) + { + // MultiSend vector is a pair of 1)Address as a std::string 2) Percent of stake to send as an int + nAmount = ( ( out.tx->GetCredit() - out.tx->GetDebit() ) * vMultiSend[i].second )/100; + CBitcoinAddress strAddSend(vMultiSend[i].first); + CScript scriptPubKey; + scriptPubKey.SetDestination(strAddSend.Get()); + vecSend.push_back(make_pair(scriptPubKey, nAmount)); + } + //make sure splitblock is off + fSplitBlock = false; + + // Create the transaction and commit it to the network + bool fCreated = CreateTransaction(vecSend, wtx, keyChange, nFeeRet, 1, cControl); + if (!fCreated) + printf("MultiSend createtransaction failed"); + if(!CommitTransaction(wtx, keyChange)) + printf("MultiSend transaction commit failed"); + else + fMultiSendNotify = true; + delete cControl; + + //write nLastMultiSendHeight to DB + CWalletDB walletdb(strWalletFile); + nLastMultiSendHeight = nBestHeight; + if(!walletdb.WriteMSettings(fMultiSend, nLastMultiSendHeight)) + printf("Failed to write MultiSend setting to DB"); + + } + } + } + return true; +} diff --git a/src/wallet.h b/src/wallet.h index 82d25d0..0993624 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -96,8 +96,15 @@ class CWallet : public CCryptoKeyStore bool fSplitBlock; bool fWalletUnlockMintOnly; uint64_t nStakeSplitThreshold; - CLockedCoins lockedcoins; + + //MultiSend + std::vector > vMultiSend; + bool fMultiSend; + bool fMultiSendNotify; + std::string strMultiSendChangeAddress; + int nLastMultiSendHeight; + std::vector vDisabledAddresses; CWallet() { @@ -111,6 +118,14 @@ class CWallet : public CCryptoKeyStore lockedcoins.vLockedCoins.clear(); fWalletUnlockMintOnly = false; nStakeSplitThreshold = 10000; + + //MultiSend + vMultiSend.clear(); + fMultiSend = false; + fMultiSendNotify = false; + strMultiSendChangeAddress = ""; + nLastMultiSendHeight = 0; + vDisabledAddresses.clear(); } CWallet(std::string strWalletFileIn) { @@ -125,6 +140,14 @@ class CWallet : public CCryptoKeyStore lockedcoins.vLockedCoins.clear(); fWalletUnlockMintOnly = false; nStakeSplitThreshold = 10000; + + //MultiSend + vMultiSend.clear(); + fMultiSend = false; + fMultiSendNotify = false; + strMultiSendChangeAddress = ""; + nLastMultiSendHeight = 0; + vDisabledAddresses.clear(); } std::map mapWallet; @@ -199,6 +222,7 @@ class CWallet : public CCryptoKeyStore bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, int nSplitBlock, const CCoinControl *coinControl=NULL); bool CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); + bool MultiSend(); bool GetStakeWeight(const CKeyStore& keystore, uint64_t& nMinWeight, uint64_t& nMaxWeight, uint64_t& nWeight); bool CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64_t nSearchInterval, int64_t nFees, CTransaction& txNew, CKey& key); diff --git a/src/walletdb.cpp b/src/walletdb.cpp index cdc5d27..6fc0fdd 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -424,6 +424,30 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { ssValue >> pwallet->nStakeSplitThreshold; } + else if (strType == "multisend") //presstab HyperStake + { + unsigned int i; + ssKey >> i; + std::pair pMultiSend; + ssValue >> pMultiSend; + if(CBitcoinAddress(pMultiSend.first).IsValid()) + { + pwallet->vMultiSend.push_back(pMultiSend); + } + } + else if(strType == "msettings")//presstab HyperStake + { + std::pair pSettings; + ssValue >> pSettings; + pwallet->fMultiSend = pSettings.first; + pwallet->nLastMultiSendHeight = pSettings.second; + } + else if(strType == "mdisabled")//presstab HyperStake + { + std::string strDisabledAddress; + ssValue >> strDisabledAddress; + pwallet->vDisabledAddresses.push_back(strDisabledAddress); + } } catch (...) { return false; diff --git a/src/walletdb.h b/src/walletdb.h index 4ed40bd..7e83d32 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -220,6 +220,67 @@ class CWalletDB : public CDB nWalletDBUpdated++; return Write(std::string("stakeSplitThreshold"), nStakeSplitThreshold); } + //presstab HyperStake + bool WriteMultiSend(std::vector > vMultiSend) + { + nWalletDBUpdated++; + bool ret = true; + for(unsigned int i = 0; i < vMultiSend.size(); i++) + { + std::pair pMultiSend; + pMultiSend = vMultiSend[i]; + if(!Write(std::make_pair(std::string("multisend"), i), pMultiSend, true)) + ret = false; + } + return ret; + } + //presstab HyperStake + bool EraseMultiSend(std::vector > vMultiSend) + { + nWalletDBUpdated++; + bool ret = true; + for(unsigned int i = 0; i < vMultiSend.size(); i++) + { + std::pair pMultiSend; + pMultiSend = vMultiSend[i]; + if(!Erase(std::make_pair(std::string("multisend"), i))) + ret = false; + } + return ret; + } + //presstab HyperStake + bool WriteMSettings(bool fEnable, int nLastMultiSendHeight) + { + nWalletDBUpdated++; + std::pair pSettings; + pSettings.first = fEnable; + pSettings.second = nLastMultiSendHeight; + return Write(std::string("msettings"), pSettings, true); + } + //presstab HyperStake + bool WriteMSDisabledAddresses(std::vector vDisabledAddresses) + { + nWalletDBUpdated++; + bool ret = true; + for(unsigned int i = 0; i < vDisabledAddresses.size(); i++) + { + if(!Write(std::make_pair(std::string("mdisabled"), i), vDisabledAddresses[i])) + ret = false; + } + return ret; + } + //presstab HyperStake + bool EraseMSDisabledAddresses(std::vector vDisabledAddresses) + { + nWalletDBUpdated++; + bool ret = true; + for(unsigned int i = 0; i < vDisabledAddresses.size(); i++) + { + if(!Erase(std::make_pair(std::string("mdisabled"), i))) + ret = false; + } + return ret; + } bool ReadAccount(const std::string& strAccount, CAccount& account); bool WriteAccount(const std::string& strAccount, const CAccount& account);