Skip to content

Instantly share code, notes, and snippets.

@brandonrobertz
Created January 19, 2016 03:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brandonrobertz/6096791b98b76b025410 to your computer and use it in GitHub Desktop.
Save brandonrobertz/6096791b98b76b025410 to your computer and use it in GitHub Desktop.
Namecoin Core QT Rebase/Merge
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 96123e4..eeaabeb 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -96,6 +96,8 @@ QT_FORMS_UI = \
qt/forms/debugwindow.ui \
qt/forms/sendcoinsdialog.ui \
qt/forms/sendcoinsentry.ui \
+ qt/forms/managenamespage.ui \
+ qt/forms/configurenamedialog.ui \
qt/forms/signverifymessagedialog.ui \
qt/forms/transactiondescdialog.ui
@@ -111,12 +113,15 @@ QT_MOC_CPP = \
qt/moc_clientmodel.cpp \
qt/moc_coincontroldialog.cpp \
qt/moc_coincontroltreewidget.cpp \
+ qt/moc_configurenamedialog.cpp \
qt/moc_csvmodelwriter.cpp \
qt/moc_editaddressdialog.cpp \
qt/moc_guiutil.cpp \
qt/moc_intro.cpp \
qt/moc_macdockiconhandler.cpp \
qt/moc_macnotificationhandler.cpp \
+ qt/moc_managenamespage.cpp \
+ qt/moc_nametablemodel.cpp \
qt/moc_notificator.cpp \
qt/moc_openuridialog.cpp \
qt/moc_optionsdialog.cpp \
@@ -178,14 +183,17 @@ BITCOIN_QT_H = \
qt/coincontroldialog.h \
qt/coincontroltreewidget.h \
qt/csvmodelwriter.h \
+ qt/configurenamedialog.h \
qt/editaddressdialog.h \
qt/guiconstants.h \
qt/guiutil.h \
qt/intro.h \
qt/macdockiconhandler.h \
qt/macnotificationhandler.h \
+ qt/managenamespage.h \
qt/networkstyle.h \
qt/notificator.h \
+ qt/nametablemodel.h \
qt/openuridialog.h \
qt/optionsdialog.h \
qt/optionsmodel.h \
@@ -202,6 +210,9 @@ BITCOIN_QT_H = \
qt/rpcconsole.h \
qt/sendcoinsdialog.h \
qt/sendcoinsentry.h \
+ qt/managenamespage.h \
+ qt/configurenamedialog.h \
+ qt/nametablemodel.h \
qt/signverifymessagedialog.h \
qt/splashscreen.h \
qt/trafficgraphwidget.h \
@@ -311,6 +322,9 @@ BITCOIN_QT_CPP += \
qt/recentrequeststablemodel.cpp \
qt/sendcoinsdialog.cpp \
qt/sendcoinsentry.cpp \
+ qt/managenamespage.cpp \
+ qt/configurenamedialog.cpp \
+ qt/nametablemodel.cpp \
qt/signverifymessagedialog.cpp \
qt/transactiondesc.cpp \
qt/transactiondescdialog.cpp \
diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp
index 62aa0f0..2a08a3a 100644
--- a/src/qt/askpassphrasedialog.cpp
+++ b/src/qt/askpassphrasedialog.cpp
@@ -15,7 +15,7 @@
#include <QPushButton>
AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) :
- QDialog(parent),
+ QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint),
ui(new Ui::AskPassphraseDialog),
mode(mode),
model(0),
diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc
index c899e95..27b66ed 100644
--- a/src/qt/bitcoin.qrc
+++ b/src/qt/bitcoin.qrc
@@ -35,6 +35,7 @@
<file alias="tx_input">res/icons/tx_input.png</file>
<file alias="tx_output">res/icons/tx_output.png</file>
<file alias="tx_inout">res/icons/tx_inout.png</file>
+ <file alias="tx_nameop">res/icons/tx_nameop.png</file>
<file alias="lock_closed">res/icons/lock_closed.png</file>
<file alias="lock_open">res/icons/lock_open.png</file>
<file alias="key">res/icons/key.png</file>
diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp
index d712705..3a792ff 100644
--- a/src/qt/bitcoinaddressvalidator.cpp
+++ b/src/qt/bitcoinaddressvalidator.cpp
@@ -15,8 +15,8 @@
- All lower-case letters except for 'l'
*/
-BitcoinAddressEntryValidator::BitcoinAddressEntryValidator(QObject *parent) :
- QValidator(parent)
+BitcoinAddressEntryValidator::BitcoinAddressEntryValidator(QObject *parent, bool fAllowEmpty) :
+ QValidator(parent), allowEmpty(fAllowEmpty)
{
}
@@ -25,7 +25,7 @@ QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &po
Q_UNUSED(pos);
// Empty address is "intermediate" input
- if (input.isEmpty())
+ if (input.isEmpty() && !allowEmpty)
return QValidator::Intermediate;
// Correction
diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h
index 30d4a26..7c6ab63 100644
--- a/src/qt/bitcoinaddressvalidator.h
+++ b/src/qt/bitcoinaddressvalidator.h
@@ -15,9 +15,12 @@ class BitcoinAddressEntryValidator : public QValidator
Q_OBJECT
public:
- explicit BitcoinAddressEntryValidator(QObject *parent);
+ explicit BitcoinAddressEntryValidator(QObject *parent, bool fAllowEmpty = false);
State validate(QString &input, int &pos) const;
+
+private:
+ bool allowEmpty;
};
/** Bitcoin address widget validator, checks for a valid bitcoin address.
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 99efdf3..2789182 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -8,6 +8,7 @@
#include "clientmodel.h"
#include "guiconstants.h"
#include "guiutil.h"
+#include "managenamespage.h"
#include "networkstyle.h"
#include "notificator.h"
#include "openuridialog.h"
@@ -292,6 +293,17 @@ void BitcoinGUI::createActions()
historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4));
tabGroup->addAction(historyAction);
+ manageNamesAction = new QAction(platformStyle->SingleColorIcon(":/icons/bitcoin"), tr("&Manage Names"), this);
+ manageNamesAction->setStatusTip(tr("Manage names registered via Namecoin"));
+ manageNamesAction->setToolTip(manageNamesAction->statusTip());
+ manageNamesAction->setCheckable(true);
+ manageNamesAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_6));
+ tabGroup->addAction(manageNamesAction);
+
+ manageNamesMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/bitcoin"), manageNamesAction->text(), this);
+ manageNamesMenuAction->setStatusTip(manageNamesAction->statusTip());
+ manageNamesMenuAction->setToolTip(manageNamesMenuAction->statusTip());
+
#ifdef ENABLE_WALLET
// These showNormalIfMinimized are needed because Send Coins and Receive Coins
// can be triggered from the tray menu, and need to show the GUI to be useful.
@@ -307,6 +319,8 @@ void BitcoinGUI::createActions()
connect(receiveCoinsMenuAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage()));
connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage()));
+ connect(manageNamesAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
+ connect(manageNamesAction, SIGNAL(triggered()), this, SLOT(gotoManageNamesPage()));
#endif // ENABLE_WALLET
quitAction = new QAction(platformStyle->TextColorIcon(":/icons/quit"), tr("E&xit"), this);
@@ -436,6 +450,7 @@ void BitcoinGUI::createToolBars()
toolbar->addAction(sendCoinsAction);
toolbar->addAction(receiveCoinsAction);
toolbar->addAction(historyAction);
+ toolbar->addAction(manageNamesAction);
overviewAction->setChecked(true);
}
}
@@ -561,6 +576,7 @@ void BitcoinGUI::createTrayIconMenu()
trayIconMenu->addSeparator();
trayIconMenu->addAction(sendCoinsMenuAction);
trayIconMenu->addAction(receiveCoinsMenuAction);
+ trayIconMenu->addAction(manageNamesMenuAction);
trayIconMenu->addSeparator();
trayIconMenu->addAction(signMessageAction);
trayIconMenu->addAction(verifyMessageAction);
@@ -656,6 +672,12 @@ void BitcoinGUI::gotoSendCoinsPage(QString addr)
if (walletFrame) walletFrame->gotoSendCoinsPage(addr);
}
+void BitcoinGUI::gotoManageNamesPage()
+{
+ manageNamesAction->setChecked(true);
+ if (walletFrame) walletFrame->gotoManageNamesPage();
+}
+
void BitcoinGUI::gotoSignMessageTab(QString addr)
{
if (walletFrame) walletFrame->gotoSignMessageTab(addr);
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 871ca1b..57b8623 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -96,6 +96,8 @@ private:
QAction *sendCoinsMenuAction;
QAction *usedSendingAddressesAction;
QAction *usedReceivingAddressesAction;
+ QAction *manageNamesAction;
+ QAction *manageNamesMenuAction;
QAction *signMessageAction;
QAction *verifyMessageAction;
QAction *aboutAction;
@@ -184,6 +186,8 @@ private Q_SLOTS:
void gotoReceiveCoinsPage();
/** Switch to send coins page */
void gotoSendCoinsPage(QString addr = "");
+ /** Switch to manage names page */
+ void gotoManageNamesPage();
/** Show Sign/Verify Message dialog and switch to sign message tab */
void gotoSignMessageTab(QString addr = "");
diff --git a/src/qt/configurenamedialog.cpp b/src/qt/configurenamedialog.cpp
new file mode 100644
index 0000000..1c1b911
--- /dev/null
+++ b/src/qt/configurenamedialog.cpp
@@ -0,0 +1,150 @@
+#include "configurenamedialog.h"
+#include "ui_configurenamedialog.h"
+
+#include "guiutil.h"
+#include "addressbookpage.h"
+#include "walletmodel.h"
+#include "names/main.h"
+#include "wallet/wallet.h"
+#include "platformstyle.h"
+
+#include <QMessageBox>
+#include <QClipboard>
+
+ConfigureNameDialog::ConfigureNameDialog(const QString &_name, const QString &data, bool _firstUpdate, QWidget *parent) :
+ QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint),
+ name(_name),
+ firstUpdate(_firstUpdate),
+ ui(new Ui::ConfigureNameDialog)
+{
+ ui->setupUi(this);
+
+#ifdef Q_OS_MAC
+ ui->transferToLayout->setSpacing(4);
+#endif
+
+ GUIUtil::setupAddressWidget(ui->transferTo, this, true);
+
+ ui->labelName->setText(name);
+ ui->dataEdit->setText(data);
+
+ returnData = data;
+
+ if (name.startsWith("d/"))
+ ui->labelDomain->setText(name.mid(2) + ".bit");
+ else
+ ui->labelDomain->setText(tr("(not a domain name)"));
+
+ if (firstUpdate)
+ {
+ ui->labelTransferTo->hide();
+ ui->labelTransferToHint->hide();
+ ui->transferTo->hide();
+ ui->addressBookButton->hide();
+ ui->pasteButton->hide();
+ ui->labelSubmitHint->setText(
+ tr("Name_firstupdate transaction will be queued and broadcasted when corresponding name_new is %1 blocks old")
+ .arg(MIN_FIRSTUPDATE_DEPTH));
+ }
+ else
+ {
+ ui->labelSubmitHint->setText(tr("Name_update transaction will be issued immediately"));
+ setWindowTitle(tr("Update Name"));
+ }
+}
+
+ConfigureNameDialog::~ConfigureNameDialog()
+{
+ delete ui;
+}
+
+void ConfigureNameDialog::accept()
+{
+ if (!walletModel)
+ return;
+
+ QString addr;
+ if (!firstUpdate)
+ {
+ if (!ui->transferTo->hasAcceptableInput())
+ {
+ ui->transferTo->setValid(false);
+ return;
+ }
+
+ addr = ui->transferTo->text();
+
+ if (addr != "" && !walletModel->validateAddress(addr))
+ {
+ ui->transferTo->setValid(false);
+ return;
+ }
+ }
+
+ WalletModel::UnlockContext ctx(walletModel->requestUnlock());
+ if (!ctx.isValid())
+ return;
+
+ returnData = ui->dataEdit->text();
+ QString err_msg;
+ try
+ {
+ if (firstUpdate)
+ err_msg = walletModel->nameFirstUpdatePrepare(name, returnData);
+ else
+ err_msg = walletModel->nameUpdate(name, returnData, addr);
+ }
+ catch (std::exception& e)
+ {
+ err_msg = e.what();
+ }
+
+ if (!err_msg.isEmpty())
+ {
+ if (err_msg == "ABORTED")
+ return;
+
+ QMessageBox::critical(this, tr("Name update error"), err_msg);
+ return;
+ }
+
+ QDialog::accept();
+}
+
+void ConfigureNameDialog::reject()
+{
+ QDialog::reject();
+}
+
+void ConfigureNameDialog::setModel(WalletModel *walletModel)
+{
+ this->walletModel = walletModel;
+}
+
+void ConfigureNameDialog::on_pasteButton_clicked()
+{
+ // Paste text from clipboard into recipient field
+ ui->transferTo->setText(QApplication::clipboard()->text());
+}
+
+void ConfigureNameDialog::on_addressBookButton_clicked()
+{
+ if (!walletModel)
+ return;
+
+ // NOTE: we will need to push this into the parent class creation
+ // pretty sure this will result in blank/default options?
+ const PlatformStyle *platformStyle;
+ AddressBookPage dlg(
+ // platformStyle
+ platformStyle,
+ // mode
+ AddressBookPage::ForSelection,
+ // tab
+ AddressBookPage::SendingTab,
+ // *parent
+ this);
+ dlg.setModel(walletModel->getAddressTableModel());
+ if (dlg.exec())
+ ui->transferTo->setText(dlg.getReturnValue());
+}
diff --git a/src/qt/configurenamedialog.h b/src/qt/configurenamedialog.h
new file mode 100644
index 0000000..fcd4994
--- /dev/null
+++ b/src/qt/configurenamedialog.h
@@ -0,0 +1,40 @@
+#ifndef CONFIGURENAMEDIALOG_H
+#define CONFIGURENAMEDIALOG_H
+
+#include <QDialog>
+
+namespace Ui {
+ class ConfigureNameDialog;
+}
+
+class WalletModel;
+
+/** Dialog for editing an address and associated information.
+ */
+class ConfigureNameDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+
+ explicit ConfigureNameDialog(const QString &_name, const QString &data, bool _firstUpdate, QWidget *parent = 0);
+ ~ConfigureNameDialog();
+
+ void setModel(WalletModel *walletModel);
+ const QString &getReturnData() const { return returnData; }
+
+public Q_SLOTS:
+ void accept();
+ void reject();
+ void on_addressBookButton_clicked();
+ void on_pasteButton_clicked();
+
+private:
+ QString returnData;
+ Ui::ConfigureNameDialog *ui;
+ WalletModel *walletModel;
+ QString name;
+ bool firstUpdate;
+};
+
+#endif // CONFIGURENAMEDIALOG_H
diff --git a/src/qt/forms/configurenamedialog.ui b/src/qt/forms/configurenamedialog.ui
new file mode 100644
index 0000000..9ca282e
--- /dev/null
+++ b/src/qt/forms/configurenamedialog.ui
@@ -0,0 +1,271 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureNameDialog</class>
+ <widget class="QDialog" name="ConfigureNameDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>629</width>
+ <height>214</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Name</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+ </property>
+ <property name="labelAlignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Name:</string>
+ </property>
+ <property name="buddy">
+ <cstring>dataEdit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="labelName">
+ <property name="text">
+ <string notr="true">TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>&amp;Data:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="buddy">
+ <cstring>dataEdit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLineEdit" name="dataEdit">
+ <property name="toolTip">
+ <string>Enter JSON string that will be associated with the name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;JSON string, e.g. {&amp;quot;ns&amp;quot;: [&amp;quot;1.2.3.4&amp;quot;, &amp;quot;1.2.3.5&amp;quot;]}&lt;/p&gt;
+&lt;p style=&quot; margin:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;See &lt;a href=&quot;http://dot-bit.org/HowToRegisterAndConfigureBitDomains&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;How to Register and Configure Bit Domains&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="labelTransferTo">
+ <property name="text">
+ <string>&amp;Transfer to:</string>
+ </property>
+ <property name="buddy">
+ <cstring>transferTo</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <layout class="QHBoxLayout" name="transferToLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <widget class="QValidatedLineEdit" name="transferTo">
+ <property name="toolTip">
+ <string>The Namecoin address to transfer domain to
+(e.g. N1KHAL5C1CRzy58NdJwp1tbLze3XrkFxx9).
+Leave empty, if not needed.</string>
+ </property>
+ <property name="maxLength">
+ <number>34</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="addressBookButton">
+ <property name="toolTip">
+ <string>Choose address from address book</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset resource="../bitcoin.qrc">
+ <normaloff>:/icons/address-book</normaloff>:/icons/address-book</iconset>
+ </property>
+ <property name="shortcut">
+ <string>Alt+A</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="pasteButton">
+ <property name="toolTip">
+ <string>Paste address from clipboard</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset resource="../bitcoin.qrc">
+ <normaloff>:/icons/editpaste</normaloff>:/icons/editpaste</iconset>
+ </property>
+ <property name="shortcut">
+ <string>Alt+P</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="6" column="1">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Domain name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="labelDomain">
+ <property name="text">
+ <string notr="true">TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="8" column="1">
+ <widget class="QLabel" name="labelTransferToHint">
+ <property name="text">
+ <string>(can be left empty)</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelSubmitHint">
+ <property name="text">
+ <string notr="true">TextLabel</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QValidatedLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header>../../src/qt/qvalidatedlineedit.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="../bitcoin.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigureNameDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigureNameDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/qt/forms/managenamespage.ui b/src/qt/forms/managenamespage.ui
new file mode 100644
index 0000000..3ac54c9
--- /dev/null
+++ b/src/qt/forms/managenamespage.ui
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ManageNamesPage</class>
+ <widget class="QDialog" name="ManageNamesPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>776</width>
+ <height>364</height>
+ </rect>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+
+ <widget class="QFrame" name="frame2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+
+
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;New name:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="buddy">
+ <cstring>registerName</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QValidatedLineEdit" name="registerName">
+ <property name="toolTip">
+ <string>Enter a name or domain name (prefixed with d/) to be registered via Namecoin.</string>
+ </property>
+ <property name="text">
+ <string>d/</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use &lt;span style=&quot; font-weight:600;&quot;&gt;d/&lt;/span&gt; prefix for domain names. E.g. &lt;span style=&quot; font-weight:600;&quot;&gt;d/mysite&lt;/span&gt; will register &lt;span style=&quot; font-weight:600;&quot;&gt;mysite.bit&lt;/span&gt;&lt;/p&gt;&lt;p&gt;See &lt;a href=&quot;http://dot-bit.org/Namespace:Domain_names&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Domain names&lt;/span&gt;&lt;/a&gt; in Namecoin wiki for reference. Other prfixes can be used for miscellaneous purposes (not domain names).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="submitNameButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Confirm the new name action. Sends name_new transaction
+to the network and creates a pending name_firstupdate transaction.</string>
+ </property>
+ <property name="text">
+ <string>&amp;Submit</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../bitcoin.qrc">
+ <normaloff>:/icons/send</normaloff>:/icons/send</iconset>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Maximum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>12</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Your registered names:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="buddy">
+ <cstring>registerName</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTableView" name="tableView">
+ <property name="contextMenuPolicy">
+ <enum>Qt::CustomContextMenu</enum>
+ </property>
+ <property name="toolTip">
+ <string>Double-click name to configure</string>
+ </property>
+ <property name="tabKeyNavigation">
+ <bool>false</bool>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="sortingEnabled">
+ <bool>true</bool>
+ </property>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="configureNameButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Configure name and submit update operation</string>
+ </property>
+ <property name="text">
+ <string>&amp;Configure Name...</string>
+ </property>
+ <property name="default">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QValidatedLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header>../../src/qt/qvalidatedlineedit.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="../bitcoin.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 0f42d59..5bf12fd 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -111,7 +111,7 @@ QFont fixedPitchFont()
#endif
}
-void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
+void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent, bool fAllowEmpty /*= false*/)
{
parent->setFocusProxy(widget);
@@ -121,7 +121,7 @@ void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
// and this is the only place, where this address is supplied.
widget->setPlaceholderText(QObject::tr("Enter a Namecoin address (e.g. %1)").arg("N1KHAL5C1CRzy58NdJwp1tbLze3XrkFxx9"));
#endif
- widget->setValidator(new BitcoinAddressEntryValidator(parent));
+ widget->setValidator(new BitcoinAddressEntryValidator(parent, fAllowEmpty));
widget->setCheckValidator(new BitcoinAddressCheckValidator(parent));
}
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 9267e0a..169c3ce 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -41,7 +41,7 @@ namespace GUIUtil
QFont fixedPitchFont();
// Set up widgets for address and amounts
- void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent);
+ void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent, bool fAllowEmpty = false);
void setupAmountWidget(QLineEdit *widget, QWidget *parent);
// Parse "bitcoin:" URI into recipient object, return true on successful parsing
diff --git a/src/qt/managenamespage.cpp b/src/qt/managenamespage.cpp
new file mode 100644
index 0000000..c993293
--- /dev/null
+++ b/src/qt/managenamespage.cpp
@@ -0,0 +1,283 @@
+#include "managenamespage.h"
+#include "ui_managenamespage.h"
+
+#include "walletmodel.h"
+#include "nametablemodel.h"
+#include "csvmodelwriter.h"
+#include "guiutil.h"
+#include "base58.h"
+#include "main.h"
+#include "wallet/wallet.h"
+#include "ui_interface.h"
+#include "configurenamedialog.h"
+#include "platformstyle.h"
+
+#include <QSortFilterProxyModel>
+#include <QMessageBox>
+#include <QMenu>
+
+extern std::map<std::vector<unsigned char>, PreparedNameFirstUpdate> mapMyNameFirstUpdate;
+
+ManageNamesPage::ManageNamesPage(const PlatformStyle *platformStyle, QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::ManageNamesPage),
+ model(0),
+ walletModel(0),
+ proxyModel(0)
+{
+ ui->setupUi(this);
+
+ // Context menu actions
+ QAction *copyNameAction = new QAction(tr("Copy &Name"), this);
+ QAction *copyValueAction = new QAction(tr("Copy &Value"), this);
+ QAction *configureNameAction = new QAction(tr("&Configure Name..."), this);
+
+ // Build context menu
+ contextMenu = new QMenu();
+ contextMenu->addAction(copyNameAction);
+ contextMenu->addAction(copyValueAction);
+ contextMenu->addAction(configureNameAction);
+
+ // Connect signals for context menu actions
+ connect(copyNameAction, SIGNAL(triggered()), this, SLOT(onCopyNameAction()));
+ connect(copyValueAction, SIGNAL(triggered()), this, SLOT(onCopyValueAction()));
+ connect(configureNameAction, SIGNAL(triggered()), this, SLOT(on_configureNameButton_clicked()));
+
+ connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
+ connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_configureNameButton_clicked()));
+ ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+
+ ui->registerName->installEventFilter(this);
+ ui->tableView->installEventFilter(this);
+}
+
+ManageNamesPage::~ManageNamesPage()
+{
+ delete ui;
+}
+
+void ManageNamesPage::setModel(WalletModel *walletModel)
+{
+ this->walletModel = walletModel;
+ model = walletModel->getNameTableModel();
+
+ proxyModel = new QSortFilterProxyModel(this);
+ proxyModel->setSourceModel(model);
+ proxyModel->setDynamicSortFilter(true);
+ proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
+ proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+
+ ui->tableView->setModel(proxyModel);
+ ui->tableView->sortByColumn(0, Qt::AscendingOrder);
+
+ ui->tableView->horizontalHeader()->setHighlightSections(false);
+
+ // Set column widths
+ // TODO: DONT FORGET THIS
+ //#pragma message("QT version: " QT_VERSION_STR)
+ ui->tableView->horizontalHeader()->resizeSection(
+ NameTableModel::Name, 320);
+ // ui->tableView->horizontalHeader()->setResizeMode(
+ // NameTableModel::Value, QHeaderView::Stretch);
+ ui->tableView->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch);
+
+
+ connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
+ this, SLOT(selectionChanged()));
+
+ selectionChanged();
+}
+
+void ManageNamesPage::on_submitNameButton_clicked()
+{
+ if (!walletModel)
+ return;
+
+ QString name = ui->registerName->text();
+
+ if (!walletModel->nameAvailable(name))
+ {
+ QMessageBox::warning(this, tr("Name registration"), tr("Name not available"));
+ ui->registerName->setFocus();
+ return;
+ }
+
+ QString msg;
+ if (name.startsWith("d/"))
+ msg = tr("Are you sure you want to register domain name %1, which corresponds to domain %2?").arg(name).arg(name.mid(2) + ".bit");
+ else
+ msg = tr("Are you sure you want to register non-domain name %1?").arg(name);
+
+ if (QMessageBox::Yes != QMessageBox::question(this, tr("Confirm name registration"),
+ msg,
+ QMessageBox::Yes | QMessageBox::Cancel,
+ QMessageBox::Cancel))
+ {
+ return;
+ }
+
+ WalletModel::UnlockContext ctx(walletModel->requestUnlock());
+ if (!ctx.isValid())
+ return;
+
+ QString err_msg;
+
+ try
+ {
+ WalletModel::NameNewReturn res = walletModel->nameNew(name);
+
+ if (res.ok)
+ {
+ ui->registerName->setText("d/");
+ ui->submitNameButton->setDefault(true);
+
+ int newRowIndex;
+ // FIXME: CT_NEW may have been sent from nameNew (via transaction).
+ // Currently updateEntry is modified so it does not complain
+ model->updateEntry(name, "", NameTableEntry::NAME_NEW, CT_NEW, &newRowIndex);
+ ui->tableView->selectRow(newRowIndex);
+ ui->tableView->setFocus();
+
+ ConfigureNameDialog dlg(name, "", true, this);
+ dlg.setModel(walletModel);
+ if (dlg.exec() == QDialog::Accepted)
+ {
+ LOCK(cs_main);
+ // if (mapMyNameFirstUpdate.count(vchFromString(name.toStdString())) != 0)
+ if (mapMyNameFirstUpdate.count(ValtypeFromString(name.toStdString())) != 0)
+ model->updateEntry(name, dlg.getReturnData(), NameTableEntry::NAME_NEW, CT_UPDATED);
+ else
+ {
+ // name_firstupdate could have been sent, while the user was editing the value
+ // Do nothing
+ }
+ }
+
+ return;
+ }
+
+ err_msg = res.err_msg;
+ }
+ catch (std::exception& e)
+ {
+ err_msg = e.what();
+ }
+
+ if (err_msg == "ABORTED")
+ return;
+
+ QMessageBox::warning(this, tr("Name registration failed"), err_msg);
+}
+
+bool ManageNamesPage::eventFilter(QObject *object, QEvent *event)
+{
+ if (event->type() == QEvent::FocusIn)
+ {
+ if (object == ui->registerName)
+ {
+ ui->submitNameButton->setDefault(true);
+ ui->configureNameButton->setDefault(false);
+ }
+ else if (object == ui->tableView)
+ {
+ ui->submitNameButton->setDefault(false);
+ ui->configureNameButton->setDefault(true);
+ }
+ }
+ return QDialog::eventFilter(object, event);
+}
+
+void ManageNamesPage::selectionChanged()
+{
+ // Set button states based on selected tab and selection
+ QTableView *table = ui->tableView;
+ if(!table->selectionModel())
+ return;
+
+ if(table->selectionModel()->hasSelection())
+ {
+ ui->configureNameButton->setEnabled(true);
+ }
+ else
+ {
+ ui->configureNameButton->setEnabled(false);
+ }
+}
+
+void ManageNamesPage::contextualMenu(const QPoint &point)
+{
+ QModelIndex index = ui->tableView->indexAt(point);
+ if (index.isValid())
+ contextMenu->exec(QCursor::pos());
+}
+
+void ManageNamesPage::onCopyNameAction()
+{
+ GUIUtil::copyEntryData(ui->tableView, NameTableModel::Name);
+}
+
+void ManageNamesPage::onCopyValueAction()
+{
+ GUIUtil::copyEntryData(ui->tableView, NameTableModel::Value);
+}
+
+void ManageNamesPage::on_configureNameButton_clicked()
+{
+ if(!ui->tableView->selectionModel())
+ return;
+ QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows(NameTableModel::Name);
+ if(indexes.isEmpty())
+ return;
+
+ QModelIndex index = indexes.at(0);
+
+ QString name = index.data(Qt::EditRole).toString();
+ QString value = index.sibling(index.row(), NameTableModel::Value).data(Qt::EditRole).toString();
+
+ // std::vector<unsigned char> vchName = vchFromString(name.toStdString());
+ const valtype vchName = ValtypeFromString (name.toStdString());
+ bool fFirstUpdate = mapMyNameFirstUpdate.count(vchName) != 0;
+
+ ConfigureNameDialog dlg(name, value, fFirstUpdate, this);
+ dlg.setModel(walletModel);
+ if (dlg.exec() == QDialog::Accepted && fFirstUpdate)
+ {
+ LOCK(cs_main);
+ // name_firstupdate could have been sent, while the user was editing the value
+ if (mapMyNameFirstUpdate.count(vchName) != 0)
+ model->updateEntry(name, dlg.getReturnData(), NameTableEntry::NAME_NEW, CT_UPDATED);
+ }
+}
+
+void ManageNamesPage::exportClicked()
+{
+ // CSV is currently the only supported format
+ // QString filename = GUIUtil::getSaveFileName(
+ // this,
+ // tr("Export Registered Names Data"), QString(),
+ // tr("Comma separated file (*.csv)"));
+ QString suffixOut = "";
+ QString filename = GUIUtil::getSaveFileName(
+ this,
+ tr("Export Registered Names Data"),
+ QString(),
+ tr("Comma separated file (*.csv)"),
+ &suffixOut);
+
+ if (filename.isNull())
+ return;
+
+ CSVModelWriter writer(filename);
+
+ // name, column, role
+ writer.setModel(proxyModel);
+ writer.addColumn("Name", NameTableModel::Name, Qt::EditRole);
+ writer.addColumn("Value", NameTableModel::Value, Qt::EditRole);
+ writer.addColumn("Expires In", NameTableModel::ExpiresIn, Qt::EditRole);
+
+ if(!writer.write())
+ {
+ QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename),
+ QMessageBox::Abort, QMessageBox::Abort);
+ }
+}
diff --git a/src/qt/managenamespage.h b/src/qt/managenamespage.h
new file mode 100644
index 0000000..05179f2
--- /dev/null
+++ b/src/qt/managenamespage.h
@@ -0,0 +1,58 @@
+#ifndef MANAGENAMESPAGE_H
+#define MANAGENAMESPAGE_H
+
+#include "platformstyle.h"
+
+#include <QDialog>
+
+class WalletModel;
+class NameTableModel;
+
+namespace Ui {
+ class ManageNamesPage;
+}
+
+QT_BEGIN_NAMESPACE
+class QTableView;
+class QItemSelection;
+class QSortFilterProxyModel;
+class QMenu;
+class QModelIndex;
+QT_END_NAMESPACE
+
+/** Page for managing names */
+class ManageNamesPage : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit ManageNamesPage(const PlatformStyle *platformStyle, QWidget *parent = 0);
+ ~ManageNamesPage();
+
+ void setModel(WalletModel *walletModel);
+
+private:
+ Ui::ManageNamesPage *ui;
+ NameTableModel *model;
+ WalletModel *walletModel;
+ QSortFilterProxyModel *proxyModel;
+ QMenu *contextMenu;
+
+public Q_SLOTS:
+ void exportClicked();
+
+private Q_SLOTS:
+ void on_submitNameButton_clicked();
+
+ bool eventFilter(QObject *object, QEvent *event);
+ void selectionChanged();
+
+ /** Spawn contextual menu (right mouse menu) for name table entry */
+ void contextualMenu(const QPoint &point);
+
+ void onCopyNameAction();
+ void onCopyValueAction();
+ void on_configureNameButton_clicked();
+};
+
+#endif // MANAGENAMESPAGE_H
diff --git a/src/qt/nametablemodel.cpp b/src/qt/nametablemodel.cpp
new file mode 100644
index 0000000..72ed0e4
--- /dev/null
+++ b/src/qt/nametablemodel.cpp
@@ -0,0 +1,585 @@
+#include "nametablemodel.h"
+
+#include "guiutil.h"
+#include "walletmodel.h"
+#include "guiconstants.h"
+#include "wallet/wallet.h"
+#include "ui_interface.h"
+#include "platformstyle.h"
+
+#include "main.h"
+#include "names/common.h"
+
+#include <QTimer>
+#include <QObject>
+
+extern std::map<std::vector<unsigned char>, PreparedNameFirstUpdate> mapMyNameFirstUpdate;
+
+// ExpiresIn column is right-aligned as it contains numbers
+static int column_alignments[] = {
+ Qt::AlignLeft|Qt::AlignVCenter, // Name
+ Qt::AlignLeft|Qt::AlignVCenter, // Value
+ Qt::AlignRight|Qt::AlignVCenter // Expires in
+ };
+
+struct NameTableEntryLessThan
+{
+ bool operator()(const NameTableEntry &a, const NameTableEntry &b) const
+ {
+ return a.name < b.name;
+ }
+ bool operator()(const NameTableEntry &a, const QString &b) const
+ {
+ return a.name < b;
+ }
+ bool operator()(const QString &a, const NameTableEntry &b) const
+ {
+ return a < b.name;
+ }
+};
+
+// Returns true if new height is better
+/*static*/ bool NameTableEntry::CompareHeight(int nOldHeight, int nNewHeight)
+{
+ if (nOldHeight == NAME_NON_EXISTING)
+ return true;
+
+ // We use optimistic way, assuming that unconfirmed transaction will eventually become confirmed,
+ // so we update the name in the table immediately. Ideally we need a separate way of displaying
+ // unconfirmed names (e.g. grayed out)
+ if (nNewHeight == NAME_UNCONFIRMED)
+ return true;
+
+ // Here we rely on the fact that dummy height values are always negative
+ return nNewHeight > nOldHeight;
+}
+
+// Private implementation
+class NameTablePriv
+{
+public:
+ CWallet *wallet;
+ QList<NameTableEntry> cachedNameTable;
+ NameTableModel *parent;
+
+ NameTablePriv(CWallet *wallet, NameTableModel *parent):
+ wallet(wallet), parent(parent) {}
+
+ void refreshNameTable()
+ {
+ cachedNameTable.clear();
+
+ std::map< std::vector<unsigned char>, NameTableEntry > vNamesO;
+
+ // NOTE: this came from name_scan in rpcnames.cpp ... might want to use
+ // that call and parse the results into NameTableEntrys
+
+ {
+ LOCK(cs_main);
+
+ // TODO: replace with code from name_list
+ valtype name;
+ CNameData data;
+ std::auto_ptr<CNameIterator> iter(pcoinsTip->IterateNames ());
+ for (; iter->next (name, data); ) {
+ // res.push_back (getNameInfo (name, data));
+ vNamesO[name] = NameTableEntry(ValtypeToString(name),
+ ValtypeToString(data.getValue ()),
+ data.getHeight ());
+ }
+ }
+
+ // NOTE: There used to be a CRITICAL_BLOCK here
+ // {
+ // CTxIndex txindex;
+ // uint256 hash;
+ // CTxDB txdb("r");
+ // CTransaction tx;
+
+ // std::vector<unsigned char> vchName;
+ // std::vector<unsigned char> vchValue;
+ // int nHeight;
+
+ // BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, wallet->mapWallet)
+ // {
+ // hash = item.second.GetHash();
+ // bool fConfirmed;
+ // // TODO: Maybe CMerkleTx::GetDepthInMainChain() would be faster?
+ // if (!txdb.ReadDiskTx(hash, tx, txindex))
+ // {
+ // tx = item.second;
+ // fConfirmed = false;
+ // }
+ // else
+ // fConfirmed = true;
+
+ // if (tx.nVersion != NAMECOIN_TX_VERSION)
+ // continue;
+
+ // // name
+ // if (!GetNameOfTx(tx, vchName))
+ // continue;
+
+ // // value
+ // if (!GetValueOfNameTx(tx, vchValue))
+ // continue;
+
+ // if (!hooks->IsMine(wallet->mapWallet[tx.GetHash()]))
+ // continue; // Transferred
+
+ // // height
+ // if (fConfirmed)
+ // {
+ // nHeight = GetTxPosHeight(txindex.pos);
+ // if (nHeight + GetDisplayExpirationDepth(nHeight) - pindexBest->nHeight <= 0)
+ // continue; // Expired
+ // }
+ // else
+ // nHeight = NameTableEntry::NAME_UNCONFIRMED;
+
+ // // get last active name only
+ // std::map< std::vector<unsigned char>, NameTableEntry >::iterator mi = vNamesO.find(vchName);
+ // if (mi != vNamesO.end() && !NameTableEntry::CompareHeight(mi->second.nHeight, nHeight))
+ // continue;
+
+ // vNamesO[vchName] = NameTableEntry(ValtypeFromString(vchName), ValtypeFromString(vchValue), nHeight);
+ // }
+ // }
+
+ // Add existing names
+ BOOST_FOREACH(const PAIRTYPE(std::vector<unsigned char>, NameTableEntry)& item, vNamesO)
+ cachedNameTable.append(item.second);
+
+ // Add pending names (name_new)
+ BOOST_FOREACH(const PAIRTYPE(std::vector<unsigned char>, PreparedNameFirstUpdate)& item, mapMyNameFirstUpdate)
+ cachedNameTable.append(
+ NameTableEntry(ValtypeToString(item.first),
+ ValtypeToString(item.second.vchData),
+ NameTableEntry::NAME_NEW));
+
+ // qLowerBound() and qUpperBound() require our cachedNameTable list to be sorted in asc order
+ qSort(cachedNameTable.begin(), cachedNameTable.end(), NameTableEntryLessThan());
+ }
+
+ void refreshName(const std::vector<unsigned char> &inName)
+ {
+ LOCK(cs_main);
+
+ // NameTableEntry nameObj(ValtypeToString(inName),
+ // std::string(""),
+ // NameTableEntry::NAME_NON_EXISTING);
+ NameTableEntry nameObj;
+
+ CNameData data;
+ {
+ LOCK (cs_main);
+ if (!pcoinsTip->GetName (inName, data))
+ {
+ // std::ostringstream msg;
+ // msg << "name not found: '" << ValtypeToString (inName) << "'";
+ // throw JSONRPCError (RPC_WALLET_ERROR, msg.str ());
+ // TODO: err msg here
+ printf( "refreshName, name not found");
+ return;
+ }
+
+ nameObj = NameTableEntry(ValtypeToString(inName),
+ ValtypeToString(data.getValue ()),
+ data.getHeight ());
+ }
+
+ // CRITICAL_BLOCK(wallet->cs_mapWallet)
+ // {
+ // CTxIndex txindex;
+ // uint256 hash;
+ // CTxDB txdb("r");
+ // CTransaction tx;
+
+ // std::vector<unsigned char> vchName;
+ // std::vector<unsigned char> vchValue;
+ // int nHeight;
+
+ // BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, wallet->mapWallet)
+ // {
+ // hash = item.second.GetHash();
+ // bool fConfirmed;
+ // if (!txdb.ReadDiskTx(hash, tx, txindex))
+ // {
+ // tx = item.second;
+ // fConfirmed = false;
+ // }
+ // else
+ // fConfirmed = true;
+
+ // if (tx.nVersion != NAMECOIN_TX_VERSION)
+ // continue;
+
+ // // name
+ // if (!GetNameOfTx(tx, vchName) || vchName != inName)
+ // continue;
+
+ // // value
+ // if (!GetValueOfNameTx(tx, vchValue))
+ // {
+ // printf("refreshName(%s): skipping tx %s (GetValueOfNameTx returned false)\n", qPrintable(nameObj.name), hash.GetHex().c_str());
+ // continue;
+ // }
+
+ // if (!hooks->IsMine(wallet->mapWallet[tx.GetHash()]))
+ // {
+ // printf("refreshName(%s): skipping tx %s (transferred name)\n", qPrintable(nameObj.name), hash.GetHex().c_str());
+ // continue; // Transferred
+ // }
+
+ // // height
+ // if (fConfirmed)
+ // {
+ // nHeight = GetTxPosHeight(txindex.pos);
+ // if (nHeight + GetDisplayExpirationDepth(nHeight) - pindexBest->nHeight <= 0)
+ // {
+ // printf("refreshName(%s): tx %s, nHeight = %d - expired\n", qPrintable(nameObj.name), hash.GetHex().c_str(), nHeight);
+ // continue; // Expired
+ // }
+ // else
+ // {
+ // printf("refreshName(%s): tx %s, nHeight = %d\n", qPrintable(nameObj.name), hash.GetHex().c_str(), nHeight);
+ // }
+ // }
+ // else
+ // {
+ // printf("refreshName(%s): tx %s - unconfirmed\n", qPrintable(nameObj.name), hash.GetHex().c_str());
+ // nHeight = NameTableEntry::NAME_UNCONFIRMED;
+ // }
+
+ // // get last active name only
+ // if (!NameTableEntry::CompareHeight(nameObj.nHeight, nHeight))
+ // {
+ // printf("refreshName(%s): tx %s - skipped (more recent transaction exists)\n", qPrintable(nameObj.name), hash.GetHex().c_str());
+ // continue;
+ // }
+
+ // nameObj.value = QString::fromStdString(ValtypeFromString(vchValue));
+ // nameObj.nHeight = nHeight;
+
+ // printf("refreshName(%s) found tx %s, nHeight=%d, value: %s\n", qPrintable(nameObj.name), hash.GetHex().c_str(), nameObj.nHeight, qPrintable(nameObj.value));
+ // }
+ // }
+
+ // Find name in model
+ QList<NameTableEntry>::iterator lower = qLowerBound(
+ cachedNameTable.begin(), cachedNameTable.end(), nameObj.name, NameTableEntryLessThan());
+ QList<NameTableEntry>::iterator upper = qUpperBound(
+ cachedNameTable.begin(), cachedNameTable.end(), nameObj.name, NameTableEntryLessThan());
+ bool inModel = (lower != upper);
+
+ if (inModel)
+ {
+ // In model - update or delete
+
+ if (nameObj.nHeight != NameTableEntry::NAME_NON_EXISTING)
+ {
+ printf("refreshName result : %s - refreshed in the table\n", qPrintable(nameObj.name));
+ updateEntry(nameObj.name, nameObj.value, nameObj.nHeight, CT_UPDATED);
+ }
+ else
+ {
+ printf("refreshName result : %s - deleted from the table\n", qPrintable(nameObj.name));
+ updateEntry(nameObj.name, nameObj.value, nameObj.nHeight, CT_DELETED);
+ }
+ }
+ else
+ {
+ // Not in model - add or do nothing
+
+ if (nameObj.nHeight != NameTableEntry::NAME_NON_EXISTING)
+ {
+ printf("refreshName result : %s - added to the table\n", qPrintable(nameObj.name));
+ updateEntry(nameObj.name, nameObj.value, nameObj.nHeight, CT_NEW);
+ }
+ else
+ {
+ printf("refreshName result : %s - ignored (not in the table)\n", qPrintable(nameObj.name));
+ }
+ }
+ }
+
+ void updateEntry(const QString &name, const QString &value, int nHeight, int status, int *outNewRowIndex = NULL)
+ {
+ // Find name in model
+ QList<NameTableEntry>::iterator lower = qLowerBound(
+ cachedNameTable.begin(), cachedNameTable.end(), name, NameTableEntryLessThan());
+ QList<NameTableEntry>::iterator upper = qUpperBound(
+ cachedNameTable.begin(), cachedNameTable.end(), name, NameTableEntryLessThan());
+ int lowerIndex = (lower - cachedNameTable.begin());
+ int upperIndex = (upper - cachedNameTable.begin());
+ bool inModel = (lower != upper);
+
+ switch(status)
+ {
+ case CT_NEW:
+ if (inModel)
+ {
+ if (outNewRowIndex)
+ {
+ *outNewRowIndex = parent->index(lowerIndex, 0).row();
+ // HACK: ManageNamesPage uses this to ensure updating and get selected row,
+ // so we do not write warning into the log in this case
+ }
+ else {
+ // OutputDebugStringF("Warning: NameTablePriv::updateEntry: Got CT_NOW, but entry is already in model\n");
+ printf("Warning: NameTablePriv::updateEntry: Got CT_NOW, but entry is already in model\n");
+ }
+ break;
+ }
+ parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
+ cachedNameTable.insert(lowerIndex, NameTableEntry(name, value, nHeight));
+ parent->endInsertRows();
+ if (outNewRowIndex)
+ *outNewRowIndex = parent->index(lowerIndex, 0).row();
+ break;
+ case CT_UPDATED:
+ if (!inModel)
+ {
+ // OutputDebugStringF("Warning: NameTablePriv::updateEntry: Got CT_UPDATED, but entry is not in model\n");
+ printf("Warning: NameTablePriv::updateEntry: Got CT_UPDATED, but entry is not in model\n");
+ break;
+ }
+ lower->name = name;
+ lower->value = value;
+ lower->nHeight = nHeight;
+ parent->emitDataChanged(lowerIndex);
+ break;
+ case CT_DELETED:
+ if (!inModel)
+ {
+ // OutputDebugStringF("Warning: NameTablePriv::updateEntry: Got CT_DELETED, but entry is not in model\n");
+ printf("Warning: NameTablePriv::updateEntry: Got CT_DELETED, but entry is not in model\n");
+ break;
+ }
+ parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
+ cachedNameTable.erase(lower, upper);
+ parent->endRemoveRows();
+ break;
+ }
+ }
+
+ int size()
+ {
+ return cachedNameTable.size();
+ }
+
+ NameTableEntry *index(int idx)
+ {
+ if (idx >= 0 && idx < cachedNameTable.size())
+ {
+ return &cachedNameTable[idx];
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+};
+
+//NameTableModel::NameTableModel(CWallet *wallet, WalletModel *parent) :
+// QAbstractTableModel(parent), walletModel(parent), wallet(wallet), priv(0), cachedNumBlocks(0)
+NameTableModel::NameTableModel(const PlatformStyle *platformStyle, CWallet* wallet, WalletModel *parent):
+ QAbstractTableModel(parent),
+ wallet(wallet),
+ walletModel(parent),
+ priv(new NameTablePriv(wallet, this)),
+ platformStyle(platformStyle)
+{
+ columns << tr("Name") << tr("Value") << tr("Expires in");
+ priv = new NameTablePriv(wallet, this);
+ priv->refreshNameTable();
+
+ QTimer *timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()), this, SLOT(updateExpiration()));
+ timer->start(MODEL_UPDATE_DELAY);
+}
+
+NameTableModel::~NameTableModel()
+{
+ delete priv;
+}
+
+void NameTableModel::updateExpiration()
+{
+ int nBestHeight = chainActive.Height();
+ if (nBestHeight != cachedNumBlocks)
+ {
+ LOCK(cs_main);
+
+ cachedNumBlocks = nBestHeight;
+ // Blocks came in since last poll.
+ // Delete expired names
+ for (int i = 0, n = priv->size(); i < n; i++)
+ {
+ NameTableEntry *item = priv->index(i);
+ if (!item->HeightValid())
+ continue; // Currently, unconfirmed names do not expire in the table
+ int nHeight = item->nHeight;
+
+ // NOTE: the line below used to be: GetExpirationDepth(nHeight)
+ // I changed it to just nHeight for now
+ // int GetExpirationDepth(int nHeight) {
+ // if (nHeight < 24000)
+ // return 12000;
+ // if (nHeight < 48000)
+ // return nHeight - 12000;
+ // return 36000;
+ // }
+ if (nHeight + 36000 - nBestHeight <= 0)
+ {
+ priv->updateEntry(item->name, item->value, item->nHeight, CT_DELETED);
+ // Data array changed - restart scan
+ n = priv->size();
+ i = -1;
+ }
+ }
+ // Invalidate expiration counter for all rows.
+ // Qt is smart enough to only actually request the data for the
+ // visible rows.
+ //emit
+ dataChanged(index(0, ExpiresIn), index(priv->size()-1, ExpiresIn));
+ }
+}
+
+void NameTableModel::updateTransaction(const QString &hash, int status)
+{
+ uint256 hash256;
+ hash256.SetHex(hash.toStdString());
+
+ CTransaction tx;
+
+ {
+ LOCK(wallet->cs_wallet);
+
+ // Find transaction in wallet
+ std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash256);
+ if (mi == wallet->mapWallet.end())
+ return; // Not our transaction
+ tx = mi->second;
+ }
+
+ std::vector<unsigned char> vchName;
+ // TODO: write fn that takes tx and returns name_new, name_up, etc, constants
+ if (true /*!GetNameOfTx(tx, vchName)*/ )
+ return; // Non-name transaction
+
+ //printf("updateTransaction (%s, status=%d) calls refreshName (%s)\n", qPrintable(hash), status, vchFromString(vchName).c_str());
+ // printf(
+ // "updateTransaction (%s, status=%d) calls refreshName (%s)\n",
+ // qPrintable(hash), status, ValtypeToString(vchName)
+ // );
+
+ priv->refreshName(vchName);
+}
+
+int NameTableModel::rowCount(const QModelIndex &parent /*= QModelIndex()*/) const
+{
+ Q_UNUSED(parent);
+ return priv->size();
+}
+
+int NameTableModel::columnCount(const QModelIndex &parent /*= QModelIndex()*/) const
+{
+ Q_UNUSED(parent);
+ return columns.length();
+}
+
+QVariant NameTableModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ NameTableEntry *rec = static_cast<NameTableEntry*>(index.internalPointer());
+
+ if (role == Qt::DisplayRole || role == Qt::EditRole)
+ {
+ switch(index.column())
+ {
+ case Name:
+ return rec->name;
+ case Value:
+ return rec->value;
+ case ExpiresIn:
+ if (!rec->HeightValid()) {
+ return QVariant();
+ }
+ else {
+ int nBestHeight = chainActive.Height();
+ // OG: return rec->nHeight + GetDisplayExpirationDepth(rec->nHeight)
+ // - pindexBest->nHeight;
+ return rec->nHeight + 36000 - nBestHeight;
+ }
+ }
+ }
+ return QVariant();
+}
+
+QVariant NameTableModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal)
+ {
+ if (role == Qt::DisplayRole)
+ {
+ return columns[section];
+ }
+ else if (role == Qt::TextAlignmentRole)
+ {
+ return column_alignments[section];
+ }
+ else if (role == Qt::ToolTipRole)
+ {
+ switch(section)
+ {
+ case Name:
+ return tr("Name registered using Namecoin.");
+ case Value:
+ return tr("Data associated with the name.");
+ case ExpiresIn:
+ return tr("Number of blocks, after which the name will expire."
+ " Update name to renew it.\nEmpty cell means pending"
+ " (awaiting automatic name_firstupdate or awaiting "
+ "network confirmation).");
+ }
+ }
+ }
+ return QVariant();
+}
+
+Qt::ItemFlags NameTableModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return 0;
+ //NameTableEntry *rec = static_cast<NameTableEntry*>(index.internalPointer());
+
+ return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+}
+
+QModelIndex NameTableModel::index(int row, int column, const QModelIndex &parent /* = QModelIndex()*/) const
+{
+ Q_UNUSED(parent);
+ NameTableEntry *data = priv->index(row);
+ if (data)
+ {
+ return createIndex(row, column, priv->index(row));
+ }
+ else
+ {
+ return QModelIndex();
+ }
+}
+
+void NameTableModel::updateEntry(const QString &name, const QString &value, int nHeight, int status, int *outNewRowIndex /*= NULL*/)
+{
+ priv->updateEntry(name, value, nHeight, status, outNewRowIndex);
+}
+
+void NameTableModel::emitDataChanged(int idx)
+{
+ //emit
+ dataChanged(index(idx, 0), index(idx, columns.length()-1));
+}
diff --git a/src/qt/nametablemodel.h b/src/qt/nametablemodel.h
new file mode 100644
index 0000000..2e492e5
--- /dev/null
+++ b/src/qt/nametablemodel.h
@@ -0,0 +1,81 @@
+#ifndef NAMETABLEMODEL_H
+#define NAMETABLEMODEL_H
+
+#include "bitcoinunits.h"
+
+#include <QAbstractTableModel>
+#include <QStringList>
+
+class PlatformStyle;
+class NameTablePriv;
+class CWallet;
+class WalletModel;
+
+/**
+ Qt model for "Manage Names" page.
+ */
+class NameTableModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ explicit NameTableModel(const PlatformStyle *platformStyle, CWallet* wallet, WalletModel *parent = 0);
+ //explicit NameTableModel(CWallet *wallet, WalletModel *parent = 0);
+ ~NameTableModel();
+
+ enum ColumnIndex {
+ Name = 0,
+ Value = 1,
+ ExpiresIn = 2
+ };
+
+ /** @name Methods overridden from QAbstractTableModel
+ @{*/
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role) const;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ /*@}*/
+
+private:
+ const PlatformStyle *platformStyle;
+ CWallet *wallet;
+ WalletModel *walletModel;
+ QStringList columns;
+ NameTablePriv *priv;
+ int cachedNumBlocks;
+
+ /** Notify listeners that data changed. */
+ void emitDataChanged(int index);
+
+public Q_SLOTS:
+ void updateEntry(const QString &name, const QString &value, int nHeight, int status, int *outNewRowIndex = NULL);
+ void updateExpiration();
+ void updateTransaction(const QString &hash, int status);
+
+ friend class NameTablePriv;
+};
+
+struct NameTableEntry
+{
+ QString name;
+ QString value;
+ int nHeight;
+
+ static const int NAME_NEW = -1; // Dummy nHeight value for not-yet-created names
+ static const int NAME_NON_EXISTING = -2; // Dummy nHeight value for unitinialized entries
+ static const int NAME_UNCONFIRMED = -3; // Dummy nHeight value for unconfirmed name transactions
+
+ bool HeightValid() { return nHeight >= 0; }
+ static bool CompareHeight(int nOldHeight, int nNewHeight); // Returns true if new height is better
+
+ NameTableEntry() : nHeight(NAME_NON_EXISTING) {}
+ NameTableEntry(const QString &name, const QString &value, int nHeight):
+ name(name), value(value), nHeight(nHeight) {}
+ NameTableEntry(const std::string &name, const std::string &value, int nHeight):
+ name(QString::fromStdString(name)), value(QString::fromStdString(value)), nHeight(nHeight) {}
+};
+
+#endif // NAMETABLEMODEL_H
diff --git a/src/qt/platformstyle.h b/src/qt/platformstyle.h
index 4e763e7..e6685c3 100644
--- a/src/qt/platformstyle.h
+++ b/src/qt/platformstyle.h
@@ -52,4 +52,3 @@ private:
};
#endif // BITCOIN_QT_PLATFORMSTYLE_H
-
diff --git a/src/qt/res/icons/tx_nameop.png b/src/qt/res/icons/tx_nameop.png
new file mode 100644
index 0000000..e69de29
diff --git a/src/qt/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp
index fadaa98..4792187 100644
--- a/src/qt/transactiondescdialog.cpp
+++ b/src/qt/transactiondescdialog.cpp
@@ -10,7 +10,7 @@
#include <QModelIndex>
TransactionDescDialog::TransactionDescDialog(const QModelIndex &idx, QWidget *parent) :
- QDialog(parent),
+ QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint),
ui(new Ui::TransactionDescDialog)
{
ui->setupUi(this);
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 5b16b10..73d3bc2 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -83,9 +83,29 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
{
bool involvesWatchAddress = false;
isminetype fAllFromMe = ISMINE_SPENDABLE;
+ CAmount nCarriedOverCoin = 0;
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
{
isminetype mine = wallet->IsMine(txin);
+ if (mine != ISMINE_SPENDABLE)
+ {
+ // Check whether transaction input is name_* operation - in this case consider it ours
+ CTransaction txPrev;
+ // TODO: make sure this gets initialized to 0
+ uint256 hash;
+ CTxDestination address;
+ if (GetTransaction(txin.prevout.hash, txPrev, Params().GetConsensus(), hash) &&
+ txin.prevout.n < txPrev.vout.size() &&
+ //hooks->ExtractAddress(txPrev.vout[txin.prevout.n].scriptPubKey, address)
+ ExtractDestination(txPrev.vout[txin.prevout.n].scriptPubKey, address)
+ )
+ {
+ // This is our name transaction
+ // Accumulate the coin carried from name_new, because it is not actually spent
+ nCarriedOverCoin += txPrev.vout[txin.prevout.n].nValue;
+ mine = ISMINE_SPENDABLE;
+ }
+ }
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
if(fAllFromMe > mine) fAllFromMe = mine;
}
@@ -112,7 +132,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
//
// Debit
//
- CAmount nTxFee = nDebit - wtx.GetValueOut();
+ CAmount nTxFee = nDebit - (wtx.GetValueOut() - nCarriedOverCoin);
for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
{
@@ -128,13 +148,41 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
continue;
}
+ CAmount nValue = txout.nValue;
+
CTxDestination address;
+ CBitcoinAddress addrParsed;
+
if (ExtractDestination(txout.scriptPubKey, address))
{
// Sent to Bitcoin Address
sub.type = TransactionRecord::SendToAddress;
sub.address = CBitcoinAddress(address).ToString();
}
+ //else if (hooks->ExtractAddress(txout.scriptPubKey, address))
+ else if (ExtractDestination(txout.scriptPubKey, address) && addrParsed.Set(address))
+ {
+ sub.type = TransactionRecord::NameOp;
+ sub.address = addrParsed.ToString();
+
+ // Add carried coin (from name_new)
+ if (nCarriedOverCoin > 0)
+ {
+ // Note: we subtract nCarriedOverCoin equally from all name operations,
+ // until it becomes zero. It may fail for complex transactions, which
+ // update multiple names simultaneously (standard client never creates such transactions).
+ if (nValue >= nCarriedOverCoin)
+ {
+ nValue -= nCarriedOverCoin;
+ nCarriedOverCoin = 0;
+ }
+ else
+ {
+ nCarriedOverCoin -= nValue;
+ nValue = 0;
+ }
+ }
+ }
else
{
// Sent to IP, or other non-address transaction like OP_EVAL
@@ -142,13 +190,13 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
sub.address = mapValue["to"];
}
- CAmount nValue = txout.nValue;
/* Add fee to first output */
if (nTxFee > 0)
{
nValue += nTxFee;
nTxFee = 0;
}
+
sub.debit = -nValue;
parts.append(sub);
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index a5bc375..540e6a1 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -76,7 +76,8 @@ public:
SendToOther,
RecvWithAddress,
RecvFromOther,
- SendToSelf
+ SendToSelf,
+ NameOp,
};
/** Number of confirmation recommended for accepting a transaction */
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 1647b2a..c2b8853 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -377,6 +377,8 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
return tr("Payment to yourself");
case TransactionRecord::Generated:
return tr("Mined");
+ case TransactionRecord::NameOp:
+ return tr("Name operation");
default:
return QString();
}
@@ -394,6 +396,8 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx
case TransactionRecord::SendToAddress:
case TransactionRecord::SendToOther:
return QIcon(":/icons/tx_output");
+ case TransactionRecord::NameOp:
+ return QIcon(":/icons/tx_nameop");
default:
return QIcon(":/icons/tx_inout");
}
@@ -416,6 +420,7 @@ QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, b
case TransactionRecord::Generated:
return lookupAddress(wtx->address, tooltip) + watchAddress;
case TransactionRecord::SendToOther:
+ case TransactionRecord::NameOp:
return QString::fromStdString(wtx->address) + watchAddress;
case TransactionRecord::SendToSelf:
default:
@@ -506,7 +511,8 @@ QString TransactionTableModel::formatTooltip(const TransactionRecord *rec) const
{
QString tooltip = formatTxStatus(rec) + QString("\n") + formatTxType(rec);
if(rec->type==TransactionRecord::RecvFromOther || rec->type==TransactionRecord::SendToOther ||
- rec->type==TransactionRecord::SendToAddress || rec->type==TransactionRecord::RecvWithAddress)
+ rec->type==TransactionRecord::SendToAddress || rec->type==TransactionRecord::RecvWithAddress ||
+ rec->type==TransactionRecord::NameOp)
{
tooltip += QString(" ") + formatTxToAddress(rec, true);
}
@@ -679,7 +685,8 @@ public:
TransactionNotification(uint256 hash, ChangeType status, bool showTransaction):
hash(hash), status(status), showTransaction(showTransaction) {}
- void invoke(QObject *ttm)
+ //void invoke(QObject *ttm, QObject *ntm)
+ void invoke(TransactionTableModel *ttm) //, QObject *ntm)
{
QString strHash = QString::fromStdString(hash.GetHex());
qDebug() << "NotifyTransactionChanged: " + strHash + " status= " + QString::number(status);
@@ -687,6 +694,13 @@ public:
Q_ARG(QString, strHash),
Q_ARG(int, status),
Q_ARG(bool, showTransaction));
+ //QMetaObject::invokeMethod(ntm, "updateTransaction", Qt::QueuedConnection,
+ // Q_ARG(QString, strHash),
+ // Q_ARG(int, status),
+ // Q_ARG(bool, showTransaction));
+ // //Q_ARG(QString, strHash),
+ // //Q_ARG(int, status),
+ // //Q_ARG(bool, showTransaction));
}
private:
uint256 hash;
@@ -712,6 +726,7 @@ static void NotifyTransactionChanged(TransactionTableModel *ttm, CWallet *wallet
vQueueNotifications.push_back(notification);
return;
}
+ // TODO: notify messages
notification.invoke(ttm);
}
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 4a9a198..c257951 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -89,6 +89,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
TransactionFilterProxy::TYPE(TransactionRecord::SendToOther));
typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf));
typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated));
+ typeWidget->addItem(tr("Name operation"), TransactionFilterProxy::TYPE(TransactionRecord::NameOp));
typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other));
hlayout->addWidget(typeWidget);
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index e4ca5e1..afc75a3 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -134,6 +134,13 @@ void WalletFrame::gotoSendCoinsPage(QString addr)
i.value()->gotoSendCoinsPage(addr);
}
+void WalletFrame::gotoManageNamesPage()
+{
+ QMap<QString, WalletView*>::const_iterator i;
+ for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
+ i.value()->gotoManageNamesPage();
+}
+
void WalletFrame::gotoSignMessageTab(QString addr)
{
WalletView *walletView = currentWalletView();
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index 9a5bc27..d557350 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -59,6 +59,8 @@ public Q_SLOTS:
void gotoReceiveCoinsPage();
/** Switch to send coins page */
void gotoSendCoinsPage(QString addr = "");
+ /** Switch to manage names page */
+ void gotoManageNamesPage();
/** Show Sign/Verify Message dialog and switch to sign message tab */
void gotoSignMessageTab(QString addr = "");
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index dc6eac1..d685d0e 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -8,8 +8,9 @@
#include "guiconstants.h"
#include "guiutil.h"
#include "paymentserver.h"
-#include "recentrequeststablemodel.h"
+#include "nametablemodel.h"
#include "transactiontablemodel.h"
+#include "recentrequeststablemodel.h"
#include "base58.h"
#include "keystore.h"
@@ -27,10 +28,12 @@
#include <boost/foreach.hpp>
+std::map<std::vector<unsigned char>, PreparedNameFirstUpdate> mapMyNameFirstUpdate;
+std::map<uint160, std::vector<unsigned char> > mapMyNameHashes;
+
WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *wallet, OptionsModel *optionsModel, QObject *parent) :
QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0),
- transactionTableModel(0),
- recentRequestsTableModel(0),
+ nameTableModel(0), transactionTableModel(0), recentRequestsTableModel(0),
cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0),
cachedEncryptionStatus(Unencrypted),
cachedNumBlocks(0)
@@ -39,6 +42,7 @@ WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *wallet, Op
fForceCheckBalanceChanged = false;
addressTableModel = new AddressTableModel(wallet, this);
+ nameTableModel = new NameTableModel(platformStyle, wallet, this);
transactionTableModel = new TransactionTableModel(platformStyle, wallet, this);
recentRequestsTableModel = new RecentRequestsTableModel(wallet, this);
@@ -274,7 +278,55 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
CWalletTx *newTx = transaction.getTransaction();
CReserveKey *keyChange = transaction.getPossibleKeyChange();
- bool fCreated = wallet->CreateTransaction(vecSend, NULL, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl);
+ // TODO: figure out what sign does
+ bool sign = false;
+
+ // qt/walletmodel.cpp:277:135: error: no matching function for call to ‘
+ //CWallet::CreateTransaction(
+ // std::vector<CRecipient>&,
+ // CWalletTx&,
+ // CReserveKey&,
+ // CAmount&,
+ // int&,
+ // std::string&,
+ // const CCoinControl*&)’
+
+ //bool CreateTransaction(
+ // const std::vector<CRecipient>& vecSend,
+ // const CTxIn* withInput,
+ // CWalletTx& wtxNew,
+ // CReserveKey& reservekey,
+ // CAmount& nFeeRet,
+ // int& nChangePosRet,
+ // std::string& strFailReason,
+ // const CCoinControl *coinControl = NULL,
+ // bool sign = true);
+ // qt/walletmodel.cpp: In member function ‘WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction&, const CCoinControl*)’:
+ // qt/walletmodel.cpp:277:135: error: no matching function for call to ‘CWallet::CreateTransaction(std::vector<CRecipient>&, CWalletTx&, CReserveKey&, CAmount&, int&, std::string&, const CCoinControl*&)’
+ // bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl);
+
+
+/*
+ bool fCreated = wallet->CreateTransaction(
+ vecSend,
+ *newTx, // TODO: inputTx
+ *keyChange,
+ nFeeRequired,
+ nChangePosRet,
+ strFailReason,
+ coinControl);
+*/
+ bool fCreated = wallet->CreateTransaction(
+ vecSend,
+ NULL,
+ *newTx,
+ *keyChange,
+ nFeeRequired,
+ nChangePosRet,
+ strFailReason,
+ coinControl,
+ sign);
+
transaction.setTransactionFee(nFeeRequired);
if (fSubtractFeeFromAmount && fCreated)
transaction.reassignAmounts(nChangePosRet);
@@ -380,6 +432,11 @@ AddressTableModel *WalletModel::getAddressTableModel()
return addressTableModel;
}
+NameTableModel *WalletModel::getNameTableModel()
+{
+ return nameTableModel;
+}
+
TransactionTableModel *WalletModel::getTransactionTableModel()
{
return transactionTableModel;
@@ -668,3 +725,291 @@ bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t
else
return wallet->AddDestData(dest, key, sRequest);
}
+
+bool WalletModel::nameAvailable(const QString &name)
+{
+ const std::string strName = name.toStdString();
+
+ //std::vector<unsigned char> vchName(strName.begin(), strName.end());
+
+ CNameData data;
+ if (!pcoinsTip->GetName (ValtypeFromString(strName), data)) {
+ return false;
+ }
+
+ // TODO: check to see if we own the name? we need to look
+ // and see where this gets called form. if it's only name_new
+ // then a does exist check should be enough.
+ return true;
+
+ /*
+ std::vector<CNameIndex> vtxPos;
+ CNameDB dbName("r");
+ if (!dbName.ReadName(vchName, vtxPos))
+ return true;
+
+ if (vtxPos.size() < 1)
+ return true;
+
+ CDiskTxPos txPos = vtxPos[vtxPos.size() - 1].txPos;
+ CTransaction tx;
+ if (!tx.ReadFromDisk(txPos))
+ return true; // This may indicate error, rather than name availability
+
+ std::vector<unsigned char> vchValue;
+ int nHeight;
+ uint256 hash;
+ if (txPos.IsNull() || !GetValueOfTxPos(txPos, vchValue, hash, nHeight))
+ return true;
+
+ // TODO: should we subtract MIN_FIRSTUPDATE_DEPTH blocks? I think name_new may be possible when the previous registration is just about to expire
+ if(nHeight + GetDisplayExpirationDepth(nHeight) - pindexBest->nHeight <= 0)
+ return true; // Expired
+ */
+ return true;
+}
+
+WalletModel::NameNewReturn WalletModel::nameNew(const QString &name)
+{
+ NameNewReturn ret;
+
+ /*
+ std::string strName = name.toStdString();
+ ret.vchName = std::vector<unsigned char>(strName.begin(), strName.end());
+
+ CWalletTx wtx;
+ wtx.nVersion = NAMECOIN_TX_VERSION;
+
+ uint64 rand = GetRand((uint64)-1);
+ std::vector<unsigned char> vchRand = CBigNum(rand).getvch();
+ std::vector<unsigned char> vchToHash(vchRand);
+ vchToHash.insert(vchToHash.end(), ret.vchName.begin(), ret.vchName.end());
+ uint160 hash = Hash160(vchToHash);
+
+ std::vector<unsigned char> strPubKey = wallet->GetKeyFromKeyPool();
+ CScript scriptPubKeyOrig;
+ scriptPubKeyOrig.SetBitcoinAddress(strPubKey);
+ CScript scriptPubKey;
+ scriptPubKey << OP_NAME_NEW << hash << OP_2DROP;
+ scriptPubKey += scriptPubKeyOrig;
+
+ CRITICAL_BLOCK(cs_main)
+ {
+ std::string strError = wallet->SendMoney(scriptPubKey, MIN_AMOUNT, wtx, true);
+ if (strError != "")
+ {
+ ret.ok = false;
+ ret.err_msg = QString::fromStdString(strError);
+ return ret;
+ }
+ ret.ok = true;
+ ret.hex = wtx.GetHash();
+ ret.rand = rand;
+ ret.hash = hash;
+
+ mapMyNames[ret.vchName] = ret.hex;
+ mapMyNameHashes[ret.hash] = ret.vchName;
+ mapMyNameFirstUpdate[ret.vchName].rand = ret.rand;
+
+ strError = nameFirstUpdatePrepare(name, "").toStdString();
+ if (strError != "")
+ {
+ printf("nameFirstUpdatePrepare for %s returned error: %s\n", strName.c_str(), strError.c_str());
+ // We do not return this error, because we stored data to mapMyNameFirstUpdate and configure dialog should show up
+ }
+ }
+ */
+ return ret;
+}
+
+QString WalletModel::nameFirstUpdatePrepare(const QString &name, const QString &data)
+{
+ std::string strName = name.toStdString ();
+ //std::vector<unsigned char> vchName(strName.begin(), strName.end());
+ valtype vchName = ValtypeFromString (strName);
+ std::string strData = data.toStdString();
+ //std::vector<unsigned char> vchValue(strData.begin(), strData.end());
+ valtype vchData = ValtypeFromString (strData);
+
+ LOCK(cs_main);
+
+/*
+ //CRITICAL_BLOCK(cs_main);
+ {
+ std::map<std::vector<unsigned char>, uint256>::const_iterator it1 = mapMyNames.find(vchName);
+
+ if (it1 == mapMyNames.end())
+ return tr("Cannot find stored tx hash for name");
+ std::map<std::vector<unsigned char>, PreparedNameFirstUpdate>::iterator it2 = mapMyNameFirstUpdate.find(vchName);
+ if (it2 == mapMyNameFirstUpdate.end())
+ return tr("Cannot find stored rand value for name");
+
+ uint256 wtxInHash = it1->second;
+ uint64 rand = it2->second.rand;
+
+ CWalletTx wtx;
+ std::string err_msg = nameFirstUpdateCreateTx(wtx, vchName, wtxInHash, rand, vchValue);
+ if (err_msg != "")
+ return QString::fromStdString(err_msg);
+ it2->second.vchData = vchValue;
+ it2->second.wtx = wtx;
+
+ CRITICAL_BLOCK(wallet->cs_wallet)
+ wallet->WriteNameFirstUpdate(vchName, wtxInHash, rand, vchValue, wtx);
+ printf(
+ "Automatic name_firstupdate created for name %s, created tx: %s\n",
+ qPrintable(name), wtx.GetHash().GetHex().c_str());
+ }
+*/
+ return "";
+}
+
+void WalletModel::sendPendingNameFirstUpdates()
+{
+
+/*
+ CRITICAL_BLOCK(cs_main)
+ {
+ for (std::map<std::vector<unsigned char>, PreparedNameFirstUpdate>::iterator mi = mapMyNameFirstUpdate.begin();
+ mi != mapMyNameFirstUpdate.end(); )
+ {
+ const std::vector<unsigned char> &vchName = mi->first;
+
+ std::map<std::vector<unsigned char>, uint256>::const_iterator it1 = mapMyNames.find(vchName);
+ if (it1 == mapMyNames.end())
+ {
+ printf("Automatic name_firstupdate failed - no tx id for name %s\n", stringFromVch(vchName).c_str());
+ wallet->EraseNameFirstUpdate(vchName);
+ mapMyNameFirstUpdate.erase(mi++);
+ continue;
+ }
+ uint256 wtxInHash = it1->second;
+ bool fSkip = false;
+ CRITICAL_BLOCK(wallet->cs_wallet)
+ CRITICAL_BLOCK(wallet->cs_wallet)
+ {
+ std::map<uint256, CWalletTx>::const_iterator it2 = wallet->mapWallet.find(wtxInHash);
+ if (it2 == wallet->mapWallet.end())
+ {
+ printf("Automatic name_firstupdate failed - no wallet transaction for name %s (hash %s)\n",
+ stringFromVch(vchName).c_str(),
+ wtxInHash.GetHex().c_str());
+ wallet->EraseNameFirstUpdate(vchName);
+ mapMyNameFirstUpdate.erase(mi++);
+ fSkip = true;
+ }
+ if (it2->second.GetDepthInMainChain() < MIN_FIRSTUPDATE_DEPTH)
+ {
+ mi++;
+ fSkip = true;
+ }
+ }
+ if (fSkip)
+ continue;
+
+ printf("Sending automatic name_firstupdate for name %s\n", stringFromVch(vchName).c_str());
+
+ CWalletTx wtx = mi->second.wtx;
+
+ // Currently we reserve the key when preparing firstupdate transaction. If the user changes
+ // name configuration before broadcasting the transaction, the key is forever left unused. CReserveKey dummyKey(NULL);
+ if (!wallet->CommitTransaction(wtx, dummyKey))
+ {
+ printf("Automatic name_firstupdate failed. Name: %s, rand: %s, prevTx: %s, value: %s\n",
+ stringFromVch(vchName).c_str(),
+ HexStr(CBigNum(mi->second.rand).getvch()).c_str(),
+ wtxInHash.GetHex().c_str(),
+ stringFromVch(mi->second.vchData).c_str());
+ }
+ else
+ {
+ // Report the rand value, so the user has a chance to resubmit name_firstupdate manually (e.g. if the network forks)
+ printf("Automatic name_firstupdate done. Name: %s, rand: %s, prevTx: %s, value: %s\n",
+ stringFromVch(vchName).c_str(),
+ HexStr(CBigNum(mi->second.rand).getvch()).c_str(),
+ wtxInHash.GetHex().c_str(),
+ stringFromVch(mi->second.vchData).c_str());
+ }
+ wallet->EraseNameFirstUpdate(vchName);
+ mapMyNameFirstUpdate.erase(mi++);
+ }
+ }
+*/
+}
+
+QString WalletModel::nameUpdate(const QString &name, const QString &data, const QString &transferToAddress)
+{
+ std::string strName = name.toStdString ();
+ // std::vector<unsigned char> vchName(strName.begin(), strName.end());
+ valtype vchName = ValtypeFromString (strName);
+
+ std::string strData = data.toStdString ();
+ // std::vector<unsigned char> vchValue(strData.begin(), strData.end());
+ valtype vchData = ValtypeFromString (strData);
+
+ CWalletTx wtx;
+
+/*
+ wtx.nVersion = NAMECOIN_TX_VERSION;
+ CScript scriptPubKeyOrig;
+*/
+
+
+ /*
+ if (transferToAddress != "")
+ {
+ std::string strAddress = transferToAddress.toStdString();
+ uint160 hash160;
+ bool isValid = AddressToHash160(strAddress, hash160);
+ if (!isValid)
+ return tr("Invalid Namecoin address");
+ scriptPubKeyOrig.SetBitcoinAddress(strAddress);
+ }
+ else
+ {
+ std::vector<unsigned char> strPubKey = wallet->GetKeyFromKeyPool();
+ scriptPubKeyOrig.SetBitcoinAddress(strPubKey);
+ }
+ */
+ if( transferToAddress != "") {
+ return tr("Not Implemented");
+ } else {
+ // get key from wallet
+ // build pubkey
+ }
+
+ /*
+ CScript scriptPubKey;
+ scriptPubKey << OP_NAME_UPDATE << vchName << vchValue << OP_2DROP << OP_DROP;
+ scriptPubKey += scriptPubKeyOrig;
+
+ CRITICAL_BLOCK(cs_main)
+ CRITICAL_BLOCK(wallet->cs_mapWallet)
+ {
+ if (mapNamePending.count(vchName) && mapNamePending[vchName].size())
+ {
+ error("name_update() : there are %d pending operations on that name, including %s",
+ mapNamePending[vchName].size(),
+ mapNamePending[vchName].begin()->GetHex().c_str());
+ return tr("There are pending operations on that name");
+ }
+
+ CNameDB dbName("r");
+ CTransaction tx;
+ if (!GetTxOfName(dbName, vchName, tx))
+ return tr("Could not find a coin with this name");
+
+ uint256 wtxInHash = tx.GetHash();
+
+ if (!wallet->mapWallet.count(wtxInHash))
+ {
+ error("name_update() : this coin is not in your wallet %s",
+ wtxInHash.GetHex().c_str());
+ return tr("This coin is not in your wallet");
+ }
+
+ CWalletTx& wtxIn = wallet->mapWallet[wtxInHash];
+ return QString::fromStdString(SendMoneyWithInputTx(scriptPubKey, MIN_AMOUNT, 0, wtxIn, wtx, true));
+ }
+*/
+}
diff --git a/src/qt/walletmodel.cpp.PATCH b/src/qt/walletmodel.cpp.PATCH
new file mode 100644
index 0000000..25f7c5f
--- /dev/null
+++ b/src/qt/walletmodel.cpp.PATCH
@@ -0,0 +1,1047 @@
+// Copyright (c) 2011-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "walletmodel.h"
+
+#include "addresstablemodel.h"
+#include "guiconstants.h"
+#include "guiutil.h"
+#include "paymentserver.h"
+#include "recentrequeststablemodel.h"
+#include "nametablemodel.h"
+#include "transactiontablemodel.h"
+
+#include "base58.h"
+#include "keystore.h"
+#include "main.h"
+#include "sync.h"
+//#include "namecoin.h"
+#include "names/common.h"
+#include "ui_interface.h"
+#include "wallet/wallet.h"
+#include "wallet/walletdb.h" // for BackupWallet
+
+#include <stdint.h>
+
+#include <QDebug>
+#include <QSet>
+#include <QTimer>
+
+#include <boost/foreach.hpp>
+
+std::map<std::vector<unsigned char>, PreparedNameFirstUpdate> mapMyNameFirstUpdate;
+std::map<uint160, std::vector<unsigned char> > mapMyNameHashes;
+
+WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *wallet, OptionsModel *optionsModel, QObject *parent) :
+ QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0),
+ nameTableModel(0), transactionTableModel(0),
+ recentRequestsTableModel(0),
+ cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0),
+ cachedEncryptionStatus(Unencrypted),
+ cachedNumBlocks(0)
+{
+ fHaveWatchOnly = wallet->HaveWatchOnly();
+ fForceCheckBalanceChanged = false;
+
+ addressTableModel = new AddressTableModel(wallet, this);
+ nameTableModel = new NameTableModel(wallet, this);
+ transactionTableModel = new TransactionTableModel(platformStyle, wallet, this);
+ recentRequestsTableModel = new RecentRequestsTableModel(wallet, this);
+
+ // This timer will be fired repeatedly to update the balance
+ pollTimer = new QTimer(this);
+ connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollBalanceChanged()));
+ pollTimer->start(MODEL_UPDATE_DELAY);
+
+ subscribeToCoreSignals();
+}
+
+WalletModel::~WalletModel()
+{
+ unsubscribeFromCoreSignals();
+}
+
+CAmount WalletModel::getBalance(const CCoinControl *coinControl) const
+{
+ if (coinControl)
+ {
+ CAmount nBalance = 0;
+ std::vector<COutput> vCoins;
+ wallet->AvailableCoins(vCoins, true, coinControl);
+ BOOST_FOREACH(const COutput& out, vCoins)
+ if(out.fSpendable)
+ nBalance += out.tx->vout[out.i].nValue;
+
+ return nBalance;
+ }
+
+ return wallet->GetBalance();
+}
+
+CAmount WalletModel::getUnconfirmedBalance() const
+{
+ return wallet->GetUnconfirmedBalance();
+}
+
+CAmount WalletModel::getImmatureBalance() const
+{
+ return wallet->GetImmatureBalance();
+}
+
+bool WalletModel::haveWatchOnly() const
+{
+ return fHaveWatchOnly;
+}
+
+CAmount WalletModel::getWatchBalance() const
+{
+ return wallet->GetWatchOnlyBalance();
+}
+
+CAmount WalletModel::getWatchUnconfirmedBalance() const
+{
+ return wallet->GetUnconfirmedWatchOnlyBalance();
+}
+
+CAmount WalletModel::getWatchImmatureBalance() const
+{
+ return wallet->GetImmatureWatchOnlyBalance();
+}
+
+void WalletModel::updateStatus()
+{
+ EncryptionStatus newEncryptionStatus = getEncryptionStatus();
+
+ if(cachedEncryptionStatus != newEncryptionStatus)
+ Q_EMIT encryptionStatusChanged(newEncryptionStatus);
+}
+
+void WalletModel::pollBalanceChanged()
+{
+ // Get required locks upfront. This avoids the GUI from getting stuck on
+ // periodical polls if the core is holding the locks for a longer time -
+ // for example, during a wallet rescan.
+ TRY_LOCK(cs_main, lockMain);
+ if(!lockMain)
+ return;
+ TRY_LOCK(wallet->cs_wallet, lockWallet);
+ if(!lockWallet)
+ return;
+
+ if(fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks)
+ {
+ fForceCheckBalanceChanged = false;
+
+ // Balance and number of transactions might have changed
+ cachedNumBlocks = chainActive.Height();
+
+ checkBalanceChanged();
+ if(transactionTableModel)
+ transactionTableModel->updateConfirmations();
+
+ if (!IsInitialBlockDownload())
+ sendPendingNameFirstUpdates();
+ }
+}
+
+void WalletModel::checkBalanceChanged()
+{
+ CAmount newBalance = getBalance();
+ CAmount newUnconfirmedBalance = getUnconfirmedBalance();
+ CAmount newImmatureBalance = getImmatureBalance();
+ CAmount newWatchOnlyBalance = 0;
+ CAmount newWatchUnconfBalance = 0;
+ CAmount newWatchImmatureBalance = 0;
+ if (haveWatchOnly())
+ {
+ newWatchOnlyBalance = getWatchBalance();
+ newWatchUnconfBalance = getWatchUnconfirmedBalance();
+ newWatchImmatureBalance = getWatchImmatureBalance();
+ }
+
+ if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance ||
+ cachedWatchOnlyBalance != newWatchOnlyBalance || cachedWatchUnconfBalance != newWatchUnconfBalance || cachedWatchImmatureBalance != newWatchImmatureBalance)
+ {
+ cachedBalance = newBalance;
+ cachedUnconfirmedBalance = newUnconfirmedBalance;
+ cachedImmatureBalance = newImmatureBalance;
+ cachedWatchOnlyBalance = newWatchOnlyBalance;
+ cachedWatchUnconfBalance = newWatchUnconfBalance;
+ cachedWatchImmatureBalance = newWatchImmatureBalance;
+ Q_EMIT balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance,
+ newWatchOnlyBalance, newWatchUnconfBalance, newWatchImmatureBalance);
+ }
+}
+
+void WalletModel::sendPendingNameFirstUpdates()
+{
+ CRITICAL_BLOCK(cs_main)
+ {
+ for (std::map<std::vector<unsigned char>, PreparedNameFirstUpdate>::iterator mi = mapMyNameFirstUpdate.begin();
+ mi != mapMyNameFirstUpdate.end(); )
+ {
+ const std::vector<unsigned char> &vchName = mi->first;
+
+ std::map<std::vector<unsigned char>, uint256>::const_iterator it1 = mapMyNames.find(vchName);
+ if (it1 == mapMyNames.end())
+ {
+ printf("Automatic name_firstupdate failed - no tx id for name %s\n", stringFromVch(vchName).c_str());
+ wallet->EraseNameFirstUpdate(vchName);
+ mapMyNameFirstUpdate.erase(mi++);
+ continue;
+ }
+ uint256 wtxInHash = it1->second;
+ bool fSkip = false;
+ CRITICAL_BLOCK(wallet->cs_wallet)
+ {
+ std::map<uint256, CWalletTx>::const_iterator it2 = wallet->mapWallet.find(wtxInHash);
+ if (it2 == wallet->mapWallet.end())
+ {
+ printf("Automatic name_firstupdate failed - no wallet transaction for name %s (hash %s)\n",
+ stringFromVch(vchName).c_str(),
+ wtxInHash.GetHex().c_str());
+ wallet->EraseNameFirstUpdate(vchName);
+ mapMyNameFirstUpdate.erase(mi++);
+ fSkip = true;
+ }
+ if (it2->second.GetDepthInMainChain() < MIN_FIRSTUPDATE_DEPTH)
+ {
+ mi++;
+ fSkip = true;
+ }
+ }
+ if (fSkip)
+ continue;
+
+ printf("Sending automatic name_firstupdate for name %s\n", stringFromVch(vchName).c_str());
+
+ CWalletTx wtx = mi->second.wtx;
+
+ // Currently we reserve the key when preparing firstupdate transaction. If the user changes
+ // name configuration before broadcasting the transaction, the key is forever left unused.
+ CReserveKey dummyKey(NULL);
+
+ if (!wallet->CommitTransaction(wtx, dummyKey))
+ {
+ printf("Automatic name_firstupdate failed. Name: %s, rand: %s, prevTx: %s, value: %s\n",
+ stringFromVch(vchName).c_str(),
+ HexStr(CBigNum(mi->second.rand).getvch()).c_str(),
+ wtxInHash.GetHex().c_str(),
+ stringFromVch(mi->second.vchData).c_str());
+ }
+ else
+ {
+ // Report the rand value, so the user has a chance to resubmit name_firstupdate manually (e.g. if the network forks)
+ printf("Automatic name_firstupdate done. Name: %s, rand: %s, prevTx: %s, value: %s\n",
+ stringFromVch(vchName).c_str(),
+ HexStr(CBigNum(mi->second.rand).getvch()).c_str(),
+ wtxInHash.GetHex().c_str(),
+ stringFromVch(mi->second.vchData).c_str());
+ }
+
+ wallet->EraseNameFirstUpdate(vchName);
+ mapMyNameFirstUpdate.erase(mi++);
+ }
+ }
+}
+
+// Equivalent of name_firstupdate that does not send the transaction (the transaction is kept for 12 blocks).
+// This is needed because of wallet encryption (otherwise we could store just hash+rand+value and create transaction
+// on-the-fly after 12 blocks).
+// Must hold cs_main lock.
+std::string WalletModel::nameFirstUpdateCreateTx(CWalletTx &wtx, const std::vector<unsigned char> &vchName, uint256 wtxInHash, uint64 rand, const std::vector<unsigned char> &vchValue)
+{
+ wtx.nVersion = NAMECOIN_TX_VERSION;
+
+ if (mapNamePending.count(vchName) && mapNamePending[vchName].size())
+ {
+ error("name_firstupdate() : there are %d pending operations on that name, including %s",
+ mapNamePending[vchName].size(),
+ mapNamePending[vchName].begin()->GetHex().c_str());
+ return _("there are pending operations on that name");
+ }
+
+ {
+ CNameDB dbName("r");
+ CTransaction tx;
+ if (GetTxOfName(dbName, vchName, tx))
+ {
+ error("name_firstupdate() : this name is already active with tx %s",
+ tx.GetHash().GetHex().c_str());
+ return _("this name is already active");
+ }
+ }
+
+ if (!wallet->mapWallet.count(wtxInHash))
+ return _("previous transaction is not in the wallet");
+
+ std::vector<unsigned char> vchRand = CBigNum(rand).getvch();
+
+ std::vector<unsigned char> strPubKey = wallet->GetKeyFromKeyPool();
+ CScript scriptPubKeyOrig;
+ scriptPubKeyOrig.SetBitcoinAddress(strPubKey);
+ CScript scriptPubKey;
+ scriptPubKey << OP_NAME_FIRSTUPDATE << vchName << vchRand << vchValue << OP_2DROP << OP_2DROP;
+ scriptPubKey += scriptPubKeyOrig;
+
+ CWalletTx& wtxIn = wallet->mapWallet[wtxInHash];
+ std::vector<unsigned char> vchHash;
+ bool found = false;
+ BOOST_FOREACH(CTxOut& out, wtxIn.vout)
+ {
+ std::vector<std::vector<unsigned char> > vvch;
+ int op;
+ if (DecodeNameScript(out.scriptPubKey, op, vvch)) {
+ if (op != OP_NAME_NEW)
+ return _("previous transaction wasn't a name_new");
+ vchHash = vvch[0];
+ found = true;
+ }
+ }
+
+ if (!found)
+ return _("previous tx on this name is not a name tx");
+
+ std::vector<unsigned char> vchToHash(vchRand);
+ vchToHash.insert(vchToHash.end(), vchName.begin(), vchName.end());
+ uint160 hash = Hash160(vchToHash);
+ if (uint160(vchHash) != hash)
+ return _("previous tx used a different random value");
+
+ int64 nNetFee = GetNetworkFee(pindexBest->nHeight);
+ // Round up to CENT
+ nNetFee += CENT - 1;
+ nNetFee = (nNetFee / CENT) * CENT;
+
+ //return SendMoneyWithInputTx(scriptPubKey, MIN_AMOUNT, nNetFee, wtxIn, wtx, false);
+
+ int64 nValue = MIN_AMOUNT;
+
+ int nTxOut = IndexOfNameOutput(wtxIn);
+ CReserveKey reservekey(wallet);
+ int64 nFeeRequired;
+ std::vector< std::pair<CScript, int64> > vecSend;
+ vecSend.push_back(make_pair(scriptPubKey, nValue));
+
+ if (nNetFee)
+ {
+ CScript scriptFee;
+ scriptFee << OP_RETURN;
+ vecSend.push_back(make_pair(scriptFee, nNetFee));
+ }
+
+ if (!CreateTransactionWithInputTx(vecSend, wtxIn, nTxOut, wtx, reservekey, nFeeRequired))
+ {
+ std::string strError;
+ if (nValue + nFeeRequired > wallet->GetBalance())
+ strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds "), FormatMoney(nFeeRequired).c_str());
+ else
+ strError = _("Error: Transaction creation failed ");
+ printf("nameFirstUpdateCreateTx() : %s", strError.c_str());
+ return strError;
+ }
+
+ // Note: currently we do not notify the user about the name_firstupdate fee:
+ // - it can be confusing, since name_firstupdate can be re-configured many times
+ // - canceling the fee will leave the configured name in inconsistent state: name_new without pending name_firstupdate may result in losing the hex value (rand)
+ //if (!uiInterface.ThreadSafeAskFee(nFeeRequired))
+ // return "ABORTED";
+
+ // Take key pair from key pool so it won't be used again
+ reservekey.KeepKey();
+
+ if (!wtx.CheckTransaction())
+ return "Error: CheckTransaction failed for transaction created by nameFirstUpdateCreateTx";
+
+ return "";
+}
+
+void WalletModel::updateTransaction()
+{
+ // Balance and number of transactions might have changed
+ fForceCheckBalanceChanged = true;
+}
+
+void WalletModel::updateAddressBook(const QString &address, const QString &label,
+ bool isMine, const QString &purpose, int status)
+{
+ if(addressTableModel)
+ addressTableModel->updateEntry(address, label, isMine, purpose, status);
+}
+
+void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly)
+{
+ fHaveWatchOnly = fHaveWatchonly;
+ Q_EMIT notifyWatchonlyChanged(fHaveWatchonly);
+}
+
+bool WalletModel::validateAddress(const QString &address)
+{
+ CBitcoinAddress addressParsed(address.toStdString());
+ return addressParsed.IsValid();
+}
+
+WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl)
+{
+ CAmount total = 0;
+ bool fSubtractFeeFromAmount = false;
+ QList<SendCoinsRecipient> recipients = transaction.getRecipients();
+ std::vector<CRecipient> vecSend;
+
+ if(recipients.empty())
+ {
+ return OK;
+ }
+
+ QSet<QString> setAddress; // Used to detect duplicates
+ int nAddresses = 0;
+
+ // Pre-check input data for validity
+ Q_FOREACH(const SendCoinsRecipient &rcp, recipients)
+ {
+ if (rcp.fSubtractFeeFromAmount)
+ fSubtractFeeFromAmount = true;
+
+ if (rcp.paymentRequest.IsInitialized())
+ { // PaymentRequest...
+ CAmount subtotal = 0;
+ const payments::PaymentDetails& details = rcp.paymentRequest.getDetails();
+ for (int i = 0; i < details.outputs_size(); i++)
+ {
+ const payments::Output& out = details.outputs(i);
+ if (out.amount() <= 0) continue;
+ subtotal += out.amount();
+ const unsigned char* scriptStr = (const unsigned char*)out.script().data();
+ CScript scriptPubKey(scriptStr, scriptStr+out.script().size());
+ CAmount nAmount = out.amount();
+ CRecipient recipient = {scriptPubKey, nAmount, rcp.fSubtractFeeFromAmount};
+ vecSend.push_back(recipient);
+ }
+ if (subtotal <= 0)
+ {
+ return InvalidAmount;
+ }
+ total += subtotal;
+ }
+ else
+ { // User-entered bitcoin address / amount:
+ if(!validateAddress(rcp.address))
+ {
+ return InvalidAddress;
+ }
+ if(rcp.amount <= 0)
+ {
+ return InvalidAmount;
+ }
+ setAddress.insert(rcp.address);
+ ++nAddresses;
+
+ CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get());
+ CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount};
+ vecSend.push_back(recipient);
+
+ total += rcp.amount;
+ }
+ }
+ if(setAddress.size() != nAddresses)
+ {
+ return DuplicateAddress;
+ }
+
+ CAmount nBalance = getBalance(coinControl);
+
+ if(total > nBalance)
+ {
+ return AmountExceedsBalance;
+ }
+
+ {
+ LOCK2(cs_main, wallet->cs_wallet);
+
+ transaction.newPossibleKeyChange(wallet);
+
+ CAmount nFeeRequired = 0;
+ int nChangePosRet = -1;
+ std::string strFailReason;
+
+ CWalletTx *newTx = transaction.getTransaction();
+ CReserveKey *keyChange = transaction.getPossibleKeyChange();
+ bool fCreated = wallet->CreateTransaction(vecSend, NULL, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl);
+ transaction.setTransactionFee(nFeeRequired);
+ if (fSubtractFeeFromAmount && fCreated)
+ transaction.reassignAmounts(nChangePosRet);
+
+ if(!fCreated)
+ {
+ if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance)
+ {
+ return SendCoinsReturn(AmountWithFeeExceedsBalance);
+ }
+ Q_EMIT message(tr("Send Coins"), QString::fromStdString(strFailReason),
+ CClientUIInterface::MSG_ERROR);
+ return TransactionCreationFailed;
+ }
+
+ // reject absurdly high fee. (This can never happen because the
+ // wallet caps the fee at maxTxFee. This merely serves as a
+ // belt-and-suspenders check)
+ if (nFeeRequired > maxTxFee)
+ return AbsurdFee;
+ }
+
+ return SendCoinsReturn(OK);
+}
+
+WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction)
+{
+ QByteArray transaction_array; /* store serialized transaction */
+
+ {
+ LOCK2(cs_main, wallet->cs_wallet);
+ CWalletTx *newTx = transaction.getTransaction();
+
+ Q_FOREACH(const SendCoinsRecipient &rcp, transaction.getRecipients())
+ {
+ if (rcp.paymentRequest.IsInitialized())
+ {
+ // Make sure any payment requests involved are still valid.
+ if (PaymentServer::verifyExpired(rcp.paymentRequest.getDetails())) {
+ return PaymentRequestExpired;
+ }
+
+ // Store PaymentRequests in wtx.vOrderForm in wallet.
+ std::string key("PaymentRequest");
+ std::string value;
+ rcp.paymentRequest.SerializeToString(&value);
+ newTx->vOrderForm.push_back(make_pair(key, value));
+ }
+ else if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example)
+ newTx->vOrderForm.push_back(make_pair("Message", rcp.message.toStdString()));
+ }
+
+ CReserveKey *keyChange = transaction.getPossibleKeyChange();
+ if(!wallet->CommitTransaction(*newTx, *keyChange))
+ return TransactionCommitFailed;
+
+ CTransaction* t = (CTransaction*)newTx;
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << *t;
+ transaction_array.append(&(ssTx[0]), ssTx.size());
+ }
+
+ // Add addresses / update labels that we've sent to to the address book,
+ // and emit coinsSent signal for each recipient
+ Q_FOREACH(const SendCoinsRecipient &rcp, transaction.getRecipients())
+ {
+ // Don't touch the address book when we have a payment request
+ if (!rcp.paymentRequest.IsInitialized())
+ {
+ std::string strAddress = rcp.address.toStdString();
+ CTxDestination dest = CBitcoinAddress(strAddress).Get();
+ std::string strLabel = rcp.label.toStdString();
+ {
+ LOCK(wallet->cs_wallet);
+
+ std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest);
+
+ // Check if we have a new address or an updated label
+ if (mi == wallet->mapAddressBook.end())
+ {
+ wallet->SetAddressBook(dest, strLabel, "send");
+ }
+ else if (mi->second.name != strLabel)
+ {
+ wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose
+ }
+ }
+ }
+ Q_EMIT coinsSent(wallet, rcp, transaction_array);
+ }
+ checkBalanceChanged(); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits
+
+ return SendCoinsReturn(OK);
+}
+
+bool WalletModel::nameAvailable(const QString &name)
+{
+ std::string strName = name.toStdString();
+ std::vector<unsigned char> vchName(strName.begin(), strName.end());
+
+ std::vector<CNameIndex> vtxPos;
+ CNameDB dbName("r");
+ if (!dbName.ReadName(vchName, vtxPos))
+ return true;
+
+ if (vtxPos.size() < 1)
+ return true;
+
+ CDiskTxPos txPos = vtxPos[vtxPos.size() - 1].txPos;
+ CTransaction tx;
+ if (!tx.ReadFromDisk(txPos))
+ return true; // This may indicate error, rather than name availability
+
+ std::vector<unsigned char> vchValue;
+ int nHeight;
+ uint256 hash;
+ if (txPos.IsNull() || !GetValueOfTxPos(txPos, vchValue, hash, nHeight))
+ return true;
+
+ // TODO: should we subtract MIN_FIRSTUPDATE_DEPTH blocks? I think name_new may be possible when the previous registration is just about to expire
+ if(nHeight + GetDisplayExpirationDepth(nHeight) - pindexBest->nHeight <= 0)
+ return true; // Expired
+
+ return false;
+}
+
+WalletModel::NameNewReturn WalletModel::nameNew(const QString &name)
+{
+ NameNewReturn ret;
+
+ std::string strName = name.toStdString();
+ ret.vchName = std::vector<unsigned char>(strName.begin(), strName.end());
+
+ CWalletTx wtx;
+ wtx.nVersion = NAMECOIN_TX_VERSION;
+
+ uint64 rand = GetRand((uint64)-1);
+ std::vector<unsigned char> vchRand = CBigNum(rand).getvch();
+ std::vector<unsigned char> vchToHash(vchRand);
+ vchToHash.insert(vchToHash.end(), ret.vchName.begin(), ret.vchName.end());
+ uint160 hash = Hash160(vchToHash);
+
+ std::vector<unsigned char> strPubKey = wallet->GetKeyFromKeyPool();
+ CScript scriptPubKeyOrig;
+ scriptPubKeyOrig.SetBitcoinAddress(strPubKey);
+ CScript scriptPubKey;
+ scriptPubKey << OP_NAME_NEW << hash << OP_2DROP;
+ scriptPubKey += scriptPubKeyOrig;
+
+ CRITICAL_BLOCK(cs_main)
+ {
+ std::string strError = wallet->SendMoney(scriptPubKey, MIN_AMOUNT, wtx, true);
+ if (strError != "")
+ {
+ ret.ok = false;
+ ret.err_msg = QString::fromStdString(strError);
+ return ret;
+ }
+ ret.ok = true;
+ ret.hex = wtx.GetHash();
+ ret.rand = rand;
+ ret.hash = hash;
+
+ mapMyNames[ret.vchName] = ret.hex;
+ mapMyNameHashes[ret.hash] = ret.vchName;
+ mapMyNameFirstUpdate[ret.vchName].rand = ret.rand;
+
+ strError = nameFirstUpdatePrepare(name, "").toStdString();
+ if (strError != "")
+ {
+ printf("nameFirstUpdatePrepare for %s returned error: %s\n", strName.c_str(), strError.c_str());
+ // We do not return this error, because we stored data to mapMyNameFirstUpdate and configure dialog should show up
+ }
+ }
+ return ret;
+}
+
+QString WalletModel::nameFirstUpdatePrepare(const QString &name, const QString &data)
+{
+ std::string strName = name.toStdString();
+ std::vector<unsigned char> vchName(strName.begin(), strName.end());
+
+ std::string strData = data.toStdString();
+ std::vector<unsigned char> vchValue(strData.begin(), strData.end());
+
+ CRITICAL_BLOCK(cs_main)
+ {
+ std::map<std::vector<unsigned char>, uint256>::const_iterator it1 = mapMyNames.find(vchName);
+ if (it1 == mapMyNames.end())
+ return tr("Cannot find stored tx hash for name");
+
+ std::map<std::vector<unsigned char>, PreparedNameFirstUpdate>::iterator it2 = mapMyNameFirstUpdate.find(vchName);
+ if (it2 == mapMyNameFirstUpdate.end())
+ return tr("Cannot find stored rand value for name");
+
+ uint256 wtxInHash = it1->second;
+ uint64 rand = it2->second.rand;
+
+ CWalletTx wtx;
+ std::string err_msg = nameFirstUpdateCreateTx(wtx, vchName, wtxInHash, rand, vchValue);
+ if (err_msg != "")
+ return QString::fromStdString(err_msg);
+ it2->second.vchData = vchValue;
+ it2->second.wtx = wtx;
+
+ CRITICAL_BLOCK(wallet->cs_wallet)
+ wallet->WriteNameFirstUpdate(vchName, wtxInHash, rand, vchValue, wtx);
+ printf("Automatic name_firstupdate created for name %s, created tx: %s\n", qPrintable(name), wtx.GetHash().GetHex().c_str());
+ }
+
+ return "";
+}
+
+QString WalletModel::nameUpdate(const QString &name, const QString &data, const QString &transferToAddress)
+{
+ std::string strName = name.toStdString();
+ std::vector<unsigned char> vchName(strName.begin(), strName.end());
+
+ std::string strData = data.toStdString();
+ std::vector<unsigned char> vchValue(strData.begin(), strData.end());
+
+ CWalletTx wtx;
+ wtx.nVersion = NAMECOIN_TX_VERSION;
+ CScript scriptPubKeyOrig;
+
+ if (transferToAddress != "")
+ {
+ std::string strAddress = transferToAddress.toStdString();
+ uint160 hash160;
+ bool isValid = AddressToHash160(strAddress, hash160);
+ if (!isValid)
+ return tr("Invalid Namecoin address");
+ scriptPubKeyOrig.SetBitcoinAddress(strAddress);
+ }
+ else
+ {
+ std::vector<unsigned char> strPubKey = wallet->GetKeyFromKeyPool();
+ scriptPubKeyOrig.SetBitcoinAddress(strPubKey);
+ }
+
+ CScript scriptPubKey;
+ scriptPubKey << OP_NAME_UPDATE << vchName << vchValue << OP_2DROP << OP_DROP;
+ scriptPubKey += scriptPubKeyOrig;
+
+ CRITICAL_BLOCK(cs_main)
+ CRITICAL_BLOCK(wallet->cs_mapWallet)
+ {
+ if (mapNamePending.count(vchName) && mapNamePending[vchName].size())
+ {
+ error("name_update() : there are %d pending operations on that name, including %s",
+ mapNamePending[vchName].size(),
+ mapNamePending[vchName].begin()->GetHex().c_str());
+ return tr("There are pending operations on that name");
+ }
+
+ CNameDB dbName("r");
+ CTransaction tx;
+ if (!GetTxOfName(dbName, vchName, tx))
+ return tr("Could not find a coin with this name");
+
+ uint256 wtxInHash = tx.GetHash();
+
+ if (!wallet->mapWallet.count(wtxInHash))
+ {
+ error("name_update() : this coin is not in your wallet %s",
+ wtxInHash.GetHex().c_str());
+ return tr("This coin is not in your wallet");
+ }
+
+ CWalletTx& wtxIn = wallet->mapWallet[wtxInHash];
+ return QString::fromStdString(SendMoneyWithInputTx(scriptPubKey, MIN_AMOUNT, 0, wtxIn, wtx, true));
+ }
+}
+
+OptionsModel *WalletModel::getOptionsModel()
+{
+ return optionsModel;
+}
+
+AddressTableModel *WalletModel::getAddressTableModel()
+{
+ return addressTableModel;
+}
+
+NameTableModel *WalletModel::getNameTableModel()
+{
+ return nameTableModel;
+}
+
+TransactionTableModel *WalletModel::getTransactionTableModel()
+{
+ return transactionTableModel;
+}
+
+RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel()
+{
+ return recentRequestsTableModel;
+}
+
+WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const
+{
+ if(!wallet->IsCrypted())
+ {
+ return Unencrypted;
+ }
+ else if(wallet->IsLocked())
+ {
+ return Locked;
+ }
+ else
+ {
+ return Unlocked;
+ }
+}
+
+bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase)
+{
+ if(encrypted)
+ {
+ // Encrypt
+ return wallet->EncryptWallet(passphrase);
+ }
+ else
+ {
+ // Decrypt -- TODO; not supported yet
+ return false;
+ }
+}
+
+bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase)
+{
+ if(locked)
+ {
+ // Lock
+ return wallet->Lock();
+ }
+ else
+ {
+ // Unlock
+ return wallet->Unlock(passPhrase);
+ }
+}
+
+bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass)
+{
+ bool retval;
+ {
+ LOCK(wallet->cs_wallet);
+ wallet->Lock(); // Make sure wallet is locked before attempting pass change
+ retval = wallet->ChangeWalletPassphrase(oldPass, newPass);
+ }
+ return retval;
+}
+
+bool WalletModel::backupWallet(const QString &filename)
+{
+ return BackupWallet(*wallet, filename.toLocal8Bit().data());
+}
+
+// Handlers for core signals
+static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStore *wallet)
+{
+ qDebug() << "NotifyKeyStoreStatusChanged";
+ QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection);
+}
+
+static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
+ const CTxDestination &address, const std::string &label, bool isMine,
+ const std::string &purpose, ChangeType status)
+{
+ QString strAddress = QString::fromStdString(CBitcoinAddress(address).ToString());
+ QString strLabel = QString::fromStdString(label);
+ QString strPurpose = QString::fromStdString(purpose);
+
+ qDebug() << "NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status);
+ QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
+ Q_ARG(QString, strAddress),
+ Q_ARG(QString, strLabel),
+ Q_ARG(bool, isMine),
+ Q_ARG(QString, strPurpose),
+ Q_ARG(int, status));
+}
+
+static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status)
+{
+ Q_UNUSED(wallet);
+ Q_UNUSED(hash);
+ Q_UNUSED(status);
+ QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection);
+}
+
+static void ShowProgress(WalletModel *walletmodel, const std::string &title, int nProgress)
+{
+ // emits signal "showProgress"
+ QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection,
+ Q_ARG(QString, QString::fromStdString(title)),
+ Q_ARG(int, nProgress));
+}
+
+static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly)
+{
+ QMetaObject::invokeMethod(walletmodel, "updateWatchOnlyFlag", Qt::QueuedConnection,
+ Q_ARG(bool, fHaveWatchonly));
+}
+
+void WalletModel::subscribeToCoreSignals()
+{
+ // Connect signals to wallet
+ wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
+ wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6));
+ wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
+ wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
+ wallet->NotifyWatchonlyChanged.connect(boost::bind(NotifyWatchonlyChanged, this, _1));
+}
+
+void WalletModel::unsubscribeFromCoreSignals()
+{
+ // Disconnect signals from wallet
+ wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
+ wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6));
+ wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
+ wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
+ wallet->NotifyWatchonlyChanged.disconnect(boost::bind(NotifyWatchonlyChanged, this, _1));
+}
+
+// WalletModel::UnlockContext implementation
+WalletModel::UnlockContext WalletModel::requestUnlock()
+{
+ bool was_locked = getEncryptionStatus() == Locked;
+ if(was_locked)
+ {
+ // Request UI to unlock wallet
+ Q_EMIT requireUnlock();
+ }
+ // If wallet is still locked, unlock was failed or cancelled, mark context as invalid
+ bool valid = getEncryptionStatus() != Locked;
+
+ return UnlockContext(this, valid, was_locked);
+}
+
+WalletModel::UnlockContext::UnlockContext(WalletModel *wallet, bool valid, bool relock):
+ wallet(wallet),
+ valid(valid),
+ relock(relock)
+{
+}
+
+WalletModel::UnlockContext::~UnlockContext()
+{
+ if(valid && relock)
+ {
+ wallet->setWalletLocked(true);
+ }
+}
+
+void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs)
+{
+ // Transfer context; old object no longer relocks wallet
+ *this = rhs;
+ rhs.relock = false;
+}
+
+bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
+{
+ return wallet->GetPubKey(address, vchPubKeyOut);
+}
+
+bool WalletModel::havePrivKey(const CKeyID &address) const
+{
+ return wallet->HaveKey(address);
+}
+
+// returns a list of COutputs from COutPoints
+void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs)
+{
+ LOCK2(cs_main, wallet->cs_wallet);
+ BOOST_FOREACH(const COutPoint& outpoint, vOutpoints)
+ {
+ if (!wallet->mapWallet.count(outpoint.hash)) continue;
+ int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
+ if (nDepth < 0) continue;
+ COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true);
+ vOutputs.push_back(out);
+ }
+}
+
+bool WalletModel::isSpent(const COutPoint& outpoint) const
+{
+ LOCK2(cs_main, wallet->cs_wallet);
+ return wallet->IsSpent(outpoint.hash, outpoint.n);
+}
+
+// AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address)
+void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const
+{
+ std::vector<COutput> vCoins;
+ wallet->AvailableCoins(vCoins);
+
+ LOCK2(cs_main, wallet->cs_wallet); // ListLockedCoins, mapWallet
+ std::vector<COutPoint> vLockedCoins;
+ wallet->ListLockedCoins(vLockedCoins);
+
+ // add locked coins
+ BOOST_FOREACH(const COutPoint& outpoint, vLockedCoins)
+ {
+ if (!wallet->mapWallet.count(outpoint.hash)) continue;
+ int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
+ if (nDepth < 0) continue;
+ COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true);
+ if (outpoint.n < out.tx->vout.size() && wallet->IsMine(out.tx->vout[outpoint.n]) == ISMINE_SPENDABLE)
+ vCoins.push_back(out);
+ }
+
+ BOOST_FOREACH(const COutput& out, vCoins)
+ {
+ COutput cout = out;
+
+ while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0]))
+ {
+ if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break;
+ cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true);
+ }
+
+ CTxDestination address;
+ if(!out.fSpendable || !ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address))
+ continue;
+ mapCoins[QString::fromStdString(CBitcoinAddress(address).ToString())].push_back(out);
+ }
+}
+
+bool WalletModel::isLockedCoin(uint256 hash, unsigned int n) const
+{
+ LOCK2(cs_main, wallet->cs_wallet);
+ return wallet->IsLockedCoin(hash, n);
+}
+
+void WalletModel::lockCoin(COutPoint& output)
+{
+ LOCK2(cs_main, wallet->cs_wallet);
+ wallet->LockCoin(output);
+}
+
+void WalletModel::unlockCoin(COutPoint& output)
+{
+ LOCK2(cs_main, wallet->cs_wallet);
+ wallet->UnlockCoin(output);
+}
+
+void WalletModel::listLockedCoins(std::vector<COutPoint>& vOutpts)
+{
+ LOCK2(cs_main, wallet->cs_wallet);
+ wallet->ListLockedCoins(vOutpts);
+}
+
+void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests)
+{
+ LOCK(wallet->cs_wallet);
+ BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook)
+ BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item2, item.second.destdata)
+ if (item2.first.size() > 2 && item2.first.substr(0,2) == "rr") // receive request
+ vReceiveRequests.push_back(item2.second);
+}
+
+bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest)
+{
+ CTxDestination dest = CBitcoinAddress(sAddress).Get();
+
+ std::stringstream ss;
+ ss << nId;
+ std::string key = "rr" + ss.str(); // "rr" prefix = "receive request" in destdata
+
+ LOCK(wallet->cs_wallet);
+ if (sRequest.empty())
+ return wallet->EraseDestData(dest, key);
+ else
+ return wallet->AddDestData(dest, key, sRequest);
+}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 7a47eda..9011fab 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -19,6 +19,7 @@ class AddressTableModel;
class OptionsModel;
class PlatformStyle;
class RecentRequestsTableModel;
+class NameTableModel;
class TransactionTableModel;
class WalletModelTransaction;
@@ -127,6 +128,7 @@ public:
OptionsModel *getOptionsModel();
AddressTableModel *getAddressTableModel();
+ NameTableModel *getNameTableModel();
TransactionTableModel *getTransactionTableModel();
RecentRequestsTableModel *getRecentRequestsTableModel();
@@ -200,6 +202,35 @@ public:
void loadReceiveRequests(std::vector<std::string>& vReceiveRequests);
bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest);
+ struct NameNewReturn
+ {
+ bool ok;
+ QString err_msg;
+ QString address;
+ std::vector<unsigned char> vchName;
+ uint256 hex; // Transaction hash in hex
+ uint64_t rand; // Secret number in hex
+ uint160 hash; // Hash of rand+name
+ };
+
+ bool nameAvailable(const QString &name);
+
+ // Register new name
+ // Requires unlocked wallet; can throw exception instead of returning error
+ NameNewReturn nameNew(const QString &name);
+
+ // Create pending name update
+ // Requires unlocked wallet; can throw exception instead of returning error
+ QString nameFirstUpdatePrepare(const QString &name, const QString &data);
+
+ // Send pending name updates, if they are 12 blocks old
+ void sendPendingNameFirstUpdates();
+
+ // Update name
+ // Requires unlocked wallet; can throw exception instead of returning error
+ QString nameUpdate(const QString &name, const QString &data, const QString &transferToAddress);
+
+
private:
CWallet *wallet;
bool fHaveWatchOnly;
@@ -211,6 +242,7 @@ private:
AddressTableModel *addressTableModel;
TransactionTableModel *transactionTableModel;
+ NameTableModel *nameTableModel;
RecentRequestsTableModel *recentRequestsTableModel;
// Cache some values to be able to detect changes
@@ -229,6 +261,7 @@ private:
void unsubscribeFromCoreSignals();
void checkBalanceChanged();
+
Q_SIGNALS:
// Signal that balance in wallet changed
void balanceChanged(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance,
@@ -265,6 +298,8 @@ public Q_SLOTS:
void updateWatchOnlyFlag(bool fHaveWatchonly);
/* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */
void pollBalanceChanged();
+
+
};
#endif // BITCOIN_QT_WALLETMODEL_H
diff --git a/src/qt/walletmodel.h.PATCH b/src/qt/walletmodel.h.PATCH
new file mode 100644
index 0000000..f206dd2
--- /dev/null
+++ b/src/qt/walletmodel.h.PATCH
@@ -0,0 +1,303 @@
+// Copyright (c) 2011-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_WALLETMODEL_H
+#define BITCOIN_QT_WALLETMODEL_H
+
+#include "paymentrequestplus.h"
+#include "walletmodeltransaction.h"
+
+#include "support/allocators/secure.h"
+
+#include <map>
+#include <vector>
+
+#include <QObject>
+
+class AddressTableModel;
+class OptionsModel;
+class PlatformStyle;
+class RecentRequestsTableModel;
+class NameTableModel;
+class TransactionTableModel;
+class WalletModelTransaction;
+
+class CCoinControl;
+class CKeyID;
+class COutPoint;
+class COutput;
+class CPubKey;
+class CWallet;
+class uint256;
+class CWalletTx;
+
+QT_BEGIN_NAMESPACE
+class QTimer;
+QT_END_NAMESPACE
+
+class SendCoinsRecipient
+{
+public:
+ explicit SendCoinsRecipient() : amount(0), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) { }
+ explicit SendCoinsRecipient(const QString &addr, const QString &label, const CAmount& amount, const QString &message):
+ address(addr), label(label), amount(amount), message(message), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) {}
+
+ // If from an unauthenticated payment request, this is used for storing
+ // the addresses, e.g. address-A<br />address-B<br />address-C.
+ // Info: As we don't need to process addresses in here when using
+ // payment requests, we can abuse it for displaying an address list.
+ // Todo: This is a hack, should be replaced with a cleaner solution!
+ QString address;
+ QString label;
+ CAmount amount;
+ // If from a payment request, this is used for storing the memo
+ QString message;
+
+ // If from a payment request, paymentRequest.IsInitialized() will be true
+ PaymentRequestPlus paymentRequest;
+ // Empty if no authentication or invalid signature/cert/etc.
+ QString authenticatedMerchant;
+
+ bool fSubtractFeeFromAmount; // memory only
+
+ static const int CURRENT_VERSION = 1;
+ int nVersion;
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ std::string sAddress = address.toStdString();
+ std::string sLabel = label.toStdString();
+ std::string sMessage = message.toStdString();
+ std::string sPaymentRequest;
+ if (!ser_action.ForRead() && paymentRequest.IsInitialized())
+ paymentRequest.SerializeToString(&sPaymentRequest);
+ std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString();
+
+ READWRITE(this->nVersion);
+ nVersion = this->nVersion;
+ READWRITE(sAddress);
+ READWRITE(sLabel);
+ READWRITE(amount);
+ READWRITE(sMessage);
+ READWRITE(sPaymentRequest);
+ READWRITE(sAuthenticatedMerchant);
+
+ if (ser_action.ForRead())
+ {
+ address = QString::fromStdString(sAddress);
+ label = QString::fromStdString(sLabel);
+ message = QString::fromStdString(sMessage);
+ if (!sPaymentRequest.empty())
+ paymentRequest.parse(QByteArray::fromRawData(sPaymentRequest.data(), sPaymentRequest.size()));
+ authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant);
+ }
+ }
+};
+
+/** Interface to Bitcoin wallet from Qt view code. */
+class WalletModel : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit WalletModel(const PlatformStyle *platformStyle, CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0);
+ ~WalletModel();
+
+ enum StatusCode // Returned by sendCoins
+ {
+ OK,
+ InvalidAmount,
+ InvalidAddress,
+ AmountExceedsBalance,
+ AmountWithFeeExceedsBalance,
+ DuplicateAddress,
+ TransactionCreationFailed, // Error returned when wallet is still locked
+ TransactionCommitFailed,
+ AbsurdFee,
+ PaymentRequestExpired
+ };
+
+ enum EncryptionStatus
+ {
+ Unencrypted, // !wallet->IsCrypted()
+ Locked, // wallet->IsCrypted() && wallet->IsLocked()
+ Unlocked // wallet->IsCrypted() && !wallet->IsLocked()
+ };
+
+ OptionsModel *getOptionsModel();
+ AddressTableModel *getAddressTableModel();
+ NameTableModel *getNameTableModel();
+ TransactionTableModel *getTransactionTableModel();
+ RecentRequestsTableModel *getRecentRequestsTableModel();
+
+ CAmount getBalance(const CCoinControl *coinControl = NULL) const;
+ CAmount getUnconfirmedBalance() const;
+ CAmount getImmatureBalance() const;
+ bool haveWatchOnly() const;
+ CAmount getWatchBalance() const;
+ CAmount getWatchUnconfirmedBalance() const;
+ CAmount getWatchImmatureBalance() const;
+ EncryptionStatus getEncryptionStatus() const;
+
+ // Check address for validity
+ bool validateAddress(const QString &address);
+
+ // Return status record for SendCoins, contains error id + information
+ struct SendCoinsReturn
+ {
+ SendCoinsReturn(StatusCode status = OK):
+ status(status) {}
+ StatusCode status;
+ };
+
+ // prepare transaction for getting txfee before sending coins
+ SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl = NULL);
+
+ // Send coins to a list of recipients
+ SendCoinsReturn sendCoins(WalletModelTransaction &transaction);
+
+ bool nameAvailable(const QString &name);
+
+ struct NameNewReturn
+ {
+ bool ok;
+ QString err_msg;
+ std::vector<unsigned char> vchName;
+ uint256 hex; // Transaction hash in hex
+ uint64_t rand; // Secret number in hex
+ uint160 hash; // Hash of rand+name
+ };
+
+ // Register new name
+ // Requires unlocked wallet; can throw exception instead of returning error
+ NameNewReturn nameNew(const QString &name);
+
+ // Create pending name update
+ // Requires unlocked wallet; can throw exception instead of returning error
+ QString nameFirstUpdatePrepare(const QString &name, const QString &data);
+
+ // Send pending name updates, if they are 12 blocks old
+ void sendPendingNameFirstUpdates();
+
+ // Update name
+ // Requires unlocked wallet; can throw exception instead of returning error
+ QString nameUpdate(const QString &name, const QString &data, const QString &transferToAddress);
+
+ // Wallet encryption
+ bool setWalletEncrypted(bool encrypted, const SecureString &passphrase);
+ // Passphrase only needed when unlocking
+ bool setWalletLocked(bool locked, const SecureString &passPhrase=SecureString());
+ bool changePassphrase(const SecureString &oldPass, const SecureString &newPass);
+ // Wallet backup
+ bool backupWallet(const QString &filename);
+
+ // RAI object for unlocking wallet, returned by requestUnlock()
+ class UnlockContext
+ {
+ public:
+ UnlockContext(WalletModel *wallet, bool valid, bool relock);
+ ~UnlockContext();
+
+ bool isValid() const { return valid; }
+
+ // Copy operator and constructor transfer the context
+ UnlockContext(const UnlockContext& obj) { CopyFrom(obj); }
+ UnlockContext& operator=(const UnlockContext& rhs) { CopyFrom(rhs); return *this; }
+ private:
+ WalletModel *wallet;
+ bool valid;
+ mutable bool relock; // mutable, as it can be set to false by copying
+
+ void CopyFrom(const UnlockContext& rhs);
+ };
+
+ UnlockContext requestUnlock();
+
+ bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const;
+ bool havePrivKey(const CKeyID &address) const;
+ void getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs);
+ bool isSpent(const COutPoint& outpoint) const;
+ void listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const;
+
+ bool isLockedCoin(uint256 hash, unsigned int n) const;
+ void lockCoin(COutPoint& output);
+ void unlockCoin(COutPoint& output);
+ void listLockedCoins(std::vector<COutPoint>& vOutpts);
+
+ void loadReceiveRequests(std::vector<std::string>& vReceiveRequests);
+ bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest);
+
+private:
+ CWallet *wallet;
+ bool fHaveWatchOnly;
+ bool fForceCheckBalanceChanged;
+
+ // Wallet has an options model for wallet-specific options
+ // (transaction fee, for example)
+ OptionsModel *optionsModel;
+
+ AddressTableModel *addressTableModel;
+ NameTableModel *nameTableModel;
+ TransactionTableModel *transactionTableModel;
+ RecentRequestsTableModel *recentRequestsTableModel;
+
+ // Cache some values to be able to detect changes
+ CAmount cachedBalance;
+ CAmount cachedUnconfirmedBalance;
+ CAmount cachedImmatureBalance;
+ CAmount cachedWatchOnlyBalance;
+ CAmount cachedWatchUnconfBalance;
+ CAmount cachedWatchImmatureBalance;
+ EncryptionStatus cachedEncryptionStatus;
+ int cachedNumBlocks;
+
+ QTimer *pollTimer;
+
+ void subscribeToCoreSignals();
+ void unsubscribeFromCoreSignals();
+ void checkBalanceChanged();
+
+ std::string nameFirstUpdateCreateTx(CWalletTx &wtx, const std::vector<unsigned char> &vchName, uint256 wtxInHash, uint64_t rand, const std::vector<unsigned char> &vchValue);
+
+Q_SIGNALS:
+ // Signal that balance in wallet changed
+ void balanceChanged(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance,
+ const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance);
+
+ // Encryption status of wallet changed
+ void encryptionStatusChanged(int status);
+
+ // Signal emitted when wallet needs to be unlocked
+ // It is valid behaviour for listeners to keep the wallet locked after this signal;
+ // this means that the unlocking failed or was cancelled.
+ void requireUnlock();
+
+ // Fired when a message should be reported to the user
+ void message(const QString &title, const QString &message, unsigned int style);
+
+ // Coins sent: from wallet, to recipient, in (serialized) transaction:
+ void coinsSent(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction);
+
+ // Show progress dialog e.g. for rescan
+ void showProgress(const QString &title, int nProgress);
+
+ // Watch-only address added
+ void notifyWatchonlyChanged(bool fHaveWatchonly);
+
+public Q_SLOTS:
+ /* Wallet status might have changed */
+ void updateStatus();
+ /* New transaction, or transaction changed status */
+ void updateTransaction();
+ /* New, updated or removed address book entry */
+ void updateAddressBook(const QString &address, const QString &label, bool isMine, const QString &purpose, int status);
+ /* Watch-only added */
+ void updateWatchOnlyFlag(bool fHaveWatchonly);
+ /* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */
+ void pollBalanceChanged();
+};
+
+#endif // BITCOIN_QT_WALLETMODEL_H
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 6ce98ef..a636bc9 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -18,6 +18,7 @@
#include "transactiontablemodel.h"
#include "transactionview.h"
#include "walletmodel.h"
+#include "managenamespage.h"
#include "ui_interface.h"
@@ -55,6 +56,7 @@ WalletView::WalletView(const PlatformStyle *platformStyle, QWidget *parent):
receiveCoinsPage = new ReceiveCoinsDialog(platformStyle);
sendCoinsPage = new SendCoinsDialog(platformStyle);
+ manageNamesPage = new ManageNamesPage(platformStyle);
usedSendingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::SendingTab, this);
usedReceivingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this);
@@ -63,6 +65,7 @@ WalletView::WalletView(const PlatformStyle *platformStyle, QWidget *parent):
addWidget(transactionsPage);
addWidget(receiveCoinsPage);
addWidget(sendCoinsPage);
+ addWidget(manageNamesPage);
// Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex)));
@@ -118,6 +121,7 @@ void WalletView::setWalletModel(WalletModel *walletModel)
overviewPage->setWalletModel(walletModel);
receiveCoinsPage->setModel(walletModel);
sendCoinsPage->setModel(walletModel);
+ manageNamesPage->setModel(walletModel);
usedReceivingAddressesPage->setModel(walletModel->getAddressTableModel());
usedSendingAddressesPage->setModel(walletModel->getAddressTableModel());
@@ -185,6 +189,17 @@ void WalletView::gotoSendCoinsPage(QString addr)
sendCoinsPage->setAddress(addr);
}
+void WalletView::gotoManageNamesPage()
+{
+ setCurrentWidget(manageNamesPage);
+
+/* FIXME: Move to tab itself; see 45155d3010a3bbbe3cfbba670538ae18b9772a39
+ exportAction->setEnabled(true);
+ disconnect(exportAction, SIGNAL(triggered()), 0, 0);
+ connect(exportAction, SIGNAL(triggered()), manageNamesPage, SLOT(exportClicked()));
+*/
+}
+
void WalletView::gotoSignMessageTab(QString addr)
{
// calls show() in showTab_SM()
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index dbb289f..b65f0ca 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -16,6 +16,7 @@ class PlatformStyle;
class ReceiveCoinsDialog;
class SendCoinsDialog;
class SendCoinsRecipient;
+class ManageNamesPage;
class TransactionView;
class WalletModel;
class AddressBookPage;
@@ -62,6 +63,7 @@ private:
QWidget *transactionsPage;
ReceiveCoinsDialog *receiveCoinsPage;
SendCoinsDialog *sendCoinsPage;
+ ManageNamesPage *manageNamesPage;
AddressBookPage *usedSendingAddressesPage;
AddressBookPage *usedReceivingAddressesPage;
@@ -85,6 +87,10 @@ public Q_SLOTS:
/** Show Sign/Verify Message dialog and switch to verify message tab */
void gotoVerifyMessageTab(QString addr = "");
+ /* NMC SHIT */
+ void gotoManageNamesPage();
+
+
/** Show incoming transaction notification for new transactions.
The new items are those between start and end inclusive, under the given parent item.
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 2c5b00a..939787c 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2489,6 +2489,32 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString());
}
+//#ifdef GUI
+bool CWallet::WriteNameFirstUpdate(const std::vector<unsigned char>& vchName,
+ const uint256& hex,
+ const uint64_t& rand,
+ const std::vector<unsigned char>& vchData,
+ const CWalletTx &wtx)
+{
+ /*
+ if (!fFileBacked)
+ return false;
+ return CWalletDB(strWalletFile).WriteNameFirstUpdate(vchName, hex, rand, vchData, wtx);
+ */
+ return true;
+}
+
+bool CWallet::EraseNameFirstUpdate(const std::vector<unsigned char>& vchName)
+{
+ /*
+ if (!fFileBacked)
+ return false;
+ return CWalletDB(strWalletFile).EraseNameFirstUpdate(vchName);
+ */
+ return true;
+}
+//#endif
+
bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
{
if (fFileBacked)
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index f697cd1..c58d29f 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -748,6 +748,15 @@ public:
bool DelAddressBook(const CTxDestination& address);
+//#ifdef GUI
+ bool WriteNameFirstUpdate(const std::vector<unsigned char>& vchName,
+ const uint256& hex,
+ const uint64_t& rand,
+ const std::vector<unsigned char>& vchData,
+ const CWalletTx &wtx);
+ bool EraseNameFirstUpdate(const std::vector<unsigned char>& vchName);
+//#endif
+
void UpdatedTransaction(const uint256 &hashTx);
void Inventory(const uint256 &hash)
@@ -879,4 +888,14 @@ public:
}
};
+//#ifdef GUI
+// Editable transaction, which is not broadcasted immediately (only after 12 blocks)
+struct PreparedNameFirstUpdate
+{
+ uint64_t rand;
+ std::vector<unsigned char> vchData;
+ CWalletTx wtx;
+};
+//#endif
+
#endif // BITCOIN_WALLET_WALLET_H
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 6751197..1c8151b 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -21,6 +21,11 @@
#include <boost/scoped_ptr.hpp>
#include <boost/thread.hpp>
+//#ifdef GUI
+extern std::map<std::vector<unsigned char>, PreparedNameFirstUpdate> mapMyNameFirstUpdate;
+extern std::map<uint160, std::vector<unsigned char> > mapMyNameHashes; // Name for name_new hash (to show name in transaction list)
+//#endif
+
using namespace std;
static uint64_t nAccountingEntryNumber = 0;
@@ -176,6 +181,33 @@ bool CWalletDB::WriteMinVersion(int nVersion)
return Write(std::string("minversion"), nVersion);
}
+//#ifdef GUI
+bool CWalletDB::WriteNameFirstUpdate(const std::vector<unsigned char>& vchName,
+ const uint256& hex,
+ const uint64_t& rand,
+ const std::vector<unsigned char>& vchData,
+ const CWalletTx &wtx)
+{
+ /*
+ CDataStream ssValue;
+ ssValue << hex << rand << vchData << wtx;
+
+ nWalletDBUpdated++;
+ return Write(make_pair(string("name_firstupdate"), vchName), ssValue, true);
+ */
+ return true;
+}
+
+bool CWalletDB::EraseNameFirstUpdate(const std::vector<unsigned char>& vchName)
+{
+ /*
+ nWalletDBUpdated++;
+ return Erase(make_pair(string("name_firstupdate"), vchName));
+ */
+ return true;
+}
+//#endif
+
bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
{
account.SetNull();
@@ -401,6 +433,23 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
wss.fAnyUnordered = true;
pwallet->AddToWallet(wtx, true, NULL);
+
+//#ifdef GUI
+/* TODO: implement this
+ int op, nOut;
+ std::vector<std::vector<unsigned char> > vvch;
+ if (DecodeNameTx(wtx, op, nOut, vvch) && op == OP_NAME_FIRSTUPDATE && vvch.size() == 3)
+ {
+ std::vector<unsigned char> &vchName = vvch[0];
+ std::vector<unsigned char> &vchRand = vvch[1];
+
+ std::vector<unsigned char> vchToHash(vchRand);
+ vchToHash.insert(vchToHash.end(), vchName.begin(), vchName.end());
+ uint160 hash = Hash160(vchToHash);
+ mapMyNameHashes[hash] = vchName;
+ }
+ */
+//#endif
}
else if (strType == "acentry")
{
@@ -565,6 +614,37 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
if (pwallet->mapKeyMetadata.count(keyid) == 0)
pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
}
+//#ifdef GUI
+ else if (strType == "name_firstupdate")
+ {
+ std::vector<unsigned char> vchName;
+ uint256 wtxInHash;
+
+ ssKey >> vchName;
+ PreparedNameFirstUpdate prep;
+ // Note: name, rand and data are stored unencrypted. Even if we encrypt them,
+ // they are recoverable from prep.wtx, which has to be unencrypted (so it can be
+ // auto-broadcasted, when name_new is 12 blocks old)
+ ssValue >> wtxInHash >> prep.rand >> prep.vchData >> prep.wtx;
+
+ // TODO: look into the prep wtc structure and get the assignment right
+ //prep.wtx.pwallet = pwallet;
+
+ // TODO: also would be good to check that name, rand, wtxInHash and value match with prep.wtx
+ // Note: wtxInHash IS NOT prep.wtx.GetHash(), it is the hash of previous name_new
+
+ // TODO: reimpliment this hashmap
+ //mapMyNames[vchName] = wtxInHash;
+ //mapMyNameFirstUpdate[vchName] = prep;
+
+ // TODO: CBigNum doesn't exist
+ //std::vector<unsigned char> vchRand = CBigNum(prep.rand).getvch();
+ //std::vector<unsigned char> vchToHash(vchRand);
+ //vchToHash.insert(vchToHash.end(), vchName.begin(), vchName.end());
+ //uint160 hash = Hash160(vchToHash);
+ //mapMyNameHashes[hash] = vchName;
+ }
+//#endif
else if (strType == "version")
{
ssValue >> wss.nFileVersion;
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 8da33de..c475861 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -126,6 +126,16 @@ public:
CAmount GetAccountCreditDebit(const std::string& strAccount);
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& acentries);
+//#ifdef GUI
+ bool WriteNameFirstUpdate(const std::vector<unsigned char>& vchName,
+ const uint256& hex,
+ const uint64_t& rand,
+ const std::vector<unsigned char>& vchData,
+ const CWalletTx &wtx);
+ bool EraseNameFirstUpdate(const std::vector<unsigned char>& vchName);
+//#endif
+
+
DBErrors ReorderTransactions(CWallet* pwallet);
DBErrors LoadWallet(CWallet* pwallet);
DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment