From aaa1c3c4001d4931299d674ef4146d8201dae634 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 7 May 2011 22:13:39 +0200 Subject: initial commit --- .gitignore | 2 + BitcoinGUI.cpp | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++ BitcoinGUI.h | 23 +++++++++ TODO | 50 ++++++++++++++++++++ address-book.png | Bin 0 -> 656 bytes bitcoin.cpp | 17 +++++++ bitcoin.png | Bin 0 -> 1086 bytes bitcoin.pro | 12 +++++ moc_BitcoinGUI.cpp | 79 +++++++++++++++++++++++++++++++ quit.png | Bin 0 -> 876 bytes send.png | Bin 0 -> 946 bytes 11 files changed, 316 insertions(+) create mode 100644 .gitignore create mode 100644 BitcoinGUI.cpp create mode 100644 BitcoinGUI.h create mode 100644 TODO create mode 100644 address-book.png create mode 100644 bitcoin.cpp create mode 100644 bitcoin.png create mode 100644 bitcoin.pro create mode 100644 moc_BitcoinGUI.cpp create mode 100644 quit.png create mode 100644 send.png diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..d6ff91a9ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*~ +*.o diff --git a/BitcoinGUI.cpp b/BitcoinGUI.cpp new file mode 100644 index 0000000000..b18a18b88d --- /dev/null +++ b/BitcoinGUI.cpp @@ -0,0 +1,133 @@ +/* + * W.J. van der Laan 2011 + */ +#include "BitcoinGUI.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +BitcoinGUI::BitcoinGUI(QWidget *parent): + QMainWindow(parent) +{ + resize(850, 550); + setWindowTitle("Bitcoin"); + setWindowIcon(QIcon("bitcoin.png")); + + + QAction *quit = new QAction(QIcon("quit.png"), "&Quit", this); + QAction *sendcoins = new QAction(QIcon("send.png"), "&Send coins", this); + QAction *addressbook = new QAction(QIcon("address-book.png"), "&Address book", this); + QAction *about = new QAction(QIcon("bitcoin.png"), "&About", this); + + /* Menus */ + QMenu *file = menuBar()->addMenu("&File"); + file->addAction(sendcoins); + file->addSeparator(); + file->addAction(quit); + + QMenu *settings = menuBar()->addMenu("&Settings"); + settings->addAction(addressbook); + + QMenu *help = menuBar()->addMenu("&Help"); + help->addAction(about); + + /* Toolbar */ + QToolBar *toolbar = addToolBar("Main toolbar"); + toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolbar->addAction(sendcoins); + toolbar->addAction(addressbook); + + /* Address:
: New... : Paste to clipboard */ + QHBoxLayout *hbox_address = new QHBoxLayout(); + hbox_address->addWidget(new QLabel("Your Bitcoin Address:")); + QLineEdit *edit_address = new QLineEdit(); + edit_address->setReadOnly(true); + hbox_address->addWidget(edit_address); + + QPushButton *button_new = new QPushButton(trUtf8("&New\u2026")); + QPushButton *button_clipboard = new QPushButton("&Copy to clipboard"); + hbox_address->addWidget(button_new); + hbox_address->addWidget(button_clipboard); + + /* Balance: */ + QHBoxLayout *hbox_balance = new QHBoxLayout(); + hbox_balance->addWidget(new QLabel("Balance:")); + hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ + QLabel *label_balance = new QLabel("1,234.54"); + label_balance->setFont(QFont("Teletype")); + hbox_balance->addWidget(label_balance); + hbox_balance->addStretch(1); + + /* Tab widget */ + QVBoxLayout *vbox = new QVBoxLayout(); + vbox->addLayout(hbox_address); + vbox->addLayout(hbox_balance); + + /* Transaction table: + * TransactionView + * TransactionModel + * Selection behaviour + * selection mode + * QAbstractItemView::SelectItems + * QAbstractItemView::ExtendedSelection + */ + QTableView *transaction_table = new QTableView(this); + + QTabBar *tabs = new QTabBar(this); + tabs->addTab("All transactions"); + tabs->addTab("Sent/Received"); + tabs->addTab("Sent"); + tabs->addTab("Received"); + + vbox->addWidget(tabs); + vbox->addWidget(transaction_table); + + QWidget *centralwidget = new QWidget(this); + centralwidget->setLayout(vbox); + setCentralWidget(centralwidget); + + /* Status bar */ + statusBar(); + + QLabel *label_connections = new QLabel("6 connections", this); + label_connections->setFrameStyle(QFrame::Panel | QFrame::Sunken); + label_connections->setMinimumWidth(100); + + QLabel *label_blocks = new QLabel("6 blocks", this); + label_blocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); + label_blocks->setMinimumWidth(100); + + QLabel *label_transactions = new QLabel("6 transactions", this); + label_transactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); + label_transactions->setMinimumWidth(100); + + + statusBar()->addPermanentWidget(label_connections); + statusBar()->addPermanentWidget(label_blocks); + statusBar()->addPermanentWidget(label_transactions); + + + /* Action bindings */ + + connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(tabs, SIGNAL(currentChanged(int)), this, SLOT(currentChanged(int))); +} + +void BitcoinGUI::currentChanged(int tab) +{ + std::cout << "Switched to tab: " << tab << std::endl; +} diff --git a/BitcoinGUI.h b/BitcoinGUI.h new file mode 100644 index 0000000000..e4ff2fe61a --- /dev/null +++ b/BitcoinGUI.h @@ -0,0 +1,23 @@ +#ifndef H_BITCOINGUI +#define H_BITCOINGUI + +#include + +class BitcoinGUI : public QMainWindow +{ + Q_OBJECT +public: + BitcoinGUI(QWidget *parent = 0); + + /* Transaction table tab indices */ + enum { + ALL_TRANSACTIONS = 0, + SENT_RECEIVED = 1, + SENT = 2, + RECEIVED = 3 + } TabIndex; +private slots: + void currentChanged(int tab); +}; + +#endif diff --git a/TODO b/TODO new file mode 100644 index 0000000000..934b231b7f --- /dev/null +++ b/TODO @@ -0,0 +1,50 @@ +Toolbar: + Send coins + Address book + +- "Your bitcoin address" label +- address field +- "New..." +- Copy to Clipboard + +Balance: XXX + +Tabs: + All transactions + Sent/Received + Sent + Received + +Table [columns]: + Status + Date + Description + Debit + Credit + + ** Table should be the same in all tabs. Do we really need different widgets? + +Status bar: + Permanent status indicators: + < actions_crystal_project: connect_established.png / connect_no.png > + N connections + M blocks + O transactions + +SendCoinDialog +AddressesDialog (Address book) + Receiving/Sending + +OptionsDialog + Tabs at the left +AboutDialog + + +- Move resources to res/ + + - Send icon + + - Address Book icon + + +- Translation diff --git a/address-book.png b/address-book.png new file mode 100644 index 0000000000..621ca40245 Binary files /dev/null and b/address-book.png differ diff --git a/bitcoin.cpp b/bitcoin.cpp new file mode 100644 index 0000000000..d43befd41d --- /dev/null +++ b/bitcoin.cpp @@ -0,0 +1,17 @@ +/* + * W.J. van der Laan 2011 + */ +#include "BitcoinGUI.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + BitcoinGUI window; + + window.show(); + + return app.exec(); +} diff --git a/bitcoin.png b/bitcoin.png new file mode 100644 index 0000000000..09520aca23 Binary files /dev/null and b/bitcoin.png differ diff --git a/bitcoin.pro b/bitcoin.pro new file mode 100644 index 0000000000..ab440fab85 --- /dev/null +++ b/bitcoin.pro @@ -0,0 +1,12 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Sat May 7 20:45:42 2011 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += BitcoinGUI.h +SOURCES += bitcoin.cpp BitcoinGUI.cpp diff --git a/moc_BitcoinGUI.cpp b/moc_BitcoinGUI.cpp new file mode 100644 index 0000000000..3c024d3178 --- /dev/null +++ b/moc_BitcoinGUI.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'BitcoinGUI.h' +** +** Created: Sat May 7 20:43:39 2011 +** by: The Qt Meta Object Compiler version 62 (Qt 4.7.0) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "BitcoinGUI.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'BitcoinGUI.h' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 62 +#error "This file was generated using the moc from 4.7.0. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_BitcoinGUI[] = { + + // content: + 5, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 16, 12, 11, 11, 0x08, + + 0 // eod +}; + +static const char qt_meta_stringdata_BitcoinGUI[] = { + "BitcoinGUI\0\0tab\0currentChanged(int)\0" +}; + +const QMetaObject BitcoinGUI::staticMetaObject = { + { &QMainWindow::staticMetaObject, qt_meta_stringdata_BitcoinGUI, + qt_meta_data_BitcoinGUI, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &BitcoinGUI::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *BitcoinGUI::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *BitcoinGUI::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_BitcoinGUI)) + return static_cast(const_cast< BitcoinGUI*>(this)); + return QMainWindow::qt_metacast(_clname); +} + +int BitcoinGUI::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QMainWindow::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: currentChanged((*reinterpret_cast< int(*)>(_a[1]))); break; + default: ; + } + _id -= 1; + } + return _id; +} +QT_END_MOC_NAMESPACE diff --git a/quit.png b/quit.png new file mode 100644 index 0000000000..fb510fbea6 Binary files /dev/null and b/quit.png differ diff --git a/send.png b/send.png new file mode 100644 index 0000000000..52b939e8e6 Binary files /dev/null and b/send.png differ -- cgit v1.2.3 From f79405b5e12909d703a0badd0fcbc380f0a66ab9 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 7 May 2011 22:18:24 +0200 Subject: restore orig send image --- send.png | Bin 946 -> 938 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/send.png b/send.png index 52b939e8e6..51067a192b 100644 Binary files a/send.png and b/send.png differ -- cgit v1.2.3 From 4d27c960336f1cbf7c3bc741bb389826e628859b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 8 May 2011 16:30:10 +0200 Subject: update --- BitcoinGUI.cpp | 10 ++-- TransactionTableModel.cpp | 53 ++++++++++++++++++++++ TransactionTableModel.h | 23 ++++++++++ bitcoin.pro | 6 ++- bitcoin.pro.user | 113 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 199 insertions(+), 6 deletions(-) create mode 100644 TransactionTableModel.cpp create mode 100644 TransactionTableModel.h create mode 100644 bitcoin.pro.user diff --git a/BitcoinGUI.cpp b/BitcoinGUI.cpp index b18a18b88d..0b81f72faa 100644 --- a/BitcoinGUI.cpp +++ b/BitcoinGUI.cpp @@ -2,6 +2,7 @@ * W.J. van der Laan 2011 */ #include "BitcoinGUI.h" +#include "TransactionTableModel.h" #include #include @@ -27,7 +28,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): setWindowTitle("Bitcoin"); setWindowIcon(QIcon("bitcoin.png")); - QAction *quit = new QAction(QIcon("quit.png"), "&Quit", this); QAction *sendcoins = new QAction(QIcon("send.png"), "&Send coins", this); QAction *addressbook = new QAction(QIcon("address-book.png"), "&Address book", this); @@ -53,13 +53,13 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): /* Address:
: New... : Paste to clipboard */ QHBoxLayout *hbox_address = new QHBoxLayout(); - hbox_address->addWidget(new QLabel("Your Bitcoin Address:")); + hbox_address->addWidget(new QLabel(tr("Your Bitcoin Address:"))); QLineEdit *edit_address = new QLineEdit(); edit_address->setReadOnly(true); hbox_address->addWidget(edit_address); QPushButton *button_new = new QPushButton(trUtf8("&New\u2026")); - QPushButton *button_clipboard = new QPushButton("&Copy to clipboard"); + QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); hbox_address->addWidget(button_new); hbox_address->addWidget(button_clipboard); @@ -80,12 +80,14 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): /* Transaction table: * TransactionView * TransactionModel - * Selection behaviour + * Selection behavior * selection mode * QAbstractItemView::SelectItems * QAbstractItemView::ExtendedSelection */ QTableView *transaction_table = new QTableView(this); + TransactionTableModel *transaction_model = new TransactionTableModel(this); + transaction_table->setModel(transaction_model); QTabBar *tabs = new QTabBar(this); tabs->addTab("All transactions"); diff --git a/TransactionTableModel.cpp b/TransactionTableModel.cpp new file mode 100644 index 0000000000..0f35296061 --- /dev/null +++ b/TransactionTableModel.cpp @@ -0,0 +1,53 @@ +#include "TransactionTableModel.h" + +TransactionTableModel::TransactionTableModel(QObject *parent): + QAbstractTableModel(parent) +{ + columns << "Status" << "Date" << "Description" << "Debit" << "Credit"; +} + +int TransactionTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 5; +} + +int TransactionTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} + +QVariant TransactionTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + + if(role == Qt::DisplayRole) + { + /* index.row(), index.column() */ + /* Return QString */ + return QString("test"); + } + return QVariant(); +} + +QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(role != Qt::DisplayRole) + return QVariant(); + + if(orientation == Qt::Horizontal) + { + return columns[section]; + } + return QVariant(); +} + +Qt::ItemFlags TransactionTableModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemIsEnabled; + + return QAbstractTableModel::flags(index); +} diff --git a/TransactionTableModel.h b/TransactionTableModel.h new file mode 100644 index 0000000000..9071ed6688 --- /dev/null +++ b/TransactionTableModel.h @@ -0,0 +1,23 @@ +#ifndef H_TRANSACTIONTABLEMODEL +#define H_TRANSACTIONTABLEMODEL + +#include +#include + +class TransactionTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + TransactionTableModel(QObject *parent = 0); + + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; +private: + QStringList columns; +}; + +#endif + diff --git a/bitcoin.pro b/bitcoin.pro index ab440fab85..26a3216f4b 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -8,5 +8,7 @@ DEPENDPATH += . INCLUDEPATH += . # Input -HEADERS += BitcoinGUI.h -SOURCES += bitcoin.cpp BitcoinGUI.cpp +HEADERS += BitcoinGUI.h \ + TransactionTableModel.h +SOURCES += bitcoin.cpp BitcoinGUI.cpp \ + TransactionTableModel.cpp diff --git a/bitcoin.pro.user b/bitcoin.pro.user new file mode 100644 index 0000000000..48022383df --- /dev/null +++ b/bitcoin.pro.user @@ -0,0 +1,113 @@ + + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + System + + + + ProjectExplorer.Project.Target.0 + + Desktop + Qt4ProjectManager.Target.DesktopTarget + 0 + 0 + + + qmake + QtProjectManager.QMakeBuildStep + + + + Make + Qt4ProjectManager.MakeStep + false + + + + 2 + + Make + Qt4ProjectManager.MakeStep + true + + clean + + + + 1 + false + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + /store/orion/projects/bitcoin/bitcoin-qt + 4 + 0 + false + + + + qmake + QtProjectManager.QMakeBuildStep + + + + Make + Qt4ProjectManager.MakeStep + false + + + + 2 + + Make + Qt4ProjectManager.MakeStep + true + + clean + + + + 1 + false + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + /store/orion/projects/bitcoin/bitcoin-qt + 4 + 0 + false + + 2 + + bitcoin + Qt4ProjectManager.Qt4RunConfiguration + 2 + + bitcoin.pro + false + false + + false + false + + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 4 + + -- cgit v1.2.3 From 1355cfe131e9bbaa209f79f7d9987a73b69285d3 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 8 May 2011 22:23:31 +0200 Subject: add all (unpopulated) dialogs --- .gitignore | 2 + AboutDialog.cpp | 6 +++ AboutDialog.h | 18 ++++++++ AddressBookDialog.cpp | 7 +++ AddressBookDialog.h | 18 ++++++++ BitcoinGUI.cpp | 18 +++++++- BitcoinGUI.h | 14 +++--- SendCoinsDialog.cpp | 6 +++ SendCoinsDialog.h | 18 ++++++++ SettingsDialog.cpp | 7 +++ SettingsDialog.h | 18 ++++++++ TODO | 5 +- TransactionTableModel.cpp | 25 ++++++++-- TransactionTableModel.h | 14 ++++-- bitcoin.pro | 12 ++++- bitcoin.pro.user | 113 ---------------------------------------------- moc_BitcoinGUI.cpp | 79 -------------------------------- 17 files changed, 169 insertions(+), 211 deletions(-) create mode 100644 AboutDialog.cpp create mode 100644 AboutDialog.h create mode 100644 AddressBookDialog.cpp create mode 100644 AddressBookDialog.h create mode 100644 SendCoinsDialog.cpp create mode 100644 SendCoinsDialog.h create mode 100644 SettingsDialog.cpp create mode 100644 SettingsDialog.h delete mode 100644 bitcoin.pro.user delete mode 100644 moc_BitcoinGUI.cpp diff --git a/.gitignore b/.gitignore index d6ff91a9ea..a261fd9c2e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *~ *.o +moc_*.cpp +*.pro.user diff --git a/AboutDialog.cpp b/AboutDialog.cpp new file mode 100644 index 0000000000..dd2ec9f9b0 --- /dev/null +++ b/AboutDialog.cpp @@ -0,0 +1,6 @@ +#include "AboutDialog.h" + +AboutDialog::AboutDialog(QWidget *parent) : + QDialog(parent) +{ +} diff --git a/AboutDialog.h b/AboutDialog.h new file mode 100644 index 0000000000..1372121099 --- /dev/null +++ b/AboutDialog.h @@ -0,0 +1,18 @@ +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +class AboutDialog : public QDialog +{ + Q_OBJECT +public: + explicit AboutDialog(QWidget *parent = 0); + +signals: + +public slots: + +}; + +#endif // ABOUTDIALOG_H diff --git a/AddressBookDialog.cpp b/AddressBookDialog.cpp new file mode 100644 index 0000000000..7e853ede7b --- /dev/null +++ b/AddressBookDialog.cpp @@ -0,0 +1,7 @@ +#include "AddressBookDialog.h" + +AddressBookDialog::AddressBookDialog(QWidget *parent) : + QDialog(parent) +{ +} + diff --git a/AddressBookDialog.h b/AddressBookDialog.h new file mode 100644 index 0000000000..3a27aa62a1 --- /dev/null +++ b/AddressBookDialog.h @@ -0,0 +1,18 @@ +#ifndef ADDRESSBOOKDIALOG_H +#define ADDRESSBOOKDIALOG_H + +#include + +class AddressBookDialog : public QDialog +{ + Q_OBJECT +public: + explicit AddressBookDialog(QWidget *parent = 0); + +signals: + +public slots: + +}; + +#endif // ADDRESSBOOKDIALOG_H diff --git a/BitcoinGUI.cpp b/BitcoinGUI.cpp index 0b81f72faa..f07b828098 100644 --- a/BitcoinGUI.cpp +++ b/BitcoinGUI.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -86,9 +87,24 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): * QAbstractItemView::ExtendedSelection */ QTableView *transaction_table = new QTableView(this); + TransactionTableModel *transaction_model = new TransactionTableModel(this); transaction_table->setModel(transaction_model); - + transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); + transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); + + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Status, 112); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Date, 112); + transaction_table->horizontalHeader()->setResizeMode( + TransactionTableModel::Description, QHeaderView::Stretch); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Debit, 79); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Credit, 79); + /* TODO: alignment; debit/credit columns must align right */ + QTabBar *tabs = new QTabBar(this); tabs->addTab("All transactions"); tabs->addTab("Sent/Received"); diff --git a/BitcoinGUI.h b/BitcoinGUI.h index e4ff2fe61a..590bb3efb7 100644 --- a/BitcoinGUI.h +++ b/BitcoinGUI.h @@ -1,5 +1,5 @@ -#ifndef H_BITCOINGUI -#define H_BITCOINGUI +#ifndef BITCOINGUI_H +#define BITCOINGUI_H #include @@ -7,14 +7,14 @@ class BitcoinGUI : public QMainWindow { Q_OBJECT public: - BitcoinGUI(QWidget *parent = 0); + explicit BitcoinGUI(QWidget *parent = 0); /* Transaction table tab indices */ enum { - ALL_TRANSACTIONS = 0, - SENT_RECEIVED = 1, - SENT = 2, - RECEIVED = 3 + AllTransactions = 0, + SentReceived = 1, + Sent = 2, + Received = 3 } TabIndex; private slots: void currentChanged(int tab); diff --git a/SendCoinsDialog.cpp b/SendCoinsDialog.cpp new file mode 100644 index 0000000000..a89a58dc54 --- /dev/null +++ b/SendCoinsDialog.cpp @@ -0,0 +1,6 @@ +#include "SendCoinsDialog.h" + +SendCoinsDialog::SendCoinsDialog(QWidget *parent) : + QDialog(parent) +{ +} diff --git a/SendCoinsDialog.h b/SendCoinsDialog.h new file mode 100644 index 0000000000..f2720c3738 --- /dev/null +++ b/SendCoinsDialog.h @@ -0,0 +1,18 @@ +#ifndef SENDCOINSDIALOG_H +#define SENDCOINSDIALOG_H + +#include + +class SendCoinsDialog : public QDialog +{ + Q_OBJECT +public: + explicit SendCoinsDialog(QWidget *parent = 0); + +signals: + +public slots: + +}; + +#endif // SENDCOINSDIALOG_H diff --git a/SettingsDialog.cpp b/SettingsDialog.cpp new file mode 100644 index 0000000000..d2ce01ee66 --- /dev/null +++ b/SettingsDialog.cpp @@ -0,0 +1,7 @@ +#include "SettingsDialog.h" + +SettingsDialog::SettingsDialog(QWidget *parent) : + QDialog(parent) +{ +} + diff --git a/SettingsDialog.h b/SettingsDialog.h new file mode 100644 index 0000000000..7bbfb1f83a --- /dev/null +++ b/SettingsDialog.h @@ -0,0 +1,18 @@ +#ifndef SETTINGSDIALOG_H +#define SETTINGSDIALOG_H + +#include + +class SettingsDialog : public QDialog +{ + Q_OBJECT +public: + explicit SettingsDialog(QWidget *parent = 0); + +signals: + +public slots: + +}; + +#endif // SETTINGSDIALOG_H diff --git a/TODO b/TODO index 934b231b7f..4729921ce6 100644 --- a/TODO +++ b/TODO @@ -23,6 +23,9 @@ Table [columns]: Credit ** Table should be the same in all tabs. Do we really need different widgets? + -> yes, to have different proxy views + + ** Table rows are much too high? Status bar: Permanent status indicators: @@ -46,5 +49,5 @@ AboutDialog - Address Book icon - - Translation + diff --git a/TransactionTableModel.cpp b/TransactionTableModel.cpp index 0f35296061..e5cf258167 100644 --- a/TransactionTableModel.cpp +++ b/TransactionTableModel.cpp @@ -1,5 +1,14 @@ #include "TransactionTableModel.h" +/* Credit and Debit columns are right-aligned as they contain numbers */ +static Qt::AlignmentFlag column_alignments[] = { + Qt::AlignLeft, + Qt::AlignLeft, + Qt::AlignLeft, + Qt::AlignRight, + Qt::AlignRight + }; + TransactionTableModel::TransactionTableModel(QObject *parent): QAbstractTableModel(parent) { @@ -28,18 +37,24 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const /* index.row(), index.column() */ /* Return QString */ return QString("test"); + } else if (role == Qt::TextAlignmentRole) + { + return column_alignments[index.column()]; } return QVariant(); } QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const { - if(role != Qt::DisplayRole) - return QVariant(); - - if(orientation == Qt::Horizontal) + if(role == Qt::DisplayRole) + { + if(orientation == Qt::Horizontal) + { + return columns[section]; + } + } else if (role == Qt::TextAlignmentRole) { - return columns[section]; + return column_alignments[section]; } return QVariant(); } diff --git a/TransactionTableModel.h b/TransactionTableModel.h index 9071ed6688..8913f1d94c 100644 --- a/TransactionTableModel.h +++ b/TransactionTableModel.h @@ -1,5 +1,5 @@ -#ifndef H_TRANSACTIONTABLEMODEL -#define H_TRANSACTIONTABLEMODEL +#ifndef TRANSACTIONTABLEMODEL_H +#define TRANSACTIONTABLEMODEL_H #include #include @@ -8,7 +8,15 @@ class TransactionTableModel : public QAbstractTableModel { Q_OBJECT public: - TransactionTableModel(QObject *parent = 0); + explicit TransactionTableModel(QObject *parent = 0); + + enum { + Status = 0, + Date = 1, + Description = 2, + Debit = 3, + Credit = 4 + } ColumnIndex; int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; diff --git a/bitcoin.pro b/bitcoin.pro index 26a3216f4b..4f794ac693 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -9,6 +9,14 @@ INCLUDEPATH += . # Input HEADERS += BitcoinGUI.h \ - TransactionTableModel.h + TransactionTableModel.h \ + SendCoinsDialog.h \ + SettingsDialog.h \ + AddressBookDialog.h \ + AboutDialog.h SOURCES += bitcoin.cpp BitcoinGUI.cpp \ - TransactionTableModel.cpp + TransactionTableModel.cpp \ + SendCoinsDialog.cpp \ + SettingsDialog.cpp \ + AddressBookDialog.cpp \ + AboutDialog.cpp diff --git a/bitcoin.pro.user b/bitcoin.pro.user deleted file mode 100644 index 48022383df..0000000000 --- a/bitcoin.pro.user +++ /dev/null @@ -1,113 +0,0 @@ - - - - ProjectExplorer.Project.ActiveTarget - 0 - - - ProjectExplorer.Project.EditorSettings - - System - - - - ProjectExplorer.Project.Target.0 - - Desktop - Qt4ProjectManager.Target.DesktopTarget - 0 - 0 - - - qmake - QtProjectManager.QMakeBuildStep - - - - Make - Qt4ProjectManager.MakeStep - false - - - - 2 - - Make - Qt4ProjectManager.MakeStep - true - - clean - - - - 1 - false - - Debug - Qt4ProjectManager.Qt4BuildConfiguration - 2 - /store/orion/projects/bitcoin/bitcoin-qt - 4 - 0 - false - - - - qmake - QtProjectManager.QMakeBuildStep - - - - Make - Qt4ProjectManager.MakeStep - false - - - - 2 - - Make - Qt4ProjectManager.MakeStep - true - - clean - - - - 1 - false - - Release - Qt4ProjectManager.Qt4BuildConfiguration - 0 - /store/orion/projects/bitcoin/bitcoin-qt - 4 - 0 - false - - 2 - - bitcoin - Qt4ProjectManager.Qt4RunConfiguration - 2 - - bitcoin.pro - false - false - - false - false - - - 1 - - - - ProjectExplorer.Project.TargetCount - 1 - - - ProjectExplorer.Project.Updater.FileVersion - 4 - - diff --git a/moc_BitcoinGUI.cpp b/moc_BitcoinGUI.cpp deleted file mode 100644 index 3c024d3178..0000000000 --- a/moc_BitcoinGUI.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************** -** Meta object code from reading C++ file 'BitcoinGUI.h' -** -** Created: Sat May 7 20:43:39 2011 -** by: The Qt Meta Object Compiler version 62 (Qt 4.7.0) -** -** WARNING! All changes made in this file will be lost! -*****************************************************************************/ - -#include "BitcoinGUI.h" -#if !defined(Q_MOC_OUTPUT_REVISION) -#error "The header file 'BitcoinGUI.h' doesn't include ." -#elif Q_MOC_OUTPUT_REVISION != 62 -#error "This file was generated using the moc from 4.7.0. It" -#error "cannot be used with the include files from this version of Qt." -#error "(The moc has changed too much.)" -#endif - -QT_BEGIN_MOC_NAMESPACE -static const uint qt_meta_data_BitcoinGUI[] = { - - // content: - 5, // revision - 0, // classname - 0, 0, // classinfo - 1, 14, // methods - 0, 0, // properties - 0, 0, // enums/sets - 0, 0, // constructors - 0, // flags - 0, // signalCount - - // slots: signature, parameters, type, tag, flags - 16, 12, 11, 11, 0x08, - - 0 // eod -}; - -static const char qt_meta_stringdata_BitcoinGUI[] = { - "BitcoinGUI\0\0tab\0currentChanged(int)\0" -}; - -const QMetaObject BitcoinGUI::staticMetaObject = { - { &QMainWindow::staticMetaObject, qt_meta_stringdata_BitcoinGUI, - qt_meta_data_BitcoinGUI, 0 } -}; - -#ifdef Q_NO_DATA_RELOCATION -const QMetaObject &BitcoinGUI::getStaticMetaObject() { return staticMetaObject; } -#endif //Q_NO_DATA_RELOCATION - -const QMetaObject *BitcoinGUI::metaObject() const -{ - return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; -} - -void *BitcoinGUI::qt_metacast(const char *_clname) -{ - if (!_clname) return 0; - if (!strcmp(_clname, qt_meta_stringdata_BitcoinGUI)) - return static_cast(const_cast< BitcoinGUI*>(this)); - return QMainWindow::qt_metacast(_clname); -} - -int BitcoinGUI::qt_metacall(QMetaObject::Call _c, int _id, void **_a) -{ - _id = QMainWindow::qt_metacall(_c, _id, _a); - if (_id < 0) - return _id; - if (_c == QMetaObject::InvokeMetaMethod) { - switch (_id) { - case 0: currentChanged((*reinterpret_cast< int(*)>(_a[1]))); break; - default: ; - } - _id -= 1; - } - return _id; -} -QT_END_MOC_NAMESPACE -- cgit v1.2.3 From 13740b7ed168780657f986ed2c3c90240315df9f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 9 May 2011 20:44:46 +0200 Subject: Use resource system --- AddressTableModel.cpp | 6 ++++++ AddressTableModel.h | 18 ++++++++++++++++++ BitcoinGUI.cpp | 24 +++++++++++++----------- bitcoin.pro | 9 +++++++-- bitcoin.qrc | 8 ++++++++ 5 files changed, 52 insertions(+), 13 deletions(-) create mode 100644 AddressTableModel.cpp create mode 100644 AddressTableModel.h create mode 100644 bitcoin.qrc diff --git a/AddressTableModel.cpp b/AddressTableModel.cpp new file mode 100644 index 0000000000..b25ee3e9ee --- /dev/null +++ b/AddressTableModel.cpp @@ -0,0 +1,6 @@ +#include "AddressTableModel.h" + +AddressTableModel::AddressTableModel(QObject *parent) : + QAbstractTableModel(parent) +{ +} diff --git a/AddressTableModel.h b/AddressTableModel.h new file mode 100644 index 0000000000..490452e11e --- /dev/null +++ b/AddressTableModel.h @@ -0,0 +1,18 @@ +#ifndef ADDRESSTABLEMODEL_H +#define ADDRESSTABLEMODEL_H + +#include + +class AddressTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit AddressTableModel(QObject *parent = 0); + +signals: + +public slots: + +}; + +#endif // ADDRESSTABLEMODEL_H diff --git a/BitcoinGUI.cpp b/BitcoinGUI.cpp index f07b828098..3ee74a1e57 100644 --- a/BitcoinGUI.cpp +++ b/BitcoinGUI.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -27,12 +28,12 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): { resize(850, 550); setWindowTitle("Bitcoin"); - setWindowIcon(QIcon("bitcoin.png")); + setWindowIcon(QIcon(":icons/bitcoin")); - QAction *quit = new QAction(QIcon("quit.png"), "&Quit", this); - QAction *sendcoins = new QAction(QIcon("send.png"), "&Send coins", this); - QAction *addressbook = new QAction(QIcon("address-book.png"), "&Address book", this); - QAction *about = new QAction(QIcon("bitcoin.png"), "&About", this); + QAction *quit = new QAction(QIcon(":/icons/quit"), "&Quit", this); + QAction *sendcoins = new QAction(QIcon(":/icons/send"), "&Send coins", this); + QAction *addressbook = new QAction(QIcon(":/icons/address-book"), "&Address book", this); + QAction *about = new QAction(QIcon(":/icons/bitcoin"), "&About", this); /* Menus */ QMenu *file = menuBar()->addMenu("&File"); @@ -66,9 +67,10 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): /* Balance: */ QHBoxLayout *hbox_balance = new QHBoxLayout(); - hbox_balance->addWidget(new QLabel("Balance:")); + hbox_balance->addWidget(new QLabel(tr("Balance:"))); hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ - QLabel *label_balance = new QLabel("1,234.54"); + + QLabel *label_balance = new QLabel(QLocale::system().toString(1345.54)); /* TODO: use locale to format amount */ label_balance->setFont(QFont("Teletype")); hbox_balance->addWidget(label_balance); hbox_balance->addStretch(1); @@ -106,10 +108,10 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): /* TODO: alignment; debit/credit columns must align right */ QTabBar *tabs = new QTabBar(this); - tabs->addTab("All transactions"); - tabs->addTab("Sent/Received"); - tabs->addTab("Sent"); - tabs->addTab("Received"); + tabs->addTab(tr("All transactions")); + tabs->addTab(tr("Sent/Received")); + tabs->addTab(tr("Sent")); + tabs->addTab(tr("Received")); vbox->addWidget(tabs); vbox->addWidget(transaction_table); diff --git a/bitcoin.pro b/bitcoin.pro index 4f794ac693..b28b45f448 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -13,10 +13,15 @@ HEADERS += BitcoinGUI.h \ SendCoinsDialog.h \ SettingsDialog.h \ AddressBookDialog.h \ - AboutDialog.h + AboutDialog.h \ + AddressTableModel.h SOURCES += bitcoin.cpp BitcoinGUI.cpp \ TransactionTableModel.cpp \ SendCoinsDialog.cpp \ SettingsDialog.cpp \ AddressBookDialog.cpp \ - AboutDialog.cpp + AboutDialog.cpp \ + AddressTableModel.cpp + +RESOURCES += \ + bitcoin.qrc diff --git a/bitcoin.qrc b/bitcoin.qrc new file mode 100644 index 0000000000..ebce276c8d --- /dev/null +++ b/bitcoin.qrc @@ -0,0 +1,8 @@ + + + res/icons/address-book.png + res/icons/bitcoin.png + res/icons/quit.png + res/icons/send.png + + -- cgit v1.2.3 From 053980bb1933fc69c2fce55aaa76c3f6a0bd92ec Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 9 May 2011 20:45:09 +0200 Subject: moved files --- address-book.png | Bin 656 -> 0 bytes bitcoin.png | Bin 1086 -> 0 bytes quit.png | Bin 876 -> 0 bytes send.png | Bin 938 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 address-book.png delete mode 100644 bitcoin.png delete mode 100644 quit.png delete mode 100644 send.png diff --git a/address-book.png b/address-book.png deleted file mode 100644 index 621ca40245..0000000000 Binary files a/address-book.png and /dev/null differ diff --git a/bitcoin.png b/bitcoin.png deleted file mode 100644 index 09520aca23..0000000000 Binary files a/bitcoin.png and /dev/null differ diff --git a/quit.png b/quit.png deleted file mode 100644 index fb510fbea6..0000000000 Binary files a/quit.png and /dev/null differ diff --git a/send.png b/send.png deleted file mode 100644 index 51067a192b..0000000000 Binary files a/send.png and /dev/null differ -- cgit v1.2.3 From 94ccfa8c5de399b79bf11f318a63070b11047d59 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 9 May 2011 20:45:22 +0200 Subject: new resource location --- res/icons/address-book.png | Bin 0 -> 656 bytes res/icons/bitcoin.png | Bin 0 -> 1086 bytes res/icons/quit.png | Bin 0 -> 876 bytes res/icons/send.png | Bin 0 -> 938 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 res/icons/address-book.png create mode 100644 res/icons/bitcoin.png create mode 100644 res/icons/quit.png create mode 100644 res/icons/send.png diff --git a/res/icons/address-book.png b/res/icons/address-book.png new file mode 100644 index 0000000000..621ca40245 Binary files /dev/null and b/res/icons/address-book.png differ diff --git a/res/icons/bitcoin.png b/res/icons/bitcoin.png new file mode 100644 index 0000000000..09520aca23 Binary files /dev/null and b/res/icons/bitcoin.png differ diff --git a/res/icons/quit.png b/res/icons/quit.png new file mode 100644 index 0000000000..fb510fbea6 Binary files /dev/null and b/res/icons/quit.png differ diff --git a/res/icons/send.png b/res/icons/send.png new file mode 100644 index 0000000000..51067a192b Binary files /dev/null and b/res/icons/send.png differ -- cgit v1.2.3 From de11d828566829623a4bdb3f0266356de8394a1a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 10 May 2011 13:32:56 +0200 Subject: update model --- BitcoinGUI.cpp | 19 ++++++++++++------- TransactionTableModel.cpp | 7 ++++++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/BitcoinGUI.cpp b/BitcoinGUI.cpp index 3ee74a1e57..f0a66334db 100644 --- a/BitcoinGUI.cpp +++ b/BitcoinGUI.cpp @@ -27,7 +27,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QMainWindow(parent) { resize(850, 550); - setWindowTitle("Bitcoin"); + setWindowTitle(tr("Bitcoin")); setWindowIcon(QIcon(":icons/bitcoin")); QAction *quit = new QAction(QIcon(":/icons/quit"), "&Quit", this); @@ -70,7 +70,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): hbox_balance->addWidget(new QLabel(tr("Balance:"))); hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ - QLabel *label_balance = new QLabel(QLocale::system().toString(1345.54)); /* TODO: use locale to format amount */ + QLabel *label_balance = new QLabel(QLocale::system().toString(1345.54)); label_balance->setFont(QFont("Teletype")); hbox_balance->addWidget(label_balance); hbox_balance->addStretch(1); @@ -83,10 +83,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): /* Transaction table: * TransactionView * TransactionModel - * Selection behavior - * selection mode - * QAbstractItemView::SelectItems - * QAbstractItemView::ExtendedSelection */ QTableView *transaction_table = new QTableView(this); @@ -105,13 +101,22 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): TransactionTableModel::Debit, 79); transaction_table->horizontalHeader()->resizeSection( TransactionTableModel::Credit, 79); - /* TODO: alignment; debit/credit columns must align right */ + /* setupTabs */ QTabBar *tabs = new QTabBar(this); tabs->addTab(tr("All transactions")); tabs->addTab(tr("Sent/Received")); tabs->addTab(tr("Sent")); tabs->addTab(tr("Received")); + /* QSortFilterProxyModel + setFilterRole : filter on user role + setFilterKeyColumn + setFilterRegExp / setFilterFixedString + "^." + "^[sr]" + "^[s]" + "^[r]" + */ vbox->addWidget(tabs); vbox->addWidget(transaction_table); diff --git a/TransactionTableModel.cpp b/TransactionTableModel.cpp index e5cf258167..aa3128c5a5 100644 --- a/TransactionTableModel.cpp +++ b/TransactionTableModel.cpp @@ -12,7 +12,7 @@ static Qt::AlignmentFlag column_alignments[] = { TransactionTableModel::TransactionTableModel(QObject *parent): QAbstractTableModel(parent) { - columns << "Status" << "Date" << "Description" << "Debit" << "Credit"; + columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); } int TransactionTableModel::rowCount(const QModelIndex &parent) const @@ -41,6 +41,11 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { return column_alignments[index.column()]; } + /* user role: transaction type + "s" (sent) + "r" (received) + "g" (generated) + */ return QVariant(); } -- cgit v1.2.3 From 05227257544c94e3e188eeb1fdd039db37598ce0 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 10 May 2011 13:33:48 +0200 Subject: ignore generated resource file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a261fd9c2e..819c55e3ce 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.o moc_*.cpp *.pro.user +qrc_*.cpp -- cgit v1.2.3 From af943776679e66c83558ef98cf40910382f535e7 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 10 May 2011 19:03:10 +0200 Subject: implement filtering, action listeners --- BitcoinGUI.cpp | 141 ++++++++++++++++++++++++++++++---------------- BitcoinGUI.h | 7 ++- TransactionTableModel.cpp | 34 +++++++---- 3 files changed, 119 insertions(+), 63 deletions(-) diff --git a/BitcoinGUI.cpp b/BitcoinGUI.cpp index f0a66334db..0d7fbf4b20 100644 --- a/BitcoinGUI.cpp +++ b/BitcoinGUI.cpp @@ -1,15 +1,20 @@ /* + * Qt4 bitcoin GUI. + * * W.J. van der Laan 2011 */ #include "BitcoinGUI.h" #include "TransactionTableModel.h" +#include "AddressBookDialog.h" +#include "SettingsDialog.h" +#include "SendCoinsDialog.h" #include #include #include #include #include -#include +#include #include #include #include @@ -20,6 +25,9 @@ #include #include #include +#include + +#include #include @@ -30,11 +38,13 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): setWindowTitle(tr("Bitcoin")); setWindowIcon(QIcon(":icons/bitcoin")); - QAction *quit = new QAction(QIcon(":/icons/quit"), "&Quit", this); - QAction *sendcoins = new QAction(QIcon(":/icons/send"), "&Send coins", this); - QAction *addressbook = new QAction(QIcon(":/icons/address-book"), "&Address book", this); - QAction *about = new QAction(QIcon(":/icons/bitcoin"), "&About", this); - + QAction *quit = new QAction(QIcon(":/icons/quit"), tr("&Quit"), this); + QAction *sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); + QAction *addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address book"), this); + QAction *about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); + QAction *receiving_addresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); + QAction *options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); + /* Menus */ QMenu *file = menuBar()->addMenu("&File"); file->addAction(sendcoins); @@ -42,7 +52,8 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): file->addAction(quit); QMenu *settings = menuBar()->addMenu("&Settings"); - settings->addAction(addressbook); + settings->addAction(receiving_addresses); + settings->addAction(options); QMenu *help = menuBar()->addMenu("&Help"); help->addAction(about); @@ -60,7 +71,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): edit_address->setReadOnly(true); hbox_address->addWidget(edit_address); - QPushButton *button_new = new QPushButton(trUtf8("&New\u2026")); + QPushButton *button_new = new QPushButton(tr("&New...")); QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); hbox_address->addWidget(button_new); hbox_address->addWidget(button_clipboard); @@ -80,47 +91,50 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): vbox->addLayout(hbox_address); vbox->addLayout(hbox_balance); - /* Transaction table: - * TransactionView - * TransactionModel - */ - QTableView *transaction_table = new QTableView(this); - TransactionTableModel *transaction_model = new TransactionTableModel(this); - transaction_table->setModel(transaction_model); - transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); - transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); - - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Status, 112); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Date, 112); - transaction_table->horizontalHeader()->setResizeMode( - TransactionTableModel::Description, QHeaderView::Stretch); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Debit, 79); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Credit, 79); /* setupTabs */ - QTabBar *tabs = new QTabBar(this); - tabs->addTab(tr("All transactions")); - tabs->addTab(tr("Sent/Received")); - tabs->addTab(tr("Sent")); - tabs->addTab(tr("Received")); - /* QSortFilterProxyModel - setFilterRole : filter on user role - setFilterKeyColumn - setFilterRegExp / setFilterFixedString - "^." - "^[sr]" - "^[s]" - "^[r]" - */ + QStringList tab_filters, tab_labels; + tab_filters << "^." + << "^[sr]" + << "^[s]" + << "^[r]"; + tab_labels << tr("All transactions") + << tr("Sent/Received") + << tr("Sent") + << tr("Received"); + QTabWidget *tabs = new QTabWidget(this); + + for(int i = 0; i < tab_labels.size(); ++i) + { + QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this); + proxy_model->setSourceModel(transaction_model); + proxy_model->setDynamicSortFilter(true); + proxy_model->setFilterRole(Qt::UserRole); + proxy_model->setFilterRegExp(QRegExp(tab_filters.at(i))); + + QTableView *transaction_table = new QTableView(this); + transaction_table->setModel(proxy_model); + transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); + transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); + transaction_table->verticalHeader()->hide(); + + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Status, 112); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Date, 112); + transaction_table->horizontalHeader()->setResizeMode( + TransactionTableModel::Description, QHeaderView::Stretch); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Debit, 79); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Credit, 79); + + tabs->addTab(transaction_table, tab_labels.at(i)); + } vbox->addWidget(tabs); - vbox->addWidget(transaction_table); - + QWidget *centralwidget = new QWidget(this); centralwidget->setLayout(vbox); setCentralWidget(centralwidget); @@ -140,19 +154,46 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): label_transactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); label_transactions->setMinimumWidth(100); - statusBar()->addPermanentWidget(label_connections); statusBar()->addPermanentWidget(label_blocks); statusBar()->addPermanentWidget(label_transactions); - /* Action bindings */ - connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); - connect(tabs, SIGNAL(currentChanged(int)), this, SLOT(currentChanged(int))); + connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked())); + connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked())); + connect(receiving_addresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked())); + connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); + connect(button_new, SIGNAL(triggered()), this, SLOT(newAddressClicked())); + connect(button_clipboard, SIGNAL(triggered()), this, SLOT(copyClipboardClicked())); +} + +void BitcoinGUI::sendcoinsClicked() +{ + qDebug() << "Send coins clicked"; +} + +void BitcoinGUI::addressbookClicked() +{ + qDebug() << "Address book clicked"; +} + +void BitcoinGUI::optionsClicked() +{ + qDebug() << "Options clicked"; +} + +void BitcoinGUI::receivingAddressesClicked() +{ + qDebug() << "Receiving addresses clicked"; +} + +void BitcoinGUI::newAddressClicked() +{ + qDebug() << "New address clicked"; } -void BitcoinGUI::currentChanged(int tab) +void BitcoinGUI::copyClipboardClicked() { - std::cout << "Switched to tab: " << tab << std::endl; + qDebug() << "Copy to clipboard"; } diff --git a/BitcoinGUI.h b/BitcoinGUI.h index 590bb3efb7..63d28c29d4 100644 --- a/BitcoinGUI.h +++ b/BitcoinGUI.h @@ -17,7 +17,12 @@ public: Received = 3 } TabIndex; private slots: - void currentChanged(int tab); + void sendcoinsClicked(); + void addressbookClicked(); + void optionsClicked(); + void receivingAddressesClicked(); + void newAddressClicked(); + void copyClipboardClicked(); }; #endif diff --git a/TransactionTableModel.cpp b/TransactionTableModel.cpp index aa3128c5a5..d36bea211a 100644 --- a/TransactionTableModel.cpp +++ b/TransactionTableModel.cpp @@ -1,12 +1,14 @@ #include "TransactionTableModel.h" +#include + /* Credit and Debit columns are right-aligned as they contain numbers */ -static Qt::AlignmentFlag column_alignments[] = { - Qt::AlignLeft, - Qt::AlignLeft, - Qt::AlignLeft, - Qt::AlignRight, - Qt::AlignRight +static int column_alignments[] = { + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignRight|Qt::AlignVCenter, + Qt::AlignRight|Qt::AlignVCenter }; TransactionTableModel::TransactionTableModel(QObject *parent): @@ -36,16 +38,24 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { /* index.row(), index.column() */ /* Return QString */ - return QString("test"); + return QLocale::system().toString(index.row()); } else if (role == Qt::TextAlignmentRole) { return column_alignments[index.column()]; + } else if (role == Qt::UserRole) + { + /* user role: transaction type for filtering + "s" (sent) + "r" (received) + "g" (generated) + */ + switch(index.row() % 3) + { + case 0: return QString("s"); + case 1: return QString("r"); + case 2: return QString("o"); + } } - /* user role: transaction type - "s" (sent) - "r" (received) - "g" (generated) - */ return QVariant(); } -- cgit v1.2.3 From 8812ce7b27b48ac55012ede43d59a634da0e2f14 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 12 May 2011 09:40:40 +0200 Subject: add options dialog, spawn dialogs at the right place --- BitcoinGUI.cpp | 26 ++++++++++++++++++++++++-- BitcoinGUI.h | 2 ++ OptionsDialog.cpp | 7 +++++++ OptionsDialog.h | 18 ++++++++++++++++++ bitcoin.pro | 6 ++++-- 5 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 OptionsDialog.cpp create mode 100644 OptionsDialog.h diff --git a/BitcoinGUI.cpp b/BitcoinGUI.cpp index 0d7fbf4b20..0f122377aa 100644 --- a/BitcoinGUI.cpp +++ b/BitcoinGUI.cpp @@ -8,6 +8,8 @@ #include "AddressBookDialog.h" #include "SettingsDialog.h" #include "SendCoinsDialog.h" +#include "OptionsDialog.h" +#include "AboutDialog.h" #include #include @@ -166,34 +168,54 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); connect(button_new, SIGNAL(triggered()), this, SLOT(newAddressClicked())); connect(button_clipboard, SIGNAL(triggered()), this, SLOT(copyClipboardClicked())); + connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); } void BitcoinGUI::sendcoinsClicked() { qDebug() << "Send coins clicked"; + SendCoinsDialog dlg; + dlg.exec(); } void BitcoinGUI::addressbookClicked() { qDebug() << "Address book clicked"; + AddressBookDialog dlg; + /* TODO: Set tab to "Sending" */ + dlg.exec(); +} + +void BitcoinGUI::receivingAddressesClicked() +{ + qDebug() << "Receiving addresses clicked"; + AddressBookDialog dlg; + /* TODO: Set tab to "Receiving" */ + dlg.exec(); } void BitcoinGUI::optionsClicked() { qDebug() << "Options clicked"; + OptionsDialog dlg; + dlg.exec(); } -void BitcoinGUI::receivingAddressesClicked() +void BitcoinGUI::aboutClicked() { - qDebug() << "Receiving addresses clicked"; + qDebug() << "About clicked"; + AboutDialog dlg; + dlg.exec(); } void BitcoinGUI::newAddressClicked() { qDebug() << "New address clicked"; + /* TODO: generate new address */ } void BitcoinGUI::copyClipboardClicked() { qDebug() << "Copy to clipboard"; + /* TODO: copy to clipboard */ } diff --git a/BitcoinGUI.h b/BitcoinGUI.h index 63d28c29d4..0c9edad85c 100644 --- a/BitcoinGUI.h +++ b/BitcoinGUI.h @@ -21,6 +21,8 @@ private slots: void addressbookClicked(); void optionsClicked(); void receivingAddressesClicked(); + void aboutClicked(); + void newAddressClicked(); void copyClipboardClicked(); }; diff --git a/OptionsDialog.cpp b/OptionsDialog.cpp new file mode 100644 index 0000000000..891b43f4e5 --- /dev/null +++ b/OptionsDialog.cpp @@ -0,0 +1,7 @@ +#include "OptionsDialog.h" +/* TODO example: http://doc.trolltech.com/4.7/dialogs-configdialog-configdialog-cpp.html */ + +OptionsDialog::OptionsDialog(QWidget *parent) : + QDialog(parent) +{ +} diff --git a/OptionsDialog.h b/OptionsDialog.h new file mode 100644 index 0000000000..529eb21478 --- /dev/null +++ b/OptionsDialog.h @@ -0,0 +1,18 @@ +#ifndef OPTIONSDIALOG_H +#define OPTIONSDIALOG_H + +#include + +class OptionsDialog : public QDialog +{ + Q_OBJECT +public: + explicit OptionsDialog(QWidget *parent = 0); + +signals: + +public slots: + +}; + +#endif // OPTIONSDIALOG_H diff --git a/bitcoin.pro b/bitcoin.pro index b28b45f448..99a2601ecd 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -14,14 +14,16 @@ HEADERS += BitcoinGUI.h \ SettingsDialog.h \ AddressBookDialog.h \ AboutDialog.h \ - AddressTableModel.h + AddressTableModel.h \ + OptionsDialog.h SOURCES += bitcoin.cpp BitcoinGUI.cpp \ TransactionTableModel.cpp \ SendCoinsDialog.cpp \ SettingsDialog.cpp \ AddressBookDialog.cpp \ AboutDialog.cpp \ - AddressTableModel.cpp + AddressTableModel.cpp \ + OptionsDialog.cpp RESOURCES += \ bitcoin.qrc -- cgit v1.2.3 From df577886e47d62396c343c59b62150eccef44131 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 12 May 2011 14:44:52 +0200 Subject: rename to qt standard --- AboutDialog.cpp | 6 -- AboutDialog.h | 18 ---- AddressBookDialog.cpp | 7 -- AddressBookDialog.h | 18 ---- AddressTableModel.cpp | 6 -- AddressTableModel.h | 18 ---- BitcoinGUI.cpp | 221 ---------------------------------------------- BitcoinGUI.h | 30 ------- OptionsDialog.cpp | 7 -- OptionsDialog.h | 18 ---- SendCoinsDialog.cpp | 6 -- SendCoinsDialog.h | 18 ---- SettingsDialog.cpp | 7 -- SettingsDialog.h | 18 ---- TODO | 3 + TransactionTableModel.cpp | 83 ----------------- TransactionTableModel.h | 31 ------- aboutdialog.cpp | 6 ++ aboutdialog.h | 18 ++++ addressbookdialog.cpp | 19 ++++ addressbookdialog.h | 28 ++++++ addressbookdialog.ui | 77 ++++++++++++++++ addresstablemodel.cpp | 6 ++ addresstablemodel.h | 18 ++++ bitcoin.pro | 20 +++-- bitcoingui.cpp | 220 +++++++++++++++++++++++++++++++++++++++++++++ bitcoingui.h | 30 +++++++ mainoptionspage.cpp | 67 ++++++++++++++ mainoptionspage.h | 18 ++++ optionsdialog.cpp | 55 ++++++++++++ optionsdialog.h | 25 ++++++ sendcoinsdialog.cpp | 14 +++ sendcoinsdialog.h | 22 +++++ sendcoinsdialog.ui | 146 ++++++++++++++++++++++++++++++ transactiontablemodel.cpp | 83 +++++++++++++++++ transactiontablemodel.h | 31 +++++++ 36 files changed, 898 insertions(+), 520 deletions(-) delete mode 100644 AboutDialog.cpp delete mode 100644 AboutDialog.h delete mode 100644 AddressBookDialog.cpp delete mode 100644 AddressBookDialog.h delete mode 100644 AddressTableModel.cpp delete mode 100644 AddressTableModel.h delete mode 100644 BitcoinGUI.cpp delete mode 100644 BitcoinGUI.h delete mode 100644 OptionsDialog.cpp delete mode 100644 OptionsDialog.h delete mode 100644 SendCoinsDialog.cpp delete mode 100644 SendCoinsDialog.h delete mode 100644 SettingsDialog.cpp delete mode 100644 SettingsDialog.h delete mode 100644 TransactionTableModel.cpp delete mode 100644 TransactionTableModel.h create mode 100644 aboutdialog.cpp create mode 100644 aboutdialog.h create mode 100644 addressbookdialog.cpp create mode 100644 addressbookdialog.h create mode 100644 addressbookdialog.ui create mode 100644 addresstablemodel.cpp create mode 100644 addresstablemodel.h create mode 100644 bitcoingui.cpp create mode 100644 bitcoingui.h create mode 100644 mainoptionspage.cpp create mode 100644 mainoptionspage.h create mode 100644 optionsdialog.cpp create mode 100644 optionsdialog.h create mode 100644 sendcoinsdialog.cpp create mode 100644 sendcoinsdialog.h create mode 100644 sendcoinsdialog.ui create mode 100644 transactiontablemodel.cpp create mode 100644 transactiontablemodel.h diff --git a/AboutDialog.cpp b/AboutDialog.cpp deleted file mode 100644 index dd2ec9f9b0..0000000000 --- a/AboutDialog.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "AboutDialog.h" - -AboutDialog::AboutDialog(QWidget *parent) : - QDialog(parent) -{ -} diff --git a/AboutDialog.h b/AboutDialog.h deleted file mode 100644 index 1372121099..0000000000 --- a/AboutDialog.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef ABOUTDIALOG_H -#define ABOUTDIALOG_H - -#include - -class AboutDialog : public QDialog -{ - Q_OBJECT -public: - explicit AboutDialog(QWidget *parent = 0); - -signals: - -public slots: - -}; - -#endif // ABOUTDIALOG_H diff --git a/AddressBookDialog.cpp b/AddressBookDialog.cpp deleted file mode 100644 index 7e853ede7b..0000000000 --- a/AddressBookDialog.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "AddressBookDialog.h" - -AddressBookDialog::AddressBookDialog(QWidget *parent) : - QDialog(parent) -{ -} - diff --git a/AddressBookDialog.h b/AddressBookDialog.h deleted file mode 100644 index 3a27aa62a1..0000000000 --- a/AddressBookDialog.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef ADDRESSBOOKDIALOG_H -#define ADDRESSBOOKDIALOG_H - -#include - -class AddressBookDialog : public QDialog -{ - Q_OBJECT -public: - explicit AddressBookDialog(QWidget *parent = 0); - -signals: - -public slots: - -}; - -#endif // ADDRESSBOOKDIALOG_H diff --git a/AddressTableModel.cpp b/AddressTableModel.cpp deleted file mode 100644 index b25ee3e9ee..0000000000 --- a/AddressTableModel.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "AddressTableModel.h" - -AddressTableModel::AddressTableModel(QObject *parent) : - QAbstractTableModel(parent) -{ -} diff --git a/AddressTableModel.h b/AddressTableModel.h deleted file mode 100644 index 490452e11e..0000000000 --- a/AddressTableModel.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef ADDRESSTABLEMODEL_H -#define ADDRESSTABLEMODEL_H - -#include - -class AddressTableModel : public QAbstractTableModel -{ - Q_OBJECT -public: - explicit AddressTableModel(QObject *parent = 0); - -signals: - -public slots: - -}; - -#endif // ADDRESSTABLEMODEL_H diff --git a/BitcoinGUI.cpp b/BitcoinGUI.cpp deleted file mode 100644 index 0f122377aa..0000000000 --- a/BitcoinGUI.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Qt4 bitcoin GUI. - * - * W.J. van der Laan 2011 - */ -#include "BitcoinGUI.h" -#include "TransactionTableModel.h" -#include "AddressBookDialog.h" -#include "SettingsDialog.h" -#include "SendCoinsDialog.h" -#include "OptionsDialog.h" -#include "AboutDialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -BitcoinGUI::BitcoinGUI(QWidget *parent): - QMainWindow(parent) -{ - resize(850, 550); - setWindowTitle(tr("Bitcoin")); - setWindowIcon(QIcon(":icons/bitcoin")); - - QAction *quit = new QAction(QIcon(":/icons/quit"), tr("&Quit"), this); - QAction *sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); - QAction *addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address book"), this); - QAction *about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); - QAction *receiving_addresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); - QAction *options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); - - /* Menus */ - QMenu *file = menuBar()->addMenu("&File"); - file->addAction(sendcoins); - file->addSeparator(); - file->addAction(quit); - - QMenu *settings = menuBar()->addMenu("&Settings"); - settings->addAction(receiving_addresses); - settings->addAction(options); - - QMenu *help = menuBar()->addMenu("&Help"); - help->addAction(about); - - /* Toolbar */ - QToolBar *toolbar = addToolBar("Main toolbar"); - toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - toolbar->addAction(sendcoins); - toolbar->addAction(addressbook); - - /* Address:
: New... : Paste to clipboard */ - QHBoxLayout *hbox_address = new QHBoxLayout(); - hbox_address->addWidget(new QLabel(tr("Your Bitcoin Address:"))); - QLineEdit *edit_address = new QLineEdit(); - edit_address->setReadOnly(true); - hbox_address->addWidget(edit_address); - - QPushButton *button_new = new QPushButton(tr("&New...")); - QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); - hbox_address->addWidget(button_new); - hbox_address->addWidget(button_clipboard); - - /* Balance: */ - QHBoxLayout *hbox_balance = new QHBoxLayout(); - hbox_balance->addWidget(new QLabel(tr("Balance:"))); - hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ - - QLabel *label_balance = new QLabel(QLocale::system().toString(1345.54)); - label_balance->setFont(QFont("Teletype")); - hbox_balance->addWidget(label_balance); - hbox_balance->addStretch(1); - - /* Tab widget */ - QVBoxLayout *vbox = new QVBoxLayout(); - vbox->addLayout(hbox_address); - vbox->addLayout(hbox_balance); - - TransactionTableModel *transaction_model = new TransactionTableModel(this); - - /* setupTabs */ - QStringList tab_filters, tab_labels; - tab_filters << "^." - << "^[sr]" - << "^[s]" - << "^[r]"; - tab_labels << tr("All transactions") - << tr("Sent/Received") - << tr("Sent") - << tr("Received"); - QTabWidget *tabs = new QTabWidget(this); - - for(int i = 0; i < tab_labels.size(); ++i) - { - QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this); - proxy_model->setSourceModel(transaction_model); - proxy_model->setDynamicSortFilter(true); - proxy_model->setFilterRole(Qt::UserRole); - proxy_model->setFilterRegExp(QRegExp(tab_filters.at(i))); - - QTableView *transaction_table = new QTableView(this); - transaction_table->setModel(proxy_model); - transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); - transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); - transaction_table->verticalHeader()->hide(); - - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Status, 112); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Date, 112); - transaction_table->horizontalHeader()->setResizeMode( - TransactionTableModel::Description, QHeaderView::Stretch); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Debit, 79); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Credit, 79); - - tabs->addTab(transaction_table, tab_labels.at(i)); - } - - vbox->addWidget(tabs); - - QWidget *centralwidget = new QWidget(this); - centralwidget->setLayout(vbox); - setCentralWidget(centralwidget); - - /* Status bar */ - statusBar(); - - QLabel *label_connections = new QLabel("6 connections", this); - label_connections->setFrameStyle(QFrame::Panel | QFrame::Sunken); - label_connections->setMinimumWidth(100); - - QLabel *label_blocks = new QLabel("6 blocks", this); - label_blocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); - label_blocks->setMinimumWidth(100); - - QLabel *label_transactions = new QLabel("6 transactions", this); - label_transactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); - label_transactions->setMinimumWidth(100); - - statusBar()->addPermanentWidget(label_connections); - statusBar()->addPermanentWidget(label_blocks); - statusBar()->addPermanentWidget(label_transactions); - - /* Action bindings */ - connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); - connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked())); - connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked())); - connect(receiving_addresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked())); - connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); - connect(button_new, SIGNAL(triggered()), this, SLOT(newAddressClicked())); - connect(button_clipboard, SIGNAL(triggered()), this, SLOT(copyClipboardClicked())); - connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); -} - -void BitcoinGUI::sendcoinsClicked() -{ - qDebug() << "Send coins clicked"; - SendCoinsDialog dlg; - dlg.exec(); -} - -void BitcoinGUI::addressbookClicked() -{ - qDebug() << "Address book clicked"; - AddressBookDialog dlg; - /* TODO: Set tab to "Sending" */ - dlg.exec(); -} - -void BitcoinGUI::receivingAddressesClicked() -{ - qDebug() << "Receiving addresses clicked"; - AddressBookDialog dlg; - /* TODO: Set tab to "Receiving" */ - dlg.exec(); -} - -void BitcoinGUI::optionsClicked() -{ - qDebug() << "Options clicked"; - OptionsDialog dlg; - dlg.exec(); -} - -void BitcoinGUI::aboutClicked() -{ - qDebug() << "About clicked"; - AboutDialog dlg; - dlg.exec(); -} - -void BitcoinGUI::newAddressClicked() -{ - qDebug() << "New address clicked"; - /* TODO: generate new address */ -} - -void BitcoinGUI::copyClipboardClicked() -{ - qDebug() << "Copy to clipboard"; - /* TODO: copy to clipboard */ -} diff --git a/BitcoinGUI.h b/BitcoinGUI.h deleted file mode 100644 index 0c9edad85c..0000000000 --- a/BitcoinGUI.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef BITCOINGUI_H -#define BITCOINGUI_H - -#include - -class BitcoinGUI : public QMainWindow -{ - Q_OBJECT -public: - explicit BitcoinGUI(QWidget *parent = 0); - - /* Transaction table tab indices */ - enum { - AllTransactions = 0, - SentReceived = 1, - Sent = 2, - Received = 3 - } TabIndex; -private slots: - void sendcoinsClicked(); - void addressbookClicked(); - void optionsClicked(); - void receivingAddressesClicked(); - void aboutClicked(); - - void newAddressClicked(); - void copyClipboardClicked(); -}; - -#endif diff --git a/OptionsDialog.cpp b/OptionsDialog.cpp deleted file mode 100644 index 891b43f4e5..0000000000 --- a/OptionsDialog.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "OptionsDialog.h" -/* TODO example: http://doc.trolltech.com/4.7/dialogs-configdialog-configdialog-cpp.html */ - -OptionsDialog::OptionsDialog(QWidget *parent) : - QDialog(parent) -{ -} diff --git a/OptionsDialog.h b/OptionsDialog.h deleted file mode 100644 index 529eb21478..0000000000 --- a/OptionsDialog.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef OPTIONSDIALOG_H -#define OPTIONSDIALOG_H - -#include - -class OptionsDialog : public QDialog -{ - Q_OBJECT -public: - explicit OptionsDialog(QWidget *parent = 0); - -signals: - -public slots: - -}; - -#endif // OPTIONSDIALOG_H diff --git a/SendCoinsDialog.cpp b/SendCoinsDialog.cpp deleted file mode 100644 index a89a58dc54..0000000000 --- a/SendCoinsDialog.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "SendCoinsDialog.h" - -SendCoinsDialog::SendCoinsDialog(QWidget *parent) : - QDialog(parent) -{ -} diff --git a/SendCoinsDialog.h b/SendCoinsDialog.h deleted file mode 100644 index f2720c3738..0000000000 --- a/SendCoinsDialog.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef SENDCOINSDIALOG_H -#define SENDCOINSDIALOG_H - -#include - -class SendCoinsDialog : public QDialog -{ - Q_OBJECT -public: - explicit SendCoinsDialog(QWidget *parent = 0); - -signals: - -public slots: - -}; - -#endif // SENDCOINSDIALOG_H diff --git a/SettingsDialog.cpp b/SettingsDialog.cpp deleted file mode 100644 index d2ce01ee66..0000000000 --- a/SettingsDialog.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "SettingsDialog.h" - -SettingsDialog::SettingsDialog(QWidget *parent) : - QDialog(parent) -{ -} - diff --git a/SettingsDialog.h b/SettingsDialog.h deleted file mode 100644 index 7bbfb1f83a..0000000000 --- a/SettingsDialog.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef SETTINGSDIALOG_H -#define SETTINGSDIALOG_H - -#include - -class SettingsDialog : public QDialog -{ - Q_OBJECT -public: - explicit SettingsDialog(QWidget *parent = 0); - -signals: - -public slots: - -}; - -#endif // SETTINGSDIALOG_H diff --git a/TODO b/TODO index 4729921ce6..3d1785cda1 100644 --- a/TODO +++ b/TODO @@ -51,3 +51,6 @@ AboutDialog - Translation +- Toolbar icon + +- 'notify' on incoming transaction diff --git a/TransactionTableModel.cpp b/TransactionTableModel.cpp deleted file mode 100644 index d36bea211a..0000000000 --- a/TransactionTableModel.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "TransactionTableModel.h" - -#include - -/* Credit and Debit columns are right-aligned as they contain numbers */ -static int column_alignments[] = { - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignRight|Qt::AlignVCenter, - Qt::AlignRight|Qt::AlignVCenter - }; - -TransactionTableModel::TransactionTableModel(QObject *parent): - QAbstractTableModel(parent) -{ - columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); -} - -int TransactionTableModel::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return 5; -} - -int TransactionTableModel::columnCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return columns.length(); -} - -QVariant TransactionTableModel::data(const QModelIndex &index, int role) const -{ - if(!index.isValid()) - return QVariant(); - - if(role == Qt::DisplayRole) - { - /* index.row(), index.column() */ - /* Return QString */ - return QLocale::system().toString(index.row()); - } else if (role == Qt::TextAlignmentRole) - { - return column_alignments[index.column()]; - } else if (role == Qt::UserRole) - { - /* user role: transaction type for filtering - "s" (sent) - "r" (received) - "g" (generated) - */ - switch(index.row() % 3) - { - case 0: return QString("s"); - case 1: return QString("r"); - case 2: return QString("o"); - } - } - return QVariant(); -} - -QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if(role == Qt::DisplayRole) - { - if(orientation == Qt::Horizontal) - { - return columns[section]; - } - } else if (role == Qt::TextAlignmentRole) - { - return column_alignments[section]; - } - return QVariant(); -} - -Qt::ItemFlags TransactionTableModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::ItemIsEnabled; - - return QAbstractTableModel::flags(index); -} diff --git a/TransactionTableModel.h b/TransactionTableModel.h deleted file mode 100644 index 8913f1d94c..0000000000 --- a/TransactionTableModel.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef TRANSACTIONTABLEMODEL_H -#define TRANSACTIONTABLEMODEL_H - -#include -#include - -class TransactionTableModel : public QAbstractTableModel -{ - Q_OBJECT -public: - explicit TransactionTableModel(QObject *parent = 0); - - enum { - Status = 0, - Date = 1, - Description = 2, - Debit = 3, - Credit = 4 - } ColumnIndex; - - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - Qt::ItemFlags flags(const QModelIndex &index) const; -private: - QStringList columns; -}; - -#endif - diff --git a/aboutdialog.cpp b/aboutdialog.cpp new file mode 100644 index 0000000000..dd2ec9f9b0 --- /dev/null +++ b/aboutdialog.cpp @@ -0,0 +1,6 @@ +#include "AboutDialog.h" + +AboutDialog::AboutDialog(QWidget *parent) : + QDialog(parent) +{ +} diff --git a/aboutdialog.h b/aboutdialog.h new file mode 100644 index 0000000000..1372121099 --- /dev/null +++ b/aboutdialog.h @@ -0,0 +1,18 @@ +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +class AboutDialog : public QDialog +{ + Q_OBJECT +public: + explicit AboutDialog(QWidget *parent = 0); + +signals: + +public slots: + +}; + +#endif // ABOUTDIALOG_H diff --git a/addressbookdialog.cpp b/addressbookdialog.cpp new file mode 100644 index 0000000000..ca74159d2f --- /dev/null +++ b/addressbookdialog.cpp @@ -0,0 +1,19 @@ +#include "addressbookdialog.h" +#include "ui_addressbookdialog.h" + +AddressBookDialog::AddressBookDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AddressBookDialog) +{ + ui->setupUi(this); +} + +AddressBookDialog::~AddressBookDialog() +{ + delete ui; +} + +void AddressBookDialog::setTab(int tab) +{ + +} diff --git a/addressbookdialog.h b/addressbookdialog.h new file mode 100644 index 0000000000..a51c02a794 --- /dev/null +++ b/addressbookdialog.h @@ -0,0 +1,28 @@ +#ifndef ADDRESSBOOKDIALOG_H +#define ADDRESSBOOKDIALOG_H + +#include + +namespace Ui { + class AddressBookDialog; +} + +class AddressBookDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AddressBookDialog(QWidget *parent = 0); + ~AddressBookDialog(); + + enum { + SendingTab = 0, + ReceivingTab = 1 + } Tabs; + + void setTab(int tab); +private: + Ui::AddressBookDialog *ui; +}; + +#endif // ADDRESSBOOKDIALOG_H diff --git a/addressbookdialog.ui b/addressbookdialog.ui new file mode 100644 index 0000000000..e646b08ef2 --- /dev/null +++ b/addressbookdialog.ui @@ -0,0 +1,77 @@ + + + AddressBookDialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + AddressBookDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AddressBookDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/addresstablemodel.cpp b/addresstablemodel.cpp new file mode 100644 index 0000000000..b25ee3e9ee --- /dev/null +++ b/addresstablemodel.cpp @@ -0,0 +1,6 @@ +#include "AddressTableModel.h" + +AddressTableModel::AddressTableModel(QObject *parent) : + QAbstractTableModel(parent) +{ +} diff --git a/addresstablemodel.h b/addresstablemodel.h new file mode 100644 index 0000000000..490452e11e --- /dev/null +++ b/addresstablemodel.h @@ -0,0 +1,18 @@ +#ifndef ADDRESSTABLEMODEL_H +#define ADDRESSTABLEMODEL_H + +#include + +class AddressTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit AddressTableModel(QObject *parent = 0); + +signals: + +public slots: + +}; + +#endif // ADDRESSTABLEMODEL_H diff --git a/bitcoin.pro b/bitcoin.pro index 99a2601ecd..01d93ddb6c 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -10,20 +10,24 @@ INCLUDEPATH += . # Input HEADERS += BitcoinGUI.h \ TransactionTableModel.h \ - SendCoinsDialog.h \ - SettingsDialog.h \ - AddressBookDialog.h \ AboutDialog.h \ AddressTableModel.h \ - OptionsDialog.h + OptionsDialog.h \ + MainOptionsPage.h \ + SendCoinsDialog.h \ + addressbookdialog.h SOURCES += bitcoin.cpp BitcoinGUI.cpp \ TransactionTableModel.cpp \ - SendCoinsDialog.cpp \ - SettingsDialog.cpp \ - AddressBookDialog.cpp \ AboutDialog.cpp \ AddressTableModel.cpp \ - OptionsDialog.cpp + OptionsDialog.cpp \ + MainOptionsPage.cpp \ + SendCoinsDialog.cpp \ + addressbookdialog.cpp RESOURCES += \ bitcoin.qrc + +FORMS += \ + sendcoinsdialog.ui \ + addressbookdialog.ui diff --git a/bitcoingui.cpp b/bitcoingui.cpp new file mode 100644 index 0000000000..a3b346ed82 --- /dev/null +++ b/bitcoingui.cpp @@ -0,0 +1,220 @@ +/* + * Qt4 bitcoin GUI. + * + * W.J. van der Laan 2011 + */ +#include "BitcoinGUI.h" +#include "TransactionTableModel.h" +#include "addressbookdialog.h" +#include "SendCoinsDialog.h" +#include "OptionsDialog.h" +#include "AboutDialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +BitcoinGUI::BitcoinGUI(QWidget *parent): + QMainWindow(parent) +{ + resize(850, 550); + setWindowTitle(tr("Bitcoin")); + setWindowIcon(QIcon(":icons/bitcoin")); + + QAction *quit = new QAction(QIcon(":/icons/quit"), tr("&Quit"), this); + QAction *sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); + QAction *addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address book"), this); + QAction *about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); + QAction *receiving_addresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); + QAction *options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); + + /* Menus */ + QMenu *file = menuBar()->addMenu("&File"); + file->addAction(sendcoins); + file->addSeparator(); + file->addAction(quit); + + QMenu *settings = menuBar()->addMenu("&Settings"); + settings->addAction(receiving_addresses); + settings->addAction(options); + + QMenu *help = menuBar()->addMenu("&Help"); + help->addAction(about); + + /* Toolbar */ + QToolBar *toolbar = addToolBar("Main toolbar"); + toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolbar->addAction(sendcoins); + toolbar->addAction(addressbook); + + /* Address:
: New... : Paste to clipboard */ + QHBoxLayout *hbox_address = new QHBoxLayout(); + hbox_address->addWidget(new QLabel(tr("Your Bitcoin Address:"))); + QLineEdit *edit_address = new QLineEdit(); + edit_address->setReadOnly(true); + hbox_address->addWidget(edit_address); + + QPushButton *button_new = new QPushButton(tr("&New...")); + QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); + hbox_address->addWidget(button_new); + hbox_address->addWidget(button_clipboard); + + /* Balance: */ + QHBoxLayout *hbox_balance = new QHBoxLayout(); + hbox_balance->addWidget(new QLabel(tr("Balance:"))); + hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ + + QLabel *label_balance = new QLabel(QLocale::system().toString(1345.54)); + label_balance->setFont(QFont("Teletype")); + hbox_balance->addWidget(label_balance); + hbox_balance->addStretch(1); + + /* Tab widget */ + QVBoxLayout *vbox = new QVBoxLayout(); + vbox->addLayout(hbox_address); + vbox->addLayout(hbox_balance); + + TransactionTableModel *transaction_model = new TransactionTableModel(this); + + /* setupTabs */ + QStringList tab_filters, tab_labels; + tab_filters << "^." + << "^[sr]" + << "^[s]" + << "^[r]"; + tab_labels << tr("All transactions") + << tr("Sent/Received") + << tr("Sent") + << tr("Received"); + QTabWidget *tabs = new QTabWidget(this); + + for(int i = 0; i < tab_labels.size(); ++i) + { + QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this); + proxy_model->setSourceModel(transaction_model); + proxy_model->setDynamicSortFilter(true); + proxy_model->setFilterRole(Qt::UserRole); + proxy_model->setFilterRegExp(QRegExp(tab_filters.at(i))); + + QTableView *transaction_table = new QTableView(this); + transaction_table->setModel(proxy_model); + transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); + transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); + transaction_table->verticalHeader()->hide(); + + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Status, 112); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Date, 112); + transaction_table->horizontalHeader()->setResizeMode( + TransactionTableModel::Description, QHeaderView::Stretch); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Debit, 79); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Credit, 79); + + tabs->addTab(transaction_table, tab_labels.at(i)); + } + + vbox->addWidget(tabs); + + QWidget *centralwidget = new QWidget(this); + centralwidget->setLayout(vbox); + setCentralWidget(centralwidget); + + /* Status bar */ + statusBar(); + + QLabel *label_connections = new QLabel("6 connections"); + label_connections->setFrameStyle(QFrame::Panel | QFrame::Sunken); + label_connections->setMinimumWidth(100); + + QLabel *label_blocks = new QLabel("6 blocks"); + label_blocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); + label_blocks->setMinimumWidth(100); + + QLabel *label_transactions = new QLabel("6 transactions"); + label_transactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); + label_transactions->setMinimumWidth(100); + + statusBar()->addPermanentWidget(label_connections); + statusBar()->addPermanentWidget(label_blocks); + statusBar()->addPermanentWidget(label_transactions); + + /* Action bindings */ + connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked())); + connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked())); + connect(receiving_addresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked())); + connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); + connect(button_new, SIGNAL(clicked()), this, SLOT(newAddressClicked())); + connect(button_clipboard, SIGNAL(clicked()), this, SLOT(copyClipboardClicked())); + connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); +} + +void BitcoinGUI::sendcoinsClicked() +{ + qDebug() << "Send coins clicked"; + SendCoinsDialog dlg; + dlg.exec(); +} + +void BitcoinGUI::addressbookClicked() +{ + qDebug() << "Address book clicked"; + AddressBookDialog dlg; + dlg.setTab(AddressBookDialog::SendingTab); + dlg.exec(); +} + +void BitcoinGUI::receivingAddressesClicked() +{ + qDebug() << "Receiving addresses clicked"; + AddressBookDialog dlg; + dlg.setTab(AddressBookDialog::ReceivingTab); + dlg.exec(); +} + +void BitcoinGUI::optionsClicked() +{ + qDebug() << "Options clicked"; + OptionsDialog dlg; + dlg.exec(); +} + +void BitcoinGUI::aboutClicked() +{ + qDebug() << "About clicked"; + AboutDialog dlg; + dlg.exec(); +} + +void BitcoinGUI::newAddressClicked() +{ + qDebug() << "New address clicked"; + /* TODO: generate new address */ +} + +void BitcoinGUI::copyClipboardClicked() +{ + qDebug() << "Copy to clipboard"; + /* TODO: copy to clipboard */ +} diff --git a/bitcoingui.h b/bitcoingui.h new file mode 100644 index 0000000000..0c9edad85c --- /dev/null +++ b/bitcoingui.h @@ -0,0 +1,30 @@ +#ifndef BITCOINGUI_H +#define BITCOINGUI_H + +#include + +class BitcoinGUI : public QMainWindow +{ + Q_OBJECT +public: + explicit BitcoinGUI(QWidget *parent = 0); + + /* Transaction table tab indices */ + enum { + AllTransactions = 0, + SentReceived = 1, + Sent = 2, + Received = 3 + } TabIndex; +private slots: + void sendcoinsClicked(); + void addressbookClicked(); + void optionsClicked(); + void receivingAddressesClicked(); + void aboutClicked(); + + void newAddressClicked(); + void copyClipboardClicked(); +}; + +#endif diff --git a/mainoptionspage.cpp b/mainoptionspage.cpp new file mode 100644 index 0000000000..d833a79307 --- /dev/null +++ b/mainoptionspage.cpp @@ -0,0 +1,67 @@ +#include "MainOptionsPage.h" + +#include +#include +#include +#include +#include + +MainOptionsPage::MainOptionsPage(QWidget *parent): + QWidget(parent) +{ + QVBoxLayout *layout = new QVBoxLayout(); + + QCheckBox *bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup")); + layout->addWidget(bitcoin_at_startup); + + QCheckBox *minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar")); + layout->addWidget(minimize_to_tray); + + QCheckBox *map_port_upnp = new QCheckBox(tr("Map port using &UPnP")); + layout->addWidget(map_port_upnp); + + QCheckBox *minimize_on_close = new QCheckBox(tr("M&inimize on close")); + layout->addWidget(minimize_on_close); + + QCheckBox *connect_socks4 = new QCheckBox(tr("&Connect through socks4 proxy:")); + layout->addWidget(connect_socks4); + + QHBoxLayout *proxy_hbox = new QHBoxLayout(); + proxy_hbox->addSpacing(18); + QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP: ")); + proxy_hbox->addWidget(proxy_ip_label); + QLineEdit *proxy_ip = new QLineEdit(); + proxy_ip->setMaximumWidth(140); + proxy_ip_label->setBuddy(proxy_ip); + proxy_hbox->addWidget(proxy_ip); + QLabel *proxy_port_label = new QLabel(tr("&Port: ")); + proxy_hbox->addWidget(proxy_port_label); + QLineEdit *proxy_port = new QLineEdit(); + proxy_port->setMaximumWidth(55); + proxy_port_label->setBuddy(proxy_port); + proxy_hbox->addWidget(proxy_port); + proxy_hbox->addStretch(1); + + layout->addLayout(proxy_hbox); + QLabel *fee_help = new QLabel(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended.")); + fee_help->setWordWrap(true); + layout->addWidget(fee_help); + + QHBoxLayout *fee_hbox = new QHBoxLayout(); + fee_hbox->addSpacing(18); + QLabel *fee_label = new QLabel(tr("Pay transaction &fee")); + fee_hbox->addWidget(fee_label); + QLineEdit *fee_edit = new QLineEdit(); + fee_edit->setMaximumWidth(70); + fee_label->setBuddy(fee_edit); + fee_hbox->addWidget(fee_edit); + fee_hbox->addStretch(1); + + layout->addLayout(fee_hbox); + + + layout->addStretch(1); /* Extra space at bottom */ + + setLayout(layout); +} + diff --git a/mainoptionspage.h b/mainoptionspage.h new file mode 100644 index 0000000000..de2ef9fcd0 --- /dev/null +++ b/mainoptionspage.h @@ -0,0 +1,18 @@ +#ifndef MAINOPTIONSPAGE_H +#define MAINOPTIONSPAGE_H + +#include + +class MainOptionsPage : public QWidget +{ + Q_OBJECT +public: + explicit MainOptionsPage(QWidget *parent = 0); + +signals: + +public slots: + +}; + +#endif // MAINOPTIONSPAGE_H diff --git a/optionsdialog.cpp b/optionsdialog.cpp new file mode 100644 index 0000000000..a70eadd516 --- /dev/null +++ b/optionsdialog.cpp @@ -0,0 +1,55 @@ +#include "OptionsDialog.h" +#include "MainOptionsPage.h" + +#include +#include +#include + +OptionsDialog::OptionsDialog(QWidget *parent) : + QDialog(parent), contents_widget(0), pages_widget(0) +{ + contents_widget = new QListWidget(); + contents_widget->setMaximumWidth(128); + + pages_widget = new QStackedWidget(); + pages_widget->setMinimumWidth(300); + + QListWidgetItem *item_main = new QListWidgetItem(tr("Main")); + contents_widget->addItem(item_main); + pages_widget->addWidget(new MainOptionsPage(this)); + + contents_widget->setCurrentRow(0); + + QHBoxLayout *main_layout = new QHBoxLayout(); + main_layout->addWidget(contents_widget); + main_layout->addWidget(pages_widget, 1); + + QVBoxLayout *layout = new QVBoxLayout(); + layout->addLayout(main_layout); + + QHBoxLayout *buttons = new QHBoxLayout(); + buttons->addStretch(1); + QPushButton *ok_button = new QPushButton(tr("OK")); + buttons->addWidget(ok_button); + QPushButton *cancel_button = new QPushButton(tr("Cancel")); + buttons->addWidget(cancel_button); + QPushButton *apply_button = new QPushButton(tr("Apply")); + buttons->addWidget(apply_button); + + layout->addLayout(buttons); + + + setLayout(layout); + setWindowTitle(tr("Options")); + + +} + +void OptionsDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) +{ + Q_UNUSED(previous); + if(current) + { + pages_widget->setCurrentIndex(contents_widget->row(current)); + } +} diff --git a/optionsdialog.h b/optionsdialog.h new file mode 100644 index 0000000000..2a4beaccea --- /dev/null +++ b/optionsdialog.h @@ -0,0 +1,25 @@ +#ifndef OPTIONSDIALOG_H +#define OPTIONSDIALOG_H + +#include +#include +#include + +class OptionsDialog : public QDialog +{ + Q_OBJECT +public: + explicit OptionsDialog(QWidget *parent = 0); + +signals: + +public slots: + void changePage(QListWidgetItem *current, QListWidgetItem *previous); +private: + QListWidget *contents_widget; + QStackedWidget *pages_widget; + + void setupMainPage(); +}; + +#endif // OPTIONSDIALOG_H diff --git a/sendcoinsdialog.cpp b/sendcoinsdialog.cpp new file mode 100644 index 0000000000..ef3ade68ac --- /dev/null +++ b/sendcoinsdialog.cpp @@ -0,0 +1,14 @@ +#include "SendCoinsDialog.h" +#include "ui_sendcoinsdialog.h" + +SendCoinsDialog::SendCoinsDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::SendCoinsDialog) +{ + ui->setupUi(this); +} + +SendCoinsDialog::~SendCoinsDialog() +{ + delete ui; +} diff --git a/sendcoinsdialog.h b/sendcoinsdialog.h new file mode 100644 index 0000000000..82fae9cfac --- /dev/null +++ b/sendcoinsdialog.h @@ -0,0 +1,22 @@ +#ifndef SENDCOINSDIALOG_H +#define SENDCOINSDIALOG_H + +#include + +namespace Ui { + class SendCoinsDialog; +} + +class SendCoinsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SendCoinsDialog(QWidget *parent = 0); + ~SendCoinsDialog(); + +private: + Ui::SendCoinsDialog *ui; +}; + +#endif // SENDCOINSDIALOG_H diff --git a/sendcoinsdialog.ui b/sendcoinsdialog.ui new file mode 100644 index 0000000000..56ec6d3d23 --- /dev/null +++ b/sendcoinsdialog.ui @@ -0,0 +1,146 @@ + + + SendCoinsDialog + + + + 0 + 0 + 736 + 129 + + + + Dialog + + + + + + + + &Amount: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + pay_amount + + + + + + + Pay &To: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + pay_to + + + + + + + + + + + 145 + 16777215 + + + + + + + + &Paste + + + + + + + Address &Book... + + + + + + + + 9 + + + + Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SendCoinsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SendCoinsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/transactiontablemodel.cpp b/transactiontablemodel.cpp new file mode 100644 index 0000000000..d36bea211a --- /dev/null +++ b/transactiontablemodel.cpp @@ -0,0 +1,83 @@ +#include "TransactionTableModel.h" + +#include + +/* Credit and Debit columns are right-aligned as they contain numbers */ +static int column_alignments[] = { + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignRight|Qt::AlignVCenter, + Qt::AlignRight|Qt::AlignVCenter + }; + +TransactionTableModel::TransactionTableModel(QObject *parent): + QAbstractTableModel(parent) +{ + columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); +} + +int TransactionTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 5; +} + +int TransactionTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} + +QVariant TransactionTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + + if(role == Qt::DisplayRole) + { + /* index.row(), index.column() */ + /* Return QString */ + return QLocale::system().toString(index.row()); + } else if (role == Qt::TextAlignmentRole) + { + return column_alignments[index.column()]; + } else if (role == Qt::UserRole) + { + /* user role: transaction type for filtering + "s" (sent) + "r" (received) + "g" (generated) + */ + switch(index.row() % 3) + { + case 0: return QString("s"); + case 1: return QString("r"); + case 2: return QString("o"); + } + } + return QVariant(); +} + +QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(role == Qt::DisplayRole) + { + if(orientation == Qt::Horizontal) + { + return columns[section]; + } + } else if (role == Qt::TextAlignmentRole) + { + return column_alignments[section]; + } + return QVariant(); +} + +Qt::ItemFlags TransactionTableModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemIsEnabled; + + return QAbstractTableModel::flags(index); +} diff --git a/transactiontablemodel.h b/transactiontablemodel.h new file mode 100644 index 0000000000..8913f1d94c --- /dev/null +++ b/transactiontablemodel.h @@ -0,0 +1,31 @@ +#ifndef TRANSACTIONTABLEMODEL_H +#define TRANSACTIONTABLEMODEL_H + +#include +#include + +class TransactionTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit TransactionTableModel(QObject *parent = 0); + + enum { + Status = 0, + Date = 1, + Description = 2, + Debit = 3, + Credit = 4 + } ColumnIndex; + + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; +private: + QStringList columns; +}; + +#endif + -- cgit v1.2.3 From 0fd01780e9a5098bd7d3d55e832fe7d01569c705 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 12 May 2011 14:49:42 +0200 Subject: lowercase --- .gitignore | 2 ++ aboutdialog.cpp | 2 +- addresstablemodel.cpp | 2 +- bitcoin.cpp | 2 +- bitcoin.pro | 28 ++++++++++++++-------------- bitcoingui.cpp | 10 +++++----- mainoptionspage.cpp | 2 +- optionsdialog.cpp | 4 ++-- sendcoinsdialog.cpp | 2 +- transactiontablemodel.cpp | 2 +- 10 files changed, 29 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 819c55e3ce..94f5bcbf55 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ moc_*.cpp *.pro.user qrc_*.cpp +ui_*.cpp +ui_*.h diff --git a/aboutdialog.cpp b/aboutdialog.cpp index dd2ec9f9b0..90d74e69a4 100644 --- a/aboutdialog.cpp +++ b/aboutdialog.cpp @@ -1,4 +1,4 @@ -#include "AboutDialog.h" +#include "aboutdialog.h" AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent) diff --git a/addresstablemodel.cpp b/addresstablemodel.cpp index b25ee3e9ee..95ea73260e 100644 --- a/addresstablemodel.cpp +++ b/addresstablemodel.cpp @@ -1,4 +1,4 @@ -#include "AddressTableModel.h" +#include "addresstablemodel.h" AddressTableModel::AddressTableModel(QObject *parent) : QAbstractTableModel(parent) diff --git a/bitcoin.cpp b/bitcoin.cpp index d43befd41d..ebc0570cc4 100644 --- a/bitcoin.cpp +++ b/bitcoin.cpp @@ -1,7 +1,7 @@ /* * W.J. van der Laan 2011 */ -#include "BitcoinGUI.h" +#include "bitcoingui.h" #include diff --git a/bitcoin.pro b/bitcoin.pro index 01d93ddb6c..4e9bfda32e 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -8,21 +8,21 @@ DEPENDPATH += . INCLUDEPATH += . # Input -HEADERS += BitcoinGUI.h \ - TransactionTableModel.h \ - AboutDialog.h \ - AddressTableModel.h \ - OptionsDialog.h \ - MainOptionsPage.h \ - SendCoinsDialog.h \ +HEADERS += bitcoingui.h \ + transactiontablemodel.h \ + aboutdialog.h \ + addresstablemodel.h \ + optionsdialog.h \ + mainoptionspage.h \ + sendcoinsdialog.h \ addressbookdialog.h -SOURCES += bitcoin.cpp BitcoinGUI.cpp \ - TransactionTableModel.cpp \ - AboutDialog.cpp \ - AddressTableModel.cpp \ - OptionsDialog.cpp \ - MainOptionsPage.cpp \ - SendCoinsDialog.cpp \ +SOURCES += bitcoin.cpp bitcoingui.cpp \ + transactiontablemodel.cpp \ + aboutdialog.cpp \ + addresstablemodel.cpp \ + optionsdialog.cpp \ + mainoptionspage.cpp \ + sendcoinsdialog.cpp \ addressbookdialog.cpp RESOURCES += \ diff --git a/bitcoingui.cpp b/bitcoingui.cpp index a3b346ed82..4083afbf8b 100644 --- a/bitcoingui.cpp +++ b/bitcoingui.cpp @@ -3,12 +3,12 @@ * * W.J. van der Laan 2011 */ -#include "BitcoinGUI.h" -#include "TransactionTableModel.h" +#include "bitcoingui.h" +#include "transactiontablemodel.h" #include "addressbookdialog.h" -#include "SendCoinsDialog.h" -#include "OptionsDialog.h" -#include "AboutDialog.h" +#include "sendcoinsdialog.h" +#include "optionsdialog.h" +#include "aboutdialog.h" #include #include diff --git a/mainoptionspage.cpp b/mainoptionspage.cpp index d833a79307..021e1e470f 100644 --- a/mainoptionspage.cpp +++ b/mainoptionspage.cpp @@ -1,4 +1,4 @@ -#include "MainOptionsPage.h" +#include "mainoptionspage.h" #include #include diff --git a/optionsdialog.cpp b/optionsdialog.cpp index a70eadd516..e609e54420 100644 --- a/optionsdialog.cpp +++ b/optionsdialog.cpp @@ -1,5 +1,5 @@ -#include "OptionsDialog.h" -#include "MainOptionsPage.h" +#include "optionsdialog.h" +#include "mainoptionspage.h" #include #include diff --git a/sendcoinsdialog.cpp b/sendcoinsdialog.cpp index ef3ade68ac..283039f2b3 100644 --- a/sendcoinsdialog.cpp +++ b/sendcoinsdialog.cpp @@ -1,4 +1,4 @@ -#include "SendCoinsDialog.h" +#include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" SendCoinsDialog::SendCoinsDialog(QWidget *parent) : diff --git a/transactiontablemodel.cpp b/transactiontablemodel.cpp index d36bea211a..5a84d2ea83 100644 --- a/transactiontablemodel.cpp +++ b/transactiontablemodel.cpp @@ -1,4 +1,4 @@ -#include "TransactionTableModel.h" +#include "transactiontablemodel.h" #include -- cgit v1.2.3 From 3a7abc2c77febe46e9af1423e19a6354e6eafcb4 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 12 May 2011 17:55:24 +0200 Subject: update --- TODO | 6 ++ aboutdialog.cpp | 15 ++++- aboutdialog.h | 13 +++- aboutdialog.ui | 162 +++++++++++++++++++++++++++++++++++++++++++++ addressbookdialog.cpp | 20 ++++++ addressbookdialog.h | 6 ++ addressbookdialog.ui | 139 +++++++++++++++++++++++--------------- bitcoin.pro | 11 +-- bitcoin.qrc | 3 + bitcoingui.cpp | 2 +- res/icons/address-book.png | Bin 656 -> 1211 bytes res/icons/send.png | Bin 938 -> 1485 bytes res/images/about.png | Bin 0 -> 3488 bytes sendcoinsdialog.cpp | 23 +++++++ sendcoinsdialog.h | 6 ++ sendcoinsdialog.ui | 80 ++++++++++------------ 16 files changed, 375 insertions(+), 111 deletions(-) create mode 100644 aboutdialog.ui create mode 100644 res/images/about.png diff --git a/TODO b/TODO index 3d1785cda1..0cc78f1193 100644 --- a/TODO +++ b/TODO @@ -54,3 +54,9 @@ AboutDialog - Toolbar icon - 'notify' on incoming transaction + +- AddressTableModel + - Name / Label + - Address + - Delete / Copy to clipboard based on tab + diff --git a/aboutdialog.cpp b/aboutdialog.cpp index 90d74e69a4..3d7a3f98af 100644 --- a/aboutdialog.cpp +++ b/aboutdialog.cpp @@ -1,6 +1,19 @@ #include "aboutdialog.h" +#include "ui_aboutdialog.h" AboutDialog::AboutDialog(QWidget *parent) : - QDialog(parent) + QDialog(parent), + ui(new Ui::AboutDialog) { + ui->setupUi(this); +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} + +void AboutDialog::on_buttonBox_accepted() +{ + close(); } diff --git a/aboutdialog.h b/aboutdialog.h index 1372121099..827cc741c3 100644 --- a/aboutdialog.h +++ b/aboutdialog.h @@ -3,16 +3,23 @@ #include +namespace Ui { + class AboutDialog; +} + class AboutDialog : public QDialog { Q_OBJECT + public: explicit AboutDialog(QWidget *parent = 0); + ~AboutDialog(); -signals: - -public slots: +private: + Ui::AboutDialog *ui; +private slots: + void on_buttonBox_accepted(); }; #endif // ABOUTDIALOG_H diff --git a/aboutdialog.ui b/aboutdialog.ui new file mode 100644 index 0000000000..2cc178fb98 --- /dev/null +++ b/aboutdialog.ui @@ -0,0 +1,162 @@ + + + AboutDialog + + + + 0 + 0 + 593 + 319 + + + + About Bitcoin + + + + + + + 0 + 0 + + + + + + + :/images/about + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + <b>Bitcoin</b> version + + + + + + + 0.3.666-beta + + + Qt::RichText + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Copyright (c) 2009-2011 Bitcoin Developers + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file license.txt or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AboutDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/addressbookdialog.cpp b/addressbookdialog.cpp index ca74159d2f..e1ce1feefc 100644 --- a/addressbookdialog.cpp +++ b/addressbookdialog.cpp @@ -14,6 +14,26 @@ AddressBookDialog::~AddressBookDialog() } void AddressBookDialog::setTab(int tab) +{ + ui->tabWidget->setCurrentIndex(tab); +} + +void AddressBookDialog::on_OKButton_clicked() +{ + accept(); +} + +void AddressBookDialog::on_copyToClipboard_clicked() +{ + +} + +void AddressBookDialog::on_editButton_clicked() +{ + +} + +void AddressBookDialog::on_newAddressButton_clicked() { } diff --git a/addressbookdialog.h b/addressbookdialog.h index a51c02a794..e287019d9b 100644 --- a/addressbookdialog.h +++ b/addressbookdialog.h @@ -23,6 +23,12 @@ public: void setTab(int tab); private: Ui::AddressBookDialog *ui; + +private slots: + void on_newAddressButton_clicked(); + void on_editButton_clicked(); + void on_copyToClipboard_clicked(); + void on_OKButton_clicked(); }; #endif // ADDRESSBOOKDIALOG_H diff --git a/addressbookdialog.ui b/addressbookdialog.ui index e646b08ef2..530322ee00 100644 --- a/addressbookdialog.ui +++ b/addressbookdialog.ui @@ -6,72 +6,101 @@ 0 0 - 400 - 300 + 591 + 347 - Dialog + Address Book - - - Qt::Vertical + + + 1 - - - 20 - 40 - - - + + + Sending + + + + + + + + + + Receiving + + + + + + These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. The highlighted address is displayed in the main window. + + + Qt::AutoText + + + true + + + + + + + + + - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Copy to Clipboard + + + + + + + Edit... + + + + + + + New Address... + + + + + + + OK + + + + - - - buttonBox - accepted() - AddressBookDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - AddressBookDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - + diff --git a/bitcoin.pro b/bitcoin.pro index 4e9bfda32e..7bcf3afded 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -10,24 +10,25 @@ INCLUDEPATH += . # Input HEADERS += bitcoingui.h \ transactiontablemodel.h \ - aboutdialog.h \ addresstablemodel.h \ optionsdialog.h \ mainoptionspage.h \ sendcoinsdialog.h \ - addressbookdialog.h + addressbookdialog.h \ + aboutdialog.h SOURCES += bitcoin.cpp bitcoingui.cpp \ transactiontablemodel.cpp \ - aboutdialog.cpp \ addresstablemodel.cpp \ optionsdialog.cpp \ mainoptionspage.cpp \ sendcoinsdialog.cpp \ - addressbookdialog.cpp + addressbookdialog.cpp \ + aboutdialog.cpp RESOURCES += \ bitcoin.qrc FORMS += \ sendcoinsdialog.ui \ - addressbookdialog.ui + addressbookdialog.ui \ + aboutdialog.ui diff --git a/bitcoin.qrc b/bitcoin.qrc index ebce276c8d..0d91bbcd40 100644 --- a/bitcoin.qrc +++ b/bitcoin.qrc @@ -5,4 +5,7 @@ res/icons/quit.png res/icons/send.png + + res/images/about.png + diff --git a/bitcoingui.cpp b/bitcoingui.cpp index 4083afbf8b..c78bb3c5ac 100644 --- a/bitcoingui.cpp +++ b/bitcoingui.cpp @@ -41,7 +41,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QAction *quit = new QAction(QIcon(":/icons/quit"), tr("&Quit"), this); QAction *sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); - QAction *addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address book"), this); + QAction *addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); QAction *about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); QAction *receiving_addresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); QAction *options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); diff --git a/res/icons/address-book.png b/res/icons/address-book.png index 621ca40245..abfb3c3a51 100644 Binary files a/res/icons/address-book.png and b/res/icons/address-book.png differ diff --git a/res/icons/send.png b/res/icons/send.png index 51067a192b..0ba5359d7b 100644 Binary files a/res/icons/send.png and b/res/icons/send.png differ diff --git a/res/images/about.png b/res/images/about.png new file mode 100644 index 0000000000..c9ab9511ef Binary files /dev/null and b/res/images/about.png differ diff --git a/sendcoinsdialog.cpp b/sendcoinsdialog.cpp index 283039f2b3..1eaa35884b 100644 --- a/sendcoinsdialog.cpp +++ b/sendcoinsdialog.cpp @@ -1,6 +1,8 @@ #include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" +#include "addressbookdialog.h" + SendCoinsDialog::SendCoinsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::SendCoinsDialog) @@ -12,3 +14,24 @@ SendCoinsDialog::~SendCoinsDialog() { delete ui; } + +void SendCoinsDialog::on_sendButton_clicked() +{ + accept(); +} + +void SendCoinsDialog::on_cancelButton_clicked() +{ + reject(); +} + +void SendCoinsDialog::on_pasteButton_clicked() +{ + +} + +void SendCoinsDialog::on_addressBookButton_clicked() +{ + AddressBookDialog dlg; + dlg.exec(); +} diff --git a/sendcoinsdialog.h b/sendcoinsdialog.h index 82fae9cfac..e3ffd1d33d 100644 --- a/sendcoinsdialog.h +++ b/sendcoinsdialog.h @@ -17,6 +17,12 @@ public: private: Ui::SendCoinsDialog *ui; + +private slots: + void on_addressBookButton_clicked(); + void on_pasteButton_clicked(); + void on_cancelButton_clicked(); + void on_sendButton_clicked(); }; #endif // SENDCOINSDIALOG_H diff --git a/sendcoinsdialog.ui b/sendcoinsdialog.ui index 56ec6d3d23..b1adbc5159 100644 --- a/sendcoinsdialog.ui +++ b/sendcoinsdialog.ui @@ -7,11 +7,11 @@ 0 0 736 - 129 + 140 - Dialog + Send Coins @@ -56,14 +56,14 @@ - + &Paste - + Address &Book... @@ -97,50 +97,38 @@ - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Send + + + + + + + Cancel + + + + - - - buttonBox - accepted() - SendCoinsDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - SendCoinsDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - + -- cgit v1.2.3 From d5da2f7b375375f5e834f18e66820025267bd4ed Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 12 May 2011 18:27:20 +0200 Subject: rename UI controls --- aboutdialog.ui | 10 +++++----- addressbookdialog.ui | 8 ++++---- sendcoinsdialog.ui | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/aboutdialog.ui b/aboutdialog.ui index 2cc178fb98..17a15dd076 100644 --- a/aboutdialog.ui +++ b/aboutdialog.ui @@ -55,7 +55,7 @@ - + 0.3.666-beta @@ -133,8 +133,8 @@ This product includes software developed by the OpenSSL Project for use in the O accept() - 248 - 254 + 360 + 308 157 @@ -149,8 +149,8 @@ This product includes software developed by the OpenSSL Project for use in the O reject() - 316 - 260 + 428 + 308 286 diff --git a/addressbookdialog.ui b/addressbookdialog.ui index 530322ee00..8620a1cc72 100644 --- a/addressbookdialog.ui +++ b/addressbookdialog.ui @@ -19,17 +19,17 @@ 1 - + Sending - + - + Receiving @@ -48,7 +48,7 @@ - + diff --git a/sendcoinsdialog.ui b/sendcoinsdialog.ui index b1adbc5159..ef7eaf37c4 100644 --- a/sendcoinsdialog.ui +++ b/sendcoinsdialog.ui @@ -25,7 +25,7 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - pay_amount + payAmount @@ -38,15 +38,15 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - pay_to + payTo - + - + 145 -- cgit v1.2.3 From 3f323a61feebccda9c7d7c66ade3dd21d7cbddf5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 12 May 2011 20:16:42 +0200 Subject: clipboard handling --- bitcoin.cpp | 2 + bitcoingui.cpp | 135 +++++++++++++++++++++++++++++++--------------------- bitcoingui.h | 26 ++++++++++ sendcoinsdialog.cpp | 6 ++- 4 files changed, 115 insertions(+), 54 deletions(-) diff --git a/bitcoin.cpp b/bitcoin.cpp index ebc0570cc4..ad9835628b 100644 --- a/bitcoin.cpp +++ b/bitcoin.cpp @@ -13,5 +13,7 @@ int main(int argc, char *argv[]) window.show(); + /* Depending on settings: QApplication::setQuitOnLastWindowClosed(false); */ + return app.exec(); } diff --git a/bitcoingui.cpp b/bitcoingui.cpp index c78bb3c5ac..01b18f1b81 100644 --- a/bitcoingui.cpp +++ b/bitcoingui.cpp @@ -27,24 +27,20 @@ #include #include #include +#include #include #include BitcoinGUI::BitcoinGUI(QWidget *parent): - QMainWindow(parent) + QMainWindow(parent), trayIcon(0) { resize(850, 550); setWindowTitle(tr("Bitcoin")); setWindowIcon(QIcon(":icons/bitcoin")); - - QAction *quit = new QAction(QIcon(":/icons/quit"), tr("&Quit"), this); - QAction *sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); - QAction *addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); - QAction *about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); - QAction *receiving_addresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); - QAction *options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); + + createActions(); /* Menus */ QMenu *file = menuBar()->addMenu("&File"); @@ -68,9 +64,10 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): /* Address:
: New... : Paste to clipboard */ QHBoxLayout *hbox_address = new QHBoxLayout(); hbox_address->addWidget(new QLabel(tr("Your Bitcoin Address:"))); - QLineEdit *edit_address = new QLineEdit(); - edit_address->setReadOnly(true); - hbox_address->addWidget(edit_address); + address = new QLineEdit(); + address->setReadOnly(true); + address->setText("0123456789"); + hbox_address->addWidget(address); QPushButton *button_new = new QPushButton(tr("&New...")); QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); @@ -82,19 +79,84 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): hbox_balance->addWidget(new QLabel(tr("Balance:"))); hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ - QLabel *label_balance = new QLabel(QLocale::system().toString(1345.54)); - label_balance->setFont(QFont("Teletype")); - hbox_balance->addWidget(label_balance); + labelBalance = new QLabel(QLocale::system().toString(1345.54)); + labelBalance->setFont(QFont("Teletype")); + hbox_balance->addWidget(labelBalance); hbox_balance->addStretch(1); - /* Tab widget */ QVBoxLayout *vbox = new QVBoxLayout(); vbox->addLayout(hbox_address); vbox->addLayout(hbox_balance); - TransactionTableModel *transaction_model = new TransactionTableModel(this); + transaction_model = new TransactionTableModel(this); + + vbox->addWidget(createTabs()); - /* setupTabs */ + QWidget *centralwidget = new QWidget(this); + centralwidget->setLayout(vbox); + setCentralWidget(centralwidget); + + /* Create status bar */ + statusBar(); + + QLabel *label_connections = new QLabel("6 connections"); + label_connections->setFrameStyle(QFrame::Panel | QFrame::Sunken); + label_connections->setMinimumWidth(100); + + QLabel *label_blocks = new QLabel("6 blocks"); + label_blocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); + label_blocks->setMinimumWidth(100); + + QLabel *label_transactions = new QLabel("6 transactions"); + label_transactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); + label_transactions->setMinimumWidth(100); + + statusBar()->addPermanentWidget(label_connections); + statusBar()->addPermanentWidget(label_blocks); + statusBar()->addPermanentWidget(label_transactions); + + /* Action bindings */ + connect(button_new, SIGNAL(clicked()), this, SLOT(newAddressClicked())); + connect(button_clipboard, SIGNAL(clicked()), this, SLOT(copyClipboardClicked())); + + createTrayIcon(); +} + +void BitcoinGUI::createActions() +{ + quit = new QAction(QIcon(":/icons/quit"), tr("&Exit"), this); + sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); + addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); + about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); + receiving_addresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); + options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); + openBitCoin = new QAction(QIcon(":/icons/bitcoin"), "Open Bitcoin", this); + + connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked())); + connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked())); + connect(receiving_addresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked())); + connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); + connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); +} + +void BitcoinGUI::createTrayIcon() +{ + QMenu *trayIconMenu = new QMenu(this); + trayIconMenu->addAction(openBitCoin); + trayIconMenu->addAction(sendcoins); + trayIconMenu->addAction(options); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(quit); + + trayIcon = new QSystemTrayIcon(this); + trayIcon->setContextMenu(trayIconMenu); + trayIcon->setIcon(QIcon(":/icons/bitcoin")); + trayIcon->show(); +} + +QWidget *BitcoinGUI::createTabs() +{ QStringList tab_filters, tab_labels; tab_filters << "^." << "^[sr]" @@ -133,41 +195,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): tabs->addTab(transaction_table, tab_labels.at(i)); } - - vbox->addWidget(tabs); - - QWidget *centralwidget = new QWidget(this); - centralwidget->setLayout(vbox); - setCentralWidget(centralwidget); - - /* Status bar */ - statusBar(); - - QLabel *label_connections = new QLabel("6 connections"); - label_connections->setFrameStyle(QFrame::Panel | QFrame::Sunken); - label_connections->setMinimumWidth(100); - - QLabel *label_blocks = new QLabel("6 blocks"); - label_blocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); - label_blocks->setMinimumWidth(100); - - QLabel *label_transactions = new QLabel("6 transactions"); - label_transactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); - label_transactions->setMinimumWidth(100); - - statusBar()->addPermanentWidget(label_connections); - statusBar()->addPermanentWidget(label_blocks); - statusBar()->addPermanentWidget(label_transactions); - - /* Action bindings */ - connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); - connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked())); - connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked())); - connect(receiving_addresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked())); - connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); - connect(button_new, SIGNAL(clicked()), this, SLOT(newAddressClicked())); - connect(button_clipboard, SIGNAL(clicked()), this, SLOT(copyClipboardClicked())); - connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); + return tabs; } void BitcoinGUI::sendcoinsClicked() @@ -216,5 +244,6 @@ void BitcoinGUI::newAddressClicked() void BitcoinGUI::copyClipboardClicked() { qDebug() << "Copy to clipboard"; - /* TODO: copy to clipboard */ + /* Copy text in address to clipboard */ + QApplication::clipboard()->setText(address->text()); } diff --git a/bitcoingui.h b/bitcoingui.h index 0c9edad85c..6e1003cd50 100644 --- a/bitcoingui.h +++ b/bitcoingui.h @@ -2,6 +2,12 @@ #define BITCOINGUI_H #include +#include + +/* Forward declarations */ +class TransactionTableModel; +class QLabel; +class QLineEdit; class BitcoinGUI : public QMainWindow { @@ -16,6 +22,26 @@ public: Sent = 2, Received = 3 } TabIndex; +private: + TransactionTableModel *transaction_model; + + QLineEdit *address; + QLabel *labelBalance; + + QAction *quit; + QAction *sendcoins; + QAction *addressbook; + QAction *about; + QAction *receiving_addresses; + QAction *options; + QAction *openBitCoin; + + QSystemTrayIcon *trayIcon; + + void createActions(); + QWidget *createTabs(); + void createTrayIcon(); + private slots: void sendcoinsClicked(); void addressbookClicked(); diff --git a/sendcoinsdialog.cpp b/sendcoinsdialog.cpp index 1eaa35884b..d8ef781290 100644 --- a/sendcoinsdialog.cpp +++ b/sendcoinsdialog.cpp @@ -3,6 +3,9 @@ #include "addressbookdialog.h" +#include +#include + SendCoinsDialog::SendCoinsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::SendCoinsDialog) @@ -27,7 +30,8 @@ void SendCoinsDialog::on_cancelButton_clicked() void SendCoinsDialog::on_pasteButton_clicked() { - + /* Paste text from clipboard into recipient field */ + ui->payTo->setText(QApplication::clipboard()->text()); } void SendCoinsDialog::on_addressBookButton_clicked() -- cgit v1.2.3 From 1a6d504a382e84102b7e7009b08c920a345584f7 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 12 May 2011 20:28:07 +0200 Subject: make balance/blocks/connections/transactions settable through slots --- bitcoin.cpp | 4 ++++ bitcoingui.cpp | 48 ++++++++++++++++++++++++++++++++++-------------- bitcoingui.h | 9 +++++++++ 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/bitcoin.cpp b/bitcoin.cpp index ad9835628b..456ce1e923 100644 --- a/bitcoin.cpp +++ b/bitcoin.cpp @@ -10,6 +10,10 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); BitcoinGUI window; + window.setBalance(1234.567890); + window.setNumConnections(4); + window.setNumTransactions(4); + window.setNumBlocks(33); window.show(); diff --git a/bitcoingui.cpp b/bitcoingui.cpp index 01b18f1b81..771b902667 100644 --- a/bitcoingui.cpp +++ b/bitcoingui.cpp @@ -66,7 +66,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): hbox_address->addWidget(new QLabel(tr("Your Bitcoin Address:"))); address = new QLineEdit(); address->setReadOnly(true); - address->setText("0123456789"); + address->setText("0123456789"); /* test */ hbox_address->addWidget(address); QPushButton *button_new = new QPushButton(tr("&New...")); @@ -79,7 +79,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): hbox_balance->addWidget(new QLabel(tr("Balance:"))); hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ - labelBalance = new QLabel(QLocale::system().toString(1345.54)); + labelBalance = new QLabel(); labelBalance->setFont(QFont("Teletype")); hbox_balance->addWidget(labelBalance); hbox_balance->addStretch(1); @@ -99,21 +99,21 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): /* Create status bar */ statusBar(); - QLabel *label_connections = new QLabel("6 connections"); - label_connections->setFrameStyle(QFrame::Panel | QFrame::Sunken); - label_connections->setMinimumWidth(100); + labelConnections = new QLabel(); + labelConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); + labelConnections->setMinimumWidth(130); - QLabel *label_blocks = new QLabel("6 blocks"); - label_blocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); - label_blocks->setMinimumWidth(100); + labelBlocks = new QLabel(); + labelBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); + labelBlocks->setMinimumWidth(130); - QLabel *label_transactions = new QLabel("6 transactions"); - label_transactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); - label_transactions->setMinimumWidth(100); + labelTransactions = new QLabel(); + labelTransactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); + labelTransactions->setMinimumWidth(130); - statusBar()->addPermanentWidget(label_connections); - statusBar()->addPermanentWidget(label_blocks); - statusBar()->addPermanentWidget(label_transactions); + statusBar()->addPermanentWidget(labelConnections); + statusBar()->addPermanentWidget(labelBlocks); + statusBar()->addPermanentWidget(labelTransactions); /* Action bindings */ connect(button_new, SIGNAL(clicked()), this, SLOT(newAddressClicked())); @@ -247,3 +247,23 @@ void BitcoinGUI::copyClipboardClicked() /* Copy text in address to clipboard */ QApplication::clipboard()->setText(address->text()); } + +void BitcoinGUI::setBalance(double balance) +{ + labelBalance->setText(QLocale::system().toString(balance, 8)); +} + +void BitcoinGUI::setNumConnections(int count) +{ + labelConnections->setText(QLocale::system().toString(count)+" "+tr("connections")); +} + +void BitcoinGUI::setNumBlocks(int count) +{ + labelBlocks->setText(QLocale::system().toString(count)+" "+tr("blocks")); +} + +void BitcoinGUI::setNumTransactions(int count) +{ + labelTransactions->setText(QLocale::system().toString(count)+" "+tr("transactions")); +} diff --git a/bitcoingui.h b/bitcoingui.h index 6e1003cd50..89e3897a6c 100644 --- a/bitcoingui.h +++ b/bitcoingui.h @@ -27,6 +27,9 @@ private: QLineEdit *address; QLabel *labelBalance; + QLabel *labelConnections; + QLabel *labelBlocks; + QLabel *labelTransactions; QAction *quit; QAction *sendcoins; @@ -42,6 +45,12 @@ private: QWidget *createTabs(); void createTrayIcon(); +public slots: + void setBalance(double balance); + void setNumConnections(int count); + void setNumBlocks(int count); + void setNumTransactions(int count); + private slots: void sendcoinsClicked(); void addressbookClicked(); -- cgit v1.2.3 From 871f9979c66f0e5f1e9167dca22297d9fae07ae6 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 13 May 2011 08:30:20 +0200 Subject: make address settable from outside --- bitcoin.cpp | 1 + bitcoingui.cpp | 6 +++++- bitcoingui.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/bitcoin.cpp b/bitcoin.cpp index 456ce1e923..403e4c517b 100644 --- a/bitcoin.cpp +++ b/bitcoin.cpp @@ -14,6 +14,7 @@ int main(int argc, char *argv[]) window.setNumConnections(4); window.setNumTransactions(4); window.setNumBlocks(33); + window.setAddress("123456789"); window.show(); diff --git a/bitcoingui.cpp b/bitcoingui.cpp index 771b902667..072dc9291f 100644 --- a/bitcoingui.cpp +++ b/bitcoingui.cpp @@ -66,7 +66,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): hbox_address->addWidget(new QLabel(tr("Your Bitcoin Address:"))); address = new QLineEdit(); address->setReadOnly(true); - address->setText("0123456789"); /* test */ hbox_address->addWidget(address); QPushButton *button_new = new QPushButton(tr("&New...")); @@ -253,6 +252,11 @@ void BitcoinGUI::setBalance(double balance) labelBalance->setText(QLocale::system().toString(balance, 8)); } +void BitcoinGUI::setAddress(const QString &addr) +{ + address->setText(addr); +} + void BitcoinGUI::setNumConnections(int count) { labelConnections->setText(QLocale::system().toString(count)+" "+tr("connections")); diff --git a/bitcoingui.h b/bitcoingui.h index 89e3897a6c..12bbf58ba5 100644 --- a/bitcoingui.h +++ b/bitcoingui.h @@ -47,6 +47,7 @@ private: public slots: void setBalance(double balance); + void setAddress(const QString &address); void setNumConnections(int count); void setNumBlocks(int count); void setNumTransactions(int count); -- cgit v1.2.3 From b8e302eb538fd7f4131452c92d22300928f19a5a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 13 May 2011 15:58:27 +0200 Subject: improve address book, add less conspicious toolbar icon --- TODO | 2 ++ addressbookdialog.cpp | 55 ++++++++++++++++++++++++++++++--- addressbookdialog.h | 3 ++ addressbookdialog.ui | 53 +++++++++++++++++++++++++++++-- addresstablemodel.cpp | 51 ++++++++++++++++++++++++++++++ addresstablemodel.h | 14 +++++++++ bitcoin.pro | 9 ++++-- bitcoin.qrc | 1 + bitcoingui.cpp | 10 +++--- editaddressdialog.cpp | 14 +++++++++ editaddressdialog.h | 22 +++++++++++++ editaddressdialog.ui | 77 ++++++++++++++++++++++++++++++++++++++++++++++ res/icons/toolbar.png | Bin 0 -> 968 bytes transactiontablemodel.cpp | 9 ++++-- transactiontablemodel.h | 8 ++++- 15 files changed, 311 insertions(+), 17 deletions(-) create mode 100644 editaddressdialog.cpp create mode 100644 editaddressdialog.h create mode 100644 editaddressdialog.ui create mode 100644 res/icons/toolbar.png diff --git a/TODO b/TODO index 0cc78f1193..4f81ec95f6 100644 --- a/TODO +++ b/TODO @@ -60,3 +60,5 @@ AboutDialog - Address - Delete / Copy to clipboard based on tab +- Make icon blend into taskbar and maybe silver/grey + Same for the other icons? diff --git a/addressbookdialog.cpp b/addressbookdialog.cpp index e1ce1feefc..72b0da35f1 100644 --- a/addressbookdialog.cpp +++ b/addressbookdialog.cpp @@ -1,11 +1,20 @@ #include "addressbookdialog.h" #include "ui_addressbookdialog.h" +#include "addresstablemodel.h" +#include "editaddressdialog.h" + +#include + AddressBookDialog::AddressBookDialog(QWidget *parent) : QDialog(parent), - ui(new Ui::AddressBookDialog) + ui(new Ui::AddressBookDialog), + model(0) { ui->setupUi(this); + + model = new AddressTableModel(this); + setModel(model); } AddressBookDialog::~AddressBookDialog() @@ -13,6 +22,41 @@ AddressBookDialog::~AddressBookDialog() delete ui; } +void AddressBookDialog::setModel(AddressTableModel *model) +{ + /* Receive filter */ + QSortFilterProxyModel *receive_model = new QSortFilterProxyModel(this); + receive_model->setSourceModel(model); + receive_model->setDynamicSortFilter(true); + receive_model->setFilterRole(Qt::UserRole); + receive_model->setFilterKeyColumn(AddressTableModel::Type); + receive_model->setFilterFixedString(AddressTableModel::Receive); + ui->receiveTableView->setModel(receive_model); + + /* Send filter */ + QSortFilterProxyModel *send_model = new QSortFilterProxyModel(this); + send_model->setSourceModel(model); + send_model->setDynamicSortFilter(true); + send_model->setFilterRole(Qt::UserRole); + send_model->setFilterKeyColumn(AddressTableModel::Type); + send_model->setFilterFixedString(AddressTableModel::Send); + ui->sendTableView->setModel(send_model); + + /* Set column widths */ + ui->receiveTableView->horizontalHeader()->resizeSection( + AddressTableModel::Address, 320); + ui->receiveTableView->horizontalHeader()->setResizeMode( + AddressTableModel::Label, QHeaderView::Stretch); + ui->sendTableView->horizontalHeader()->resizeSection( + AddressTableModel::Address, 320); + ui->sendTableView->horizontalHeader()->setResizeMode( + AddressTableModel::Label, QHeaderView::Stretch); + + /* Hide "Type" column in both views as it is only used for filtering */ + ui->receiveTableView->setColumnHidden(AddressTableModel::Type, true); + ui->sendTableView->setColumnHidden(AddressTableModel::Type, true); +} + void AddressBookDialog::setTab(int tab) { ui->tabWidget->setCurrentIndex(tab); @@ -25,15 +69,18 @@ void AddressBookDialog::on_OKButton_clicked() void AddressBookDialog::on_copyToClipboard_clicked() { - + /* Copy currently selected address to clipboard */ } void AddressBookDialog::on_editButton_clicked() { - + /* Double click should trigger edit button */ + EditAddressDialog dlg; + dlg.exec(); } void AddressBookDialog::on_newAddressButton_clicked() { - + EditAddressDialog dlg; + dlg.exec(); } diff --git a/addressbookdialog.h b/addressbookdialog.h index e287019d9b..d075c9ba08 100644 --- a/addressbookdialog.h +++ b/addressbookdialog.h @@ -6,6 +6,7 @@ namespace Ui { class AddressBookDialog; } +class AddressTableModel; class AddressBookDialog : public QDialog { @@ -20,9 +21,11 @@ public: ReceivingTab = 1 } Tabs; + void setModel(AddressTableModel *model); void setTab(int tab); private: Ui::AddressBookDialog *ui; + AddressTableModel *model; private slots: void on_newAddressButton_clicked(); diff --git a/addressbookdialog.ui b/addressbookdialog.ui index 8620a1cc72..0aa093e056 100644 --- a/addressbookdialog.ui +++ b/addressbookdialog.ui @@ -25,7 +25,14 @@ - + + + QAbstractItemView::SelectRows + + + false + + @@ -48,7 +55,14 @@ - + + + QAbstractItemView::SelectRows + + + false + + @@ -102,5 +116,38 @@ - + + + receiveTableView + doubleClicked(QModelIndex) + editButton + click() + + + 334 + 249 + + + 333 + 326 + + + + + sendTableView + doubleClicked(QModelIndex) + editButton + click() + + + 329 + 261 + + + 332 + 326 + + + + diff --git a/addresstablemodel.cpp b/addresstablemodel.cpp index 95ea73260e..e8746c2b81 100644 --- a/addresstablemodel.cpp +++ b/addresstablemodel.cpp @@ -1,6 +1,57 @@ #include "addresstablemodel.h" +const QString AddressTableModel::Send = "S"; +const QString AddressTableModel::Receive = "R"; + AddressTableModel::AddressTableModel(QObject *parent) : QAbstractTableModel(parent) { + +} + +int AddressTableModel::rowCount(const QModelIndex &parent) const +{ + return 5; +} + +int AddressTableModel::columnCount(const QModelIndex &parent) const +{ + return 3; +} + +QVariant AddressTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + + if(role == Qt::DisplayRole) + { + /* index.row(), index.column() */ + /* Return QString */ + if(index.column() == Address) + return "1PC9aZC4hNX2rmmrt7uHTfYAS3hRbph4UN"; + else + return "Description"; + } else if (role == Qt::UserRole) + { + switch(index.row() % 2) + { + case 0: return Send; + case 1: return Receive; + } + } + return QVariant(); +} + +QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + return QVariant(); +} + +Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemIsEnabled; + + return QAbstractTableModel::flags(index); } diff --git a/addresstablemodel.h b/addresstablemodel.h index 490452e11e..50ed80dd39 100644 --- a/addresstablemodel.h +++ b/addresstablemodel.h @@ -9,6 +9,20 @@ class AddressTableModel : public QAbstractTableModel public: explicit AddressTableModel(QObject *parent = 0); + enum { + Label = 0, /* User specified label */ + Address = 1, /* Bitcoin address */ + Type = 2 /* Send/Receive, used for filter */ + } ColumnIndex; + + static const QString Send; /* Send addres */ + static const QString Receive; /* Receive address */ + + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; signals: public slots: diff --git a/bitcoin.pro b/bitcoin.pro index 7bcf3afded..d0b9512b95 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -15,7 +15,8 @@ HEADERS += bitcoingui.h \ mainoptionspage.h \ sendcoinsdialog.h \ addressbookdialog.h \ - aboutdialog.h + aboutdialog.h \ + editaddressdialog.h SOURCES += bitcoin.cpp bitcoingui.cpp \ transactiontablemodel.cpp \ addresstablemodel.cpp \ @@ -23,7 +24,8 @@ SOURCES += bitcoin.cpp bitcoingui.cpp \ mainoptionspage.cpp \ sendcoinsdialog.cpp \ addressbookdialog.cpp \ - aboutdialog.cpp + aboutdialog.cpp \ + editaddressdialog.cpp RESOURCES += \ bitcoin.qrc @@ -31,4 +33,5 @@ RESOURCES += \ FORMS += \ sendcoinsdialog.ui \ addressbookdialog.ui \ - aboutdialog.ui + aboutdialog.ui \ + editaddressdialog.ui diff --git a/bitcoin.qrc b/bitcoin.qrc index 0d91bbcd40..80904b341c 100644 --- a/bitcoin.qrc +++ b/bitcoin.qrc @@ -4,6 +4,7 @@ res/icons/bitcoin.png res/icons/quit.png res/icons/send.png + res/icons/toolbar.png res/images/about.png diff --git a/bitcoingui.cpp b/bitcoingui.cpp index 072dc9291f..4af703cf8d 100644 --- a/bitcoingui.cpp +++ b/bitcoingui.cpp @@ -150,7 +150,7 @@ void BitcoinGUI::createTrayIcon() trayIcon = new QSystemTrayIcon(this); trayIcon->setContextMenu(trayIconMenu); - trayIcon->setIcon(QIcon(":/icons/bitcoin")); + trayIcon->setIcon(QIcon(":/icons/toolbar")); trayIcon->show(); } @@ -158,9 +158,9 @@ QWidget *BitcoinGUI::createTabs() { QStringList tab_filters, tab_labels; tab_filters << "^." - << "^[sr]" - << "^[s]" - << "^[r]"; + << "^["+TransactionTableModel::Sent+TransactionTableModel::Received+"]" + << "^["+TransactionTableModel::Sent+"]" + << "^["+TransactionTableModel::Received+"]"; tab_labels << tr("All transactions") << tr("Sent/Received") << tr("Sent") @@ -173,6 +173,7 @@ QWidget *BitcoinGUI::createTabs() proxy_model->setSourceModel(transaction_model); proxy_model->setDynamicSortFilter(true); proxy_model->setFilterRole(Qt::UserRole); + proxy_model->setFilterKeyColumn(TransactionTableModel::Type); proxy_model->setFilterRegExp(QRegExp(tab_filters.at(i))); QTableView *transaction_table = new QTableView(this); @@ -191,6 +192,7 @@ QWidget *BitcoinGUI::createTabs() TransactionTableModel::Debit, 79); transaction_table->horizontalHeader()->resizeSection( TransactionTableModel::Credit, 79); + transaction_table->setColumnHidden(TransactionTableModel::Type, true); tabs->addTab(transaction_table, tab_labels.at(i)); } diff --git a/editaddressdialog.cpp b/editaddressdialog.cpp new file mode 100644 index 0000000000..bd5559792a --- /dev/null +++ b/editaddressdialog.cpp @@ -0,0 +1,14 @@ +#include "editaddressdialog.h" +#include "ui_editaddressdialog.h" + +EditAddressDialog::EditAddressDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::EditAddressDialog) +{ + ui->setupUi(this); +} + +EditAddressDialog::~EditAddressDialog() +{ + delete ui; +} diff --git a/editaddressdialog.h b/editaddressdialog.h new file mode 100644 index 0000000000..650ed534a0 --- /dev/null +++ b/editaddressdialog.h @@ -0,0 +1,22 @@ +#ifndef EDITADDRESSDIALOG_H +#define EDITADDRESSDIALOG_H + +#include + +namespace Ui { + class EditAddressDialog; +} + +class EditAddressDialog : public QDialog +{ + Q_OBJECT + +public: + explicit EditAddressDialog(QWidget *parent = 0); + ~EditAddressDialog(); + +private: + Ui::EditAddressDialog *ui; +}; + +#endif // EDITADDRESSDIALOG_H diff --git a/editaddressdialog.ui b/editaddressdialog.ui new file mode 100644 index 0000000000..2d8aa880f6 --- /dev/null +++ b/editaddressdialog.ui @@ -0,0 +1,77 @@ + + + EditAddressDialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + EditAddressDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + EditAddressDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/res/icons/toolbar.png b/res/icons/toolbar.png new file mode 100644 index 0000000000..f2dcb20636 Binary files /dev/null and b/res/icons/toolbar.png differ diff --git a/transactiontablemodel.cpp b/transactiontablemodel.cpp index 5a84d2ea83..ee58ecba40 100644 --- a/transactiontablemodel.cpp +++ b/transactiontablemodel.cpp @@ -2,19 +2,24 @@ #include +const QString TransactionTableModel::Sent = "s"; +const QString TransactionTableModel::Received = "r"; +const QString TransactionTableModel::Generated = "g"; + /* Credit and Debit columns are right-aligned as they contain numbers */ static int column_alignments[] = { Qt::AlignLeft|Qt::AlignVCenter, Qt::AlignLeft|Qt::AlignVCenter, Qt::AlignLeft|Qt::AlignVCenter, Qt::AlignRight|Qt::AlignVCenter, - Qt::AlignRight|Qt::AlignVCenter + Qt::AlignRight|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter }; TransactionTableModel::TransactionTableModel(QObject *parent): QAbstractTableModel(parent) { - columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); + columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit") << tr("Type"); } int TransactionTableModel::rowCount(const QModelIndex &parent) const diff --git a/transactiontablemodel.h b/transactiontablemodel.h index 8913f1d94c..77ad730673 100644 --- a/transactiontablemodel.h +++ b/transactiontablemodel.h @@ -15,9 +15,15 @@ public: Date = 1, Description = 2, Debit = 3, - Credit = 4 + Credit = 4, + Type = 5 } ColumnIndex; + /* Transaction type */ + static const QString Sent; + static const QString Received; + static const QString Generated; + int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; -- cgit v1.2.3 From 4d1bb15e31d850776d13266262c37aedc8a68d72 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 13 May 2011 22:00:27 +0200 Subject: more improvements --- addressbookdialog.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++--- addressbookdialog.h | 12 ++++++++++- addressbookdialog.ui | 33 ++++++++++++++++++++++------ addresstablemodel.cpp | 2 +- bitcoin.pro | 6 ++++-- bitcoinaddressvalidator.cpp | 8 +++++++ bitcoinaddressvalidator.h | 19 +++++++++++++++++ bitcoingui.h | 3 +++ optionsdialog.cpp | 2 ++ optionsdialog.h | 8 +++++-- sendcoinsdialog.cpp | 14 +++++++----- sendcoinsdialog.h | 2 +- sendcoinsdialog.ui | 45 +++++++++++++++++++++++++++++++++------ 13 files changed, 178 insertions(+), 28 deletions(-) create mode 100644 bitcoinaddressvalidator.cpp create mode 100644 bitcoinaddressvalidator.h diff --git a/addressbookdialog.cpp b/addressbookdialog.cpp index 72b0da35f1..71543f11a7 100644 --- a/addressbookdialog.cpp +++ b/addressbookdialog.cpp @@ -5,6 +5,7 @@ #include "editaddressdialog.h" #include +#include AddressBookDialog::AddressBookDialog(QWidget *parent) : QDialog(parent), @@ -62,14 +63,23 @@ void AddressBookDialog::setTab(int tab) ui->tabWidget->setCurrentIndex(tab); } -void AddressBookDialog::on_OKButton_clicked() +QTableView *AddressBookDialog::getCurrentTable() { - accept(); + switch(ui->tabWidget->currentIndex()) + { + case SendingTab: + return ui->sendTableView; + case ReceivingTab: + return ui->receiveTableView; + default: + return 0; + } } void AddressBookDialog::on_copyToClipboard_clicked() { - /* Copy currently selected address to clipboard */ + /* Copy currently selected address to clipboard */ + } void AddressBookDialog::on_editButton_clicked() @@ -84,3 +94,39 @@ void AddressBookDialog::on_newAddressButton_clicked() EditAddressDialog dlg; dlg.exec(); } + +void AddressBookDialog::on_tabWidget_currentChanged(int index) +{ + switch(index) + { + case SendingTab: + ui->deleteButton->show(); + break; + case ReceivingTab: + ui->deleteButton->hide(); + break; + } +} + +void AddressBookDialog::on_deleteButton_clicked() +{ + QTableView *table = getCurrentTable(); + QModelIndexList indexes = table->selectionModel()->selectedRows(); + + foreach (QModelIndex index, indexes) { + table->model()->removeRow(index.row()); + } +} + +void AddressBookDialog::on_buttonBox_accepted() +{ + QTableView *table = getCurrentTable(); + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) { + QVariant address = table->model()->data(index); + returnValue = address.toString(); + } + + accept(); +} diff --git a/addressbookdialog.h b/addressbookdialog.h index d075c9ba08..bf7c2a65a6 100644 --- a/addressbookdialog.h +++ b/addressbookdialog.h @@ -8,6 +8,10 @@ namespace Ui { } class AddressTableModel; +QT_BEGIN_NAMESPACE +class QTableView; +QT_END_NAMESPACE + class AddressBookDialog : public QDialog { Q_OBJECT @@ -23,15 +27,21 @@ public: void setModel(AddressTableModel *model); void setTab(int tab); + const QString &getReturnValue() const { return returnValue; } private: Ui::AddressBookDialog *ui; AddressTableModel *model; + QString returnValue; + + QTableView *getCurrentTable(); private slots: + void on_buttonBox_accepted(); + void on_deleteButton_clicked(); + void on_tabWidget_currentChanged(int index); void on_newAddressButton_clicked(); void on_editButton_clicked(); void on_copyToClipboard_clicked(); - void on_OKButton_clicked(); }; #endif // ADDRESSBOOKDIALOG_H diff --git a/addressbookdialog.ui b/addressbookdialog.ui index 0aa093e056..d66962a2be 100644 --- a/addressbookdialog.ui +++ b/addressbookdialog.ui @@ -26,6 +26,9 @@ + + QAbstractItemView::SingleSelection + QAbstractItemView::SelectRows @@ -56,6 +59,9 @@ + + QAbstractItemView::SingleSelection + QAbstractItemView::SelectRows @@ -83,31 +89,44 @@ + + + + &New Address... + + + - Copy to Clipboard + &Copy to Clipboard - Edit... + &Edit... - + - New Address... + &Delete - - - OK + + + + 0 + 0 + + + + QDialogButtonBox::Ok diff --git a/addresstablemodel.cpp b/addresstablemodel.cpp index e8746c2b81..d985bcee3c 100644 --- a/addresstablemodel.cpp +++ b/addresstablemodel.cpp @@ -29,7 +29,7 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const /* index.row(), index.column() */ /* Return QString */ if(index.column() == Address) - return "1PC9aZC4hNX2rmmrt7uHTfYAS3hRbph4UN"; + return "1PC9aZC4hNX2rmmrt7uHTfYAS3hRbph4UN" + QString::number(index.row()); else return "Description"; } else if (role == Qt::UserRole) diff --git a/bitcoin.pro b/bitcoin.pro index d0b9512b95..b34f562fd8 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -16,7 +16,8 @@ HEADERS += bitcoingui.h \ sendcoinsdialog.h \ addressbookdialog.h \ aboutdialog.h \ - editaddressdialog.h + editaddressdialog.h \ + bitcoinaddressvalidator.h SOURCES += bitcoin.cpp bitcoingui.cpp \ transactiontablemodel.cpp \ addresstablemodel.cpp \ @@ -25,7 +26,8 @@ SOURCES += bitcoin.cpp bitcoingui.cpp \ sendcoinsdialog.cpp \ addressbookdialog.cpp \ aboutdialog.cpp \ - editaddressdialog.cpp + editaddressdialog.cpp \ + bitcoinaddressvalidator.cpp RESOURCES += \ bitcoin.qrc diff --git a/bitcoinaddressvalidator.cpp b/bitcoinaddressvalidator.cpp new file mode 100644 index 0000000000..408027b4d5 --- /dev/null +++ b/bitcoinaddressvalidator.cpp @@ -0,0 +1,8 @@ +#include "bitcoinaddressvalidator.h" + +const QString BitcoinAddressValidator::valid_chars = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; + +BitcoinAddressValidator::BitcoinAddressValidator(QObject *parent) : + QRegExpValidator(QRegExp("^["+valid_chars+"]+"), parent) +{ +} diff --git a/bitcoinaddressvalidator.h b/bitcoinaddressvalidator.h new file mode 100644 index 0000000000..f6a2ac02bf --- /dev/null +++ b/bitcoinaddressvalidator.h @@ -0,0 +1,19 @@ +#ifndef BITCOINADDRESSVALIDATOR_H +#define BITCOINADDRESSVALIDATOR_H + +#include + +class BitcoinAddressValidator : public QRegExpValidator +{ + Q_OBJECT +public: + explicit BitcoinAddressValidator(QObject *parent = 0); + + static const QString valid_chars; +signals: + +public slots: + +}; + +#endif // BITCOINADDRESSVALIDATOR_H diff --git a/bitcoingui.h b/bitcoingui.h index 12bbf58ba5..9142b6b84b 100644 --- a/bitcoingui.h +++ b/bitcoingui.h @@ -6,8 +6,11 @@ /* Forward declarations */ class TransactionTableModel; + +QT_BEGIN_NAMESPACE class QLabel; class QLineEdit; +QT_END_NAMESPACE class BitcoinGUI : public QMainWindow { diff --git a/optionsdialog.cpp b/optionsdialog.cpp index e609e54420..70dd86323e 100644 --- a/optionsdialog.cpp +++ b/optionsdialog.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include OptionsDialog::OptionsDialog(QWidget *parent) : QDialog(parent), contents_widget(0), pages_widget(0) diff --git a/optionsdialog.h b/optionsdialog.h index 2a4beaccea..501c82e9f3 100644 --- a/optionsdialog.h +++ b/optionsdialog.h @@ -2,8 +2,12 @@ #define OPTIONSDIALOG_H #include -#include -#include + +QT_BEGIN_NAMESPACE +class QStackedWidget; +class QListWidget; +class QListWidgetItem; +QT_END_NAMESPACE class OptionsDialog : public QDialog { diff --git a/sendcoinsdialog.cpp b/sendcoinsdialog.cpp index d8ef781290..6f459fb669 100644 --- a/sendcoinsdialog.cpp +++ b/sendcoinsdialog.cpp @@ -2,6 +2,7 @@ #include "ui_sendcoinsdialog.h" #include "addressbookdialog.h" +#include "bitcoinaddressvalidator.h" #include #include @@ -11,6 +12,8 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) : ui(new Ui::SendCoinsDialog) { ui->setupUi(this); + ui->payTo->setValidator(new BitcoinAddressValidator(this)); + ui->payAmount->setValidator(new QDoubleValidator(this)); } SendCoinsDialog::~SendCoinsDialog() @@ -23,11 +26,6 @@ void SendCoinsDialog::on_sendButton_clicked() accept(); } -void SendCoinsDialog::on_cancelButton_clicked() -{ - reject(); -} - void SendCoinsDialog::on_pasteButton_clicked() { /* Paste text from clipboard into recipient field */ @@ -38,4 +36,10 @@ void SendCoinsDialog::on_addressBookButton_clicked() { AddressBookDialog dlg; dlg.exec(); + ui->payTo->setText(dlg.getReturnValue()); +} + +void SendCoinsDialog::on_buttonBox_rejected() +{ + reject(); } diff --git a/sendcoinsdialog.h b/sendcoinsdialog.h index e3ffd1d33d..a2fcdd0762 100644 --- a/sendcoinsdialog.h +++ b/sendcoinsdialog.h @@ -19,9 +19,9 @@ private: Ui::SendCoinsDialog *ui; private slots: + void on_buttonBox_rejected(); void on_addressBookButton_clicked(); void on_pasteButton_clicked(); - void on_cancelButton_clicked(); void on_sendButton_clicked(); }; diff --git a/sendcoinsdialog.ui b/sendcoinsdialog.ui index ef7eaf37c4..f14ec2af1b 100644 --- a/sendcoinsdialog.ui +++ b/sendcoinsdialog.ui @@ -43,7 +43,11 @@ - + + + 34 + + @@ -116,12 +120,22 @@ &Send + + + :/icons/send:/icons/send + - - - Cancel + + + + 0 + 0 + + + + QDialogButtonBox::Cancel @@ -129,6 +143,25 @@ - - + + + + + + payAmount + returnPressed() + sendButton + click() + + + 191 + 65 + + + 570 + 121 + + + + -- cgit v1.2.3 From 1f2e0df86573910257318cbdc2c80d114f90d85c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 14 May 2011 10:31:46 +0200 Subject: begin integration with bitcoin upstream --- aboutdialog.cpp | 19 - aboutdialog.h | 25 - aboutdialog.ui | 162 ----- addressbookdialog.cpp | 132 ---- addressbookdialog.h | 47 -- addressbookdialog.ui | 172 ----- addresstablemodel.cpp | 57 -- addresstablemodel.h | 32 - bitcoin.cpp | 24 - bitcoin.pro | 63 +- bitcoin.qrc | 12 - bitcoinaddressvalidator.cpp | 8 - bitcoinaddressvalidator.h | 19 - bitcoingui.cpp | 275 ------- bitcoingui.h | 69 -- editaddressdialog.cpp | 14 - editaddressdialog.h | 22 - editaddressdialog.ui | 77 -- gui/bitcoin.qrc | 12 + gui/forms/aboutdialog.ui | 162 +++++ gui/forms/addressbookdialog.ui | 172 +++++ gui/forms/editaddressdialog.ui | 77 ++ gui/forms/sendcoinsdialog.ui | 167 +++++ gui/include/aboutdialog.h | 25 + gui/include/addressbookdialog.h | 47 ++ gui/include/addresstablemodel.h | 32 + gui/include/bitcoinaddressvalidator.h | 21 + gui/include/bitcoingui.h | 69 ++ gui/include/editaddressdialog.h | 22 + gui/include/mainoptionspage.h | 18 + gui/include/optionsdialog.h | 29 + gui/include/sendcoinsdialog.h | 28 + gui/include/transactiontablemodel.h | 37 + gui/res/icons/address-book.png | Bin 0 -> 1211 bytes gui/res/icons/bitcoin.png | Bin 0 -> 1086 bytes gui/res/icons/quit.png | Bin 0 -> 876 bytes gui/res/icons/send.png | Bin 0 -> 1485 bytes gui/res/icons/toolbar.png | Bin 0 -> 968 bytes gui/res/images/about.png | Bin 0 -> 3488 bytes gui/src/aboutdialog.cpp | 19 + gui/src/addressbookdialog.cpp | 132 ++++ gui/src/addresstablemodel.cpp | 57 ++ gui/src/bitcoin.cpp | 24 + gui/src/bitcoinaddressvalidator.cpp | 8 + gui/src/bitcoingui.cpp | 275 +++++++ gui/src/editaddressdialog.cpp | 14 + gui/src/mainoptionspage.cpp | 67 ++ gui/src/optionsdialog.cpp | 57 ++ gui/src/sendcoinsdialog.cpp | 45 ++ gui/src/transactiontablemodel.cpp | 88 +++ lib/include/base58.h | 208 ++++++ lib/include/bignum.h | 537 ++++++++++++++ lib/include/serialize.h | 1268 +++++++++++++++++++++++++++++++++ lib/include/uint256.h | 765 ++++++++++++++++++++ lib/include/util.h | 673 +++++++++++++++++ mainoptionspage.cpp | 67 -- mainoptionspage.h | 18 - optionsdialog.cpp | 57 -- optionsdialog.h | 29 - res/icons/address-book.png | Bin 1211 -> 0 bytes res/icons/bitcoin.png | Bin 1086 -> 0 bytes res/icons/quit.png | Bin 876 -> 0 bytes res/icons/send.png | Bin 1485 -> 0 bytes res/icons/toolbar.png | Bin 968 -> 0 bytes res/images/about.png | Bin 3488 -> 0 bytes sendcoinsdialog.cpp | 45 -- sendcoinsdialog.h | 28 - sendcoinsdialog.ui | 167 ----- transactiontablemodel.cpp | 88 --- transactiontablemodel.h | 37 - 70 files changed, 5187 insertions(+), 1733 deletions(-) delete mode 100644 aboutdialog.cpp delete mode 100644 aboutdialog.h delete mode 100644 aboutdialog.ui delete mode 100644 addressbookdialog.cpp delete mode 100644 addressbookdialog.h delete mode 100644 addressbookdialog.ui delete mode 100644 addresstablemodel.cpp delete mode 100644 addresstablemodel.h delete mode 100644 bitcoin.cpp delete mode 100644 bitcoin.qrc delete mode 100644 bitcoinaddressvalidator.cpp delete mode 100644 bitcoinaddressvalidator.h delete mode 100644 bitcoingui.cpp delete mode 100644 bitcoingui.h delete mode 100644 editaddressdialog.cpp delete mode 100644 editaddressdialog.h delete mode 100644 editaddressdialog.ui create mode 100644 gui/bitcoin.qrc create mode 100644 gui/forms/aboutdialog.ui create mode 100644 gui/forms/addressbookdialog.ui create mode 100644 gui/forms/editaddressdialog.ui create mode 100644 gui/forms/sendcoinsdialog.ui create mode 100644 gui/include/aboutdialog.h create mode 100644 gui/include/addressbookdialog.h create mode 100644 gui/include/addresstablemodel.h create mode 100644 gui/include/bitcoinaddressvalidator.h create mode 100644 gui/include/bitcoingui.h create mode 100644 gui/include/editaddressdialog.h create mode 100644 gui/include/mainoptionspage.h create mode 100644 gui/include/optionsdialog.h create mode 100644 gui/include/sendcoinsdialog.h create mode 100644 gui/include/transactiontablemodel.h create mode 100644 gui/res/icons/address-book.png create mode 100644 gui/res/icons/bitcoin.png create mode 100644 gui/res/icons/quit.png create mode 100644 gui/res/icons/send.png create mode 100644 gui/res/icons/toolbar.png create mode 100644 gui/res/images/about.png create mode 100644 gui/src/aboutdialog.cpp create mode 100644 gui/src/addressbookdialog.cpp create mode 100644 gui/src/addresstablemodel.cpp create mode 100644 gui/src/bitcoin.cpp create mode 100644 gui/src/bitcoinaddressvalidator.cpp create mode 100644 gui/src/bitcoingui.cpp create mode 100644 gui/src/editaddressdialog.cpp create mode 100644 gui/src/mainoptionspage.cpp create mode 100644 gui/src/optionsdialog.cpp create mode 100644 gui/src/sendcoinsdialog.cpp create mode 100644 gui/src/transactiontablemodel.cpp create mode 100644 lib/include/base58.h create mode 100644 lib/include/bignum.h create mode 100644 lib/include/serialize.h create mode 100644 lib/include/uint256.h create mode 100644 lib/include/util.h delete mode 100644 mainoptionspage.cpp delete mode 100644 mainoptionspage.h delete mode 100644 optionsdialog.cpp delete mode 100644 optionsdialog.h delete mode 100644 res/icons/address-book.png delete mode 100644 res/icons/bitcoin.png delete mode 100644 res/icons/quit.png delete mode 100644 res/icons/send.png delete mode 100644 res/icons/toolbar.png delete mode 100644 res/images/about.png delete mode 100644 sendcoinsdialog.cpp delete mode 100644 sendcoinsdialog.h delete mode 100644 sendcoinsdialog.ui delete mode 100644 transactiontablemodel.cpp delete mode 100644 transactiontablemodel.h diff --git a/aboutdialog.cpp b/aboutdialog.cpp deleted file mode 100644 index 3d7a3f98af..0000000000 --- a/aboutdialog.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "aboutdialog.h" -#include "ui_aboutdialog.h" - -AboutDialog::AboutDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::AboutDialog) -{ - ui->setupUi(this); -} - -AboutDialog::~AboutDialog() -{ - delete ui; -} - -void AboutDialog::on_buttonBox_accepted() -{ - close(); -} diff --git a/aboutdialog.h b/aboutdialog.h deleted file mode 100644 index 827cc741c3..0000000000 --- a/aboutdialog.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef ABOUTDIALOG_H -#define ABOUTDIALOG_H - -#include - -namespace Ui { - class AboutDialog; -} - -class AboutDialog : public QDialog -{ - Q_OBJECT - -public: - explicit AboutDialog(QWidget *parent = 0); - ~AboutDialog(); - -private: - Ui::AboutDialog *ui; - -private slots: - void on_buttonBox_accepted(); -}; - -#endif // ABOUTDIALOG_H diff --git a/aboutdialog.ui b/aboutdialog.ui deleted file mode 100644 index 17a15dd076..0000000000 --- a/aboutdialog.ui +++ /dev/null @@ -1,162 +0,0 @@ - - - AboutDialog - - - - 0 - 0 - 593 - 319 - - - - About Bitcoin - - - - - - - 0 - 0 - - - - - - - :/images/about - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - <b>Bitcoin</b> version - - - - - - - 0.3.666-beta - - - Qt::RichText - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Copyright (c) 2009-2011 Bitcoin Developers - -This is experimental software. - -Distributed under the MIT/X11 software license, see the accompanying file license.txt or http://www.opensource.org/licenses/mit-license.php. - -This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Ok - - - - - - - - - - - - - buttonBox - accepted() - AboutDialog - accept() - - - 360 - 308 - - - 157 - 274 - - - - - buttonBox - rejected() - AboutDialog - reject() - - - 428 - 308 - - - 286 - 274 - - - - - diff --git a/addressbookdialog.cpp b/addressbookdialog.cpp deleted file mode 100644 index 71543f11a7..0000000000 --- a/addressbookdialog.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "addressbookdialog.h" -#include "ui_addressbookdialog.h" - -#include "addresstablemodel.h" -#include "editaddressdialog.h" - -#include -#include - -AddressBookDialog::AddressBookDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::AddressBookDialog), - model(0) -{ - ui->setupUi(this); - - model = new AddressTableModel(this); - setModel(model); -} - -AddressBookDialog::~AddressBookDialog() -{ - delete ui; -} - -void AddressBookDialog::setModel(AddressTableModel *model) -{ - /* Receive filter */ - QSortFilterProxyModel *receive_model = new QSortFilterProxyModel(this); - receive_model->setSourceModel(model); - receive_model->setDynamicSortFilter(true); - receive_model->setFilterRole(Qt::UserRole); - receive_model->setFilterKeyColumn(AddressTableModel::Type); - receive_model->setFilterFixedString(AddressTableModel::Receive); - ui->receiveTableView->setModel(receive_model); - - /* Send filter */ - QSortFilterProxyModel *send_model = new QSortFilterProxyModel(this); - send_model->setSourceModel(model); - send_model->setDynamicSortFilter(true); - send_model->setFilterRole(Qt::UserRole); - send_model->setFilterKeyColumn(AddressTableModel::Type); - send_model->setFilterFixedString(AddressTableModel::Send); - ui->sendTableView->setModel(send_model); - - /* Set column widths */ - ui->receiveTableView->horizontalHeader()->resizeSection( - AddressTableModel::Address, 320); - ui->receiveTableView->horizontalHeader()->setResizeMode( - AddressTableModel::Label, QHeaderView::Stretch); - ui->sendTableView->horizontalHeader()->resizeSection( - AddressTableModel::Address, 320); - ui->sendTableView->horizontalHeader()->setResizeMode( - AddressTableModel::Label, QHeaderView::Stretch); - - /* Hide "Type" column in both views as it is only used for filtering */ - ui->receiveTableView->setColumnHidden(AddressTableModel::Type, true); - ui->sendTableView->setColumnHidden(AddressTableModel::Type, true); -} - -void AddressBookDialog::setTab(int tab) -{ - ui->tabWidget->setCurrentIndex(tab); -} - -QTableView *AddressBookDialog::getCurrentTable() -{ - switch(ui->tabWidget->currentIndex()) - { - case SendingTab: - return ui->sendTableView; - case ReceivingTab: - return ui->receiveTableView; - default: - return 0; - } -} - -void AddressBookDialog::on_copyToClipboard_clicked() -{ - /* Copy currently selected address to clipboard */ - -} - -void AddressBookDialog::on_editButton_clicked() -{ - /* Double click should trigger edit button */ - EditAddressDialog dlg; - dlg.exec(); -} - -void AddressBookDialog::on_newAddressButton_clicked() -{ - EditAddressDialog dlg; - dlg.exec(); -} - -void AddressBookDialog::on_tabWidget_currentChanged(int index) -{ - switch(index) - { - case SendingTab: - ui->deleteButton->show(); - break; - case ReceivingTab: - ui->deleteButton->hide(); - break; - } -} - -void AddressBookDialog::on_deleteButton_clicked() -{ - QTableView *table = getCurrentTable(); - QModelIndexList indexes = table->selectionModel()->selectedRows(); - - foreach (QModelIndex index, indexes) { - table->model()->removeRow(index.row()); - } -} - -void AddressBookDialog::on_buttonBox_accepted() -{ - QTableView *table = getCurrentTable(); - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) { - QVariant address = table->model()->data(index); - returnValue = address.toString(); - } - - accept(); -} diff --git a/addressbookdialog.h b/addressbookdialog.h deleted file mode 100644 index bf7c2a65a6..0000000000 --- a/addressbookdialog.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef ADDRESSBOOKDIALOG_H -#define ADDRESSBOOKDIALOG_H - -#include - -namespace Ui { - class AddressBookDialog; -} -class AddressTableModel; - -QT_BEGIN_NAMESPACE -class QTableView; -QT_END_NAMESPACE - -class AddressBookDialog : public QDialog -{ - Q_OBJECT - -public: - explicit AddressBookDialog(QWidget *parent = 0); - ~AddressBookDialog(); - - enum { - SendingTab = 0, - ReceivingTab = 1 - } Tabs; - - void setModel(AddressTableModel *model); - void setTab(int tab); - const QString &getReturnValue() const { return returnValue; } -private: - Ui::AddressBookDialog *ui; - AddressTableModel *model; - QString returnValue; - - QTableView *getCurrentTable(); - -private slots: - void on_buttonBox_accepted(); - void on_deleteButton_clicked(); - void on_tabWidget_currentChanged(int index); - void on_newAddressButton_clicked(); - void on_editButton_clicked(); - void on_copyToClipboard_clicked(); -}; - -#endif // ADDRESSBOOKDIALOG_H diff --git a/addressbookdialog.ui b/addressbookdialog.ui deleted file mode 100644 index d66962a2be..0000000000 --- a/addressbookdialog.ui +++ /dev/null @@ -1,172 +0,0 @@ - - - AddressBookDialog - - - - 0 - 0 - 591 - 347 - - - - Address Book - - - - - - 1 - - - - Sending - - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - - - - - - Receiving - - - - - - These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. The highlighted address is displayed in the main window. - - - Qt::AutoText - - - true - - - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - &New Address... - - - - - - - &Copy to Clipboard - - - - - - - &Edit... - - - - - - - &Delete - - - - - - - - 0 - 0 - - - - QDialogButtonBox::Ok - - - - - - - - - - - receiveTableView - doubleClicked(QModelIndex) - editButton - click() - - - 334 - 249 - - - 333 - 326 - - - - - sendTableView - doubleClicked(QModelIndex) - editButton - click() - - - 329 - 261 - - - 332 - 326 - - - - - diff --git a/addresstablemodel.cpp b/addresstablemodel.cpp deleted file mode 100644 index d985bcee3c..0000000000 --- a/addresstablemodel.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "addresstablemodel.h" - -const QString AddressTableModel::Send = "S"; -const QString AddressTableModel::Receive = "R"; - -AddressTableModel::AddressTableModel(QObject *parent) : - QAbstractTableModel(parent) -{ - -} - -int AddressTableModel::rowCount(const QModelIndex &parent) const -{ - return 5; -} - -int AddressTableModel::columnCount(const QModelIndex &parent) const -{ - return 3; -} - -QVariant AddressTableModel::data(const QModelIndex &index, int role) const -{ - if(!index.isValid()) - return QVariant(); - - if(role == Qt::DisplayRole) - { - /* index.row(), index.column() */ - /* Return QString */ - if(index.column() == Address) - return "1PC9aZC4hNX2rmmrt7uHTfYAS3hRbph4UN" + QString::number(index.row()); - else - return "Description"; - } else if (role == Qt::UserRole) - { - switch(index.row() % 2) - { - case 0: return Send; - case 1: return Receive; - } - } - return QVariant(); -} - -QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - return QVariant(); -} - -Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::ItemIsEnabled; - - return QAbstractTableModel::flags(index); -} diff --git a/addresstablemodel.h b/addresstablemodel.h deleted file mode 100644 index 50ed80dd39..0000000000 --- a/addresstablemodel.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef ADDRESSTABLEMODEL_H -#define ADDRESSTABLEMODEL_H - -#include - -class AddressTableModel : public QAbstractTableModel -{ - Q_OBJECT -public: - explicit AddressTableModel(QObject *parent = 0); - - enum { - Label = 0, /* User specified label */ - Address = 1, /* Bitcoin address */ - Type = 2 /* Send/Receive, used for filter */ - } ColumnIndex; - - static const QString Send; /* Send addres */ - static const QString Receive; /* Receive address */ - - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - Qt::ItemFlags flags(const QModelIndex &index) const; -signals: - -public slots: - -}; - -#endif // ADDRESSTABLEMODEL_H diff --git a/bitcoin.cpp b/bitcoin.cpp deleted file mode 100644 index 403e4c517b..0000000000 --- a/bitcoin.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * W.J. van der Laan 2011 - */ -#include "bitcoingui.h" - -#include - -int main(int argc, char *argv[]) -{ - QApplication app(argc, argv); - - BitcoinGUI window; - window.setBalance(1234.567890); - window.setNumConnections(4); - window.setNumTransactions(4); - window.setNumBlocks(33); - window.setAddress("123456789"); - - window.show(); - - /* Depending on settings: QApplication::setQuitOnLastWindowClosed(false); */ - - return app.exec(); -} diff --git a/bitcoin.pro b/bitcoin.pro index b34f562fd8..88b92bc6cd 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -1,39 +1,40 @@ -###################################################################### -# Automatically generated by qmake (2.01a) Sat May 7 20:45:42 2011 -###################################################################### - TEMPLATE = app -TARGET = +TARGET = DEPENDPATH += . -INCLUDEPATH += . +INCLUDEPATH += gui/include lib/include # Input -HEADERS += bitcoingui.h \ - transactiontablemodel.h \ - addresstablemodel.h \ - optionsdialog.h \ - mainoptionspage.h \ - sendcoinsdialog.h \ - addressbookdialog.h \ - aboutdialog.h \ - editaddressdialog.h \ - bitcoinaddressvalidator.h -SOURCES += bitcoin.cpp bitcoingui.cpp \ - transactiontablemodel.cpp \ - addresstablemodel.cpp \ - optionsdialog.cpp \ - mainoptionspage.cpp \ - sendcoinsdialog.cpp \ - addressbookdialog.cpp \ - aboutdialog.cpp \ - editaddressdialog.cpp \ - bitcoinaddressvalidator.cpp +HEADERS += gui/include/bitcoingui.h \ + gui/include/transactiontablemodel.h \ + gui/include/addresstablemodel.h \ + gui/include/optionsdialog.h \ + gui/include/mainoptionspage.h \ + gui/include/sendcoinsdialog.h \ + gui/include/addressbookdialog.h \ + gui/include/aboutdialog.h \ + gui/include/editaddressdialog.h \ + gui/include/bitcoinaddressvalidator.h \ + lib/include/base58.h \ + lib/include/bignum.h \ + lib/include/util.h \ + lib/include/uint256.h \ + lib/include/serialize.h +SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ + gui/src/transactiontablemodel.cpp \ + gui/src/addresstablemodel.cpp \ + gui/src/optionsdialog.cpp \ + gui/src/mainoptionspage.cpp \ + gui/src/sendcoinsdialog.cpp \ + gui/src/addressbookdialog.cpp \ + gui/src/aboutdialog.cpp \ + gui/src/editaddressdialog.cpp \ + gui/src/bitcoinaddressvalidator.cpp RESOURCES += \ - bitcoin.qrc + gui/bitcoin.qrc FORMS += \ - sendcoinsdialog.ui \ - addressbookdialog.ui \ - aboutdialog.ui \ - editaddressdialog.ui + gui/forms/sendcoinsdialog.ui \ + gui/forms/addressbookdialog.ui \ + gui/forms/aboutdialog.ui \ + gui/forms/editaddressdialog.ui diff --git a/bitcoin.qrc b/bitcoin.qrc deleted file mode 100644 index 80904b341c..0000000000 --- a/bitcoin.qrc +++ /dev/null @@ -1,12 +0,0 @@ - - - res/icons/address-book.png - res/icons/bitcoin.png - res/icons/quit.png - res/icons/send.png - res/icons/toolbar.png - - - res/images/about.png - - diff --git a/bitcoinaddressvalidator.cpp b/bitcoinaddressvalidator.cpp deleted file mode 100644 index 408027b4d5..0000000000 --- a/bitcoinaddressvalidator.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "bitcoinaddressvalidator.h" - -const QString BitcoinAddressValidator::valid_chars = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; - -BitcoinAddressValidator::BitcoinAddressValidator(QObject *parent) : - QRegExpValidator(QRegExp("^["+valid_chars+"]+"), parent) -{ -} diff --git a/bitcoinaddressvalidator.h b/bitcoinaddressvalidator.h deleted file mode 100644 index f6a2ac02bf..0000000000 --- a/bitcoinaddressvalidator.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef BITCOINADDRESSVALIDATOR_H -#define BITCOINADDRESSVALIDATOR_H - -#include - -class BitcoinAddressValidator : public QRegExpValidator -{ - Q_OBJECT -public: - explicit BitcoinAddressValidator(QObject *parent = 0); - - static const QString valid_chars; -signals: - -public slots: - -}; - -#endif // BITCOINADDRESSVALIDATOR_H diff --git a/bitcoingui.cpp b/bitcoingui.cpp deleted file mode 100644 index 4af703cf8d..0000000000 --- a/bitcoingui.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Qt4 bitcoin GUI. - * - * W.J. van der Laan 2011 - */ -#include "bitcoingui.h" -#include "transactiontablemodel.h" -#include "addressbookdialog.h" -#include "sendcoinsdialog.h" -#include "optionsdialog.h" -#include "aboutdialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -BitcoinGUI::BitcoinGUI(QWidget *parent): - QMainWindow(parent), trayIcon(0) -{ - resize(850, 550); - setWindowTitle(tr("Bitcoin")); - setWindowIcon(QIcon(":icons/bitcoin")); - - createActions(); - - /* Menus */ - QMenu *file = menuBar()->addMenu("&File"); - file->addAction(sendcoins); - file->addSeparator(); - file->addAction(quit); - - QMenu *settings = menuBar()->addMenu("&Settings"); - settings->addAction(receiving_addresses); - settings->addAction(options); - - QMenu *help = menuBar()->addMenu("&Help"); - help->addAction(about); - - /* Toolbar */ - QToolBar *toolbar = addToolBar("Main toolbar"); - toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - toolbar->addAction(sendcoins); - toolbar->addAction(addressbook); - - /* Address:
: New... : Paste to clipboard */ - QHBoxLayout *hbox_address = new QHBoxLayout(); - hbox_address->addWidget(new QLabel(tr("Your Bitcoin Address:"))); - address = new QLineEdit(); - address->setReadOnly(true); - hbox_address->addWidget(address); - - QPushButton *button_new = new QPushButton(tr("&New...")); - QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); - hbox_address->addWidget(button_new); - hbox_address->addWidget(button_clipboard); - - /* Balance: */ - QHBoxLayout *hbox_balance = new QHBoxLayout(); - hbox_balance->addWidget(new QLabel(tr("Balance:"))); - hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ - - labelBalance = new QLabel(); - labelBalance->setFont(QFont("Teletype")); - hbox_balance->addWidget(labelBalance); - hbox_balance->addStretch(1); - - QVBoxLayout *vbox = new QVBoxLayout(); - vbox->addLayout(hbox_address); - vbox->addLayout(hbox_balance); - - transaction_model = new TransactionTableModel(this); - - vbox->addWidget(createTabs()); - - QWidget *centralwidget = new QWidget(this); - centralwidget->setLayout(vbox); - setCentralWidget(centralwidget); - - /* Create status bar */ - statusBar(); - - labelConnections = new QLabel(); - labelConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelConnections->setMinimumWidth(130); - - labelBlocks = new QLabel(); - labelBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelBlocks->setMinimumWidth(130); - - labelTransactions = new QLabel(); - labelTransactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelTransactions->setMinimumWidth(130); - - statusBar()->addPermanentWidget(labelConnections); - statusBar()->addPermanentWidget(labelBlocks); - statusBar()->addPermanentWidget(labelTransactions); - - /* Action bindings */ - connect(button_new, SIGNAL(clicked()), this, SLOT(newAddressClicked())); - connect(button_clipboard, SIGNAL(clicked()), this, SLOT(copyClipboardClicked())); - - createTrayIcon(); -} - -void BitcoinGUI::createActions() -{ - quit = new QAction(QIcon(":/icons/quit"), tr("&Exit"), this); - sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); - addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); - about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); - receiving_addresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); - options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); - openBitCoin = new QAction(QIcon(":/icons/bitcoin"), "Open Bitcoin", this); - - connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); - connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked())); - connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked())); - connect(receiving_addresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked())); - connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); - connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); -} - -void BitcoinGUI::createTrayIcon() -{ - QMenu *trayIconMenu = new QMenu(this); - trayIconMenu->addAction(openBitCoin); - trayIconMenu->addAction(sendcoins); - trayIconMenu->addAction(options); - trayIconMenu->addSeparator(); - trayIconMenu->addAction(quit); - - trayIcon = new QSystemTrayIcon(this); - trayIcon->setContextMenu(trayIconMenu); - trayIcon->setIcon(QIcon(":/icons/toolbar")); - trayIcon->show(); -} - -QWidget *BitcoinGUI::createTabs() -{ - QStringList tab_filters, tab_labels; - tab_filters << "^." - << "^["+TransactionTableModel::Sent+TransactionTableModel::Received+"]" - << "^["+TransactionTableModel::Sent+"]" - << "^["+TransactionTableModel::Received+"]"; - tab_labels << tr("All transactions") - << tr("Sent/Received") - << tr("Sent") - << tr("Received"); - QTabWidget *tabs = new QTabWidget(this); - - for(int i = 0; i < tab_labels.size(); ++i) - { - QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this); - proxy_model->setSourceModel(transaction_model); - proxy_model->setDynamicSortFilter(true); - proxy_model->setFilterRole(Qt::UserRole); - proxy_model->setFilterKeyColumn(TransactionTableModel::Type); - proxy_model->setFilterRegExp(QRegExp(tab_filters.at(i))); - - QTableView *transaction_table = new QTableView(this); - transaction_table->setModel(proxy_model); - transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); - transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); - transaction_table->verticalHeader()->hide(); - - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Status, 112); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Date, 112); - transaction_table->horizontalHeader()->setResizeMode( - TransactionTableModel::Description, QHeaderView::Stretch); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Debit, 79); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Credit, 79); - transaction_table->setColumnHidden(TransactionTableModel::Type, true); - - tabs->addTab(transaction_table, tab_labels.at(i)); - } - return tabs; -} - -void BitcoinGUI::sendcoinsClicked() -{ - qDebug() << "Send coins clicked"; - SendCoinsDialog dlg; - dlg.exec(); -} - -void BitcoinGUI::addressbookClicked() -{ - qDebug() << "Address book clicked"; - AddressBookDialog dlg; - dlg.setTab(AddressBookDialog::SendingTab); - dlg.exec(); -} - -void BitcoinGUI::receivingAddressesClicked() -{ - qDebug() << "Receiving addresses clicked"; - AddressBookDialog dlg; - dlg.setTab(AddressBookDialog::ReceivingTab); - dlg.exec(); -} - -void BitcoinGUI::optionsClicked() -{ - qDebug() << "Options clicked"; - OptionsDialog dlg; - dlg.exec(); -} - -void BitcoinGUI::aboutClicked() -{ - qDebug() << "About clicked"; - AboutDialog dlg; - dlg.exec(); -} - -void BitcoinGUI::newAddressClicked() -{ - qDebug() << "New address clicked"; - /* TODO: generate new address */ -} - -void BitcoinGUI::copyClipboardClicked() -{ - qDebug() << "Copy to clipboard"; - /* Copy text in address to clipboard */ - QApplication::clipboard()->setText(address->text()); -} - -void BitcoinGUI::setBalance(double balance) -{ - labelBalance->setText(QLocale::system().toString(balance, 8)); -} - -void BitcoinGUI::setAddress(const QString &addr) -{ - address->setText(addr); -} - -void BitcoinGUI::setNumConnections(int count) -{ - labelConnections->setText(QLocale::system().toString(count)+" "+tr("connections")); -} - -void BitcoinGUI::setNumBlocks(int count) -{ - labelBlocks->setText(QLocale::system().toString(count)+" "+tr("blocks")); -} - -void BitcoinGUI::setNumTransactions(int count) -{ - labelTransactions->setText(QLocale::system().toString(count)+" "+tr("transactions")); -} diff --git a/bitcoingui.h b/bitcoingui.h deleted file mode 100644 index 9142b6b84b..0000000000 --- a/bitcoingui.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef BITCOINGUI_H -#define BITCOINGUI_H - -#include -#include - -/* Forward declarations */ -class TransactionTableModel; - -QT_BEGIN_NAMESPACE -class QLabel; -class QLineEdit; -QT_END_NAMESPACE - -class BitcoinGUI : public QMainWindow -{ - Q_OBJECT -public: - explicit BitcoinGUI(QWidget *parent = 0); - - /* Transaction table tab indices */ - enum { - AllTransactions = 0, - SentReceived = 1, - Sent = 2, - Received = 3 - } TabIndex; -private: - TransactionTableModel *transaction_model; - - QLineEdit *address; - QLabel *labelBalance; - QLabel *labelConnections; - QLabel *labelBlocks; - QLabel *labelTransactions; - - QAction *quit; - QAction *sendcoins; - QAction *addressbook; - QAction *about; - QAction *receiving_addresses; - QAction *options; - QAction *openBitCoin; - - QSystemTrayIcon *trayIcon; - - void createActions(); - QWidget *createTabs(); - void createTrayIcon(); - -public slots: - void setBalance(double balance); - void setAddress(const QString &address); - void setNumConnections(int count); - void setNumBlocks(int count); - void setNumTransactions(int count); - -private slots: - void sendcoinsClicked(); - void addressbookClicked(); - void optionsClicked(); - void receivingAddressesClicked(); - void aboutClicked(); - - void newAddressClicked(); - void copyClipboardClicked(); -}; - -#endif diff --git a/editaddressdialog.cpp b/editaddressdialog.cpp deleted file mode 100644 index bd5559792a..0000000000 --- a/editaddressdialog.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "editaddressdialog.h" -#include "ui_editaddressdialog.h" - -EditAddressDialog::EditAddressDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::EditAddressDialog) -{ - ui->setupUi(this); -} - -EditAddressDialog::~EditAddressDialog() -{ - delete ui; -} diff --git a/editaddressdialog.h b/editaddressdialog.h deleted file mode 100644 index 650ed534a0..0000000000 --- a/editaddressdialog.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef EDITADDRESSDIALOG_H -#define EDITADDRESSDIALOG_H - -#include - -namespace Ui { - class EditAddressDialog; -} - -class EditAddressDialog : public QDialog -{ - Q_OBJECT - -public: - explicit EditAddressDialog(QWidget *parent = 0); - ~EditAddressDialog(); - -private: - Ui::EditAddressDialog *ui; -}; - -#endif // EDITADDRESSDIALOG_H diff --git a/editaddressdialog.ui b/editaddressdialog.ui deleted file mode 100644 index 2d8aa880f6..0000000000 --- a/editaddressdialog.ui +++ /dev/null @@ -1,77 +0,0 @@ - - - EditAddressDialog - - - - 0 - 0 - 400 - 300 - - - - Dialog - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - EditAddressDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - EditAddressDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/gui/bitcoin.qrc b/gui/bitcoin.qrc new file mode 100644 index 0000000000..80904b341c --- /dev/null +++ b/gui/bitcoin.qrc @@ -0,0 +1,12 @@ + + + res/icons/address-book.png + res/icons/bitcoin.png + res/icons/quit.png + res/icons/send.png + res/icons/toolbar.png + + + res/images/about.png + + diff --git a/gui/forms/aboutdialog.ui b/gui/forms/aboutdialog.ui new file mode 100644 index 0000000000..88668d4da8 --- /dev/null +++ b/gui/forms/aboutdialog.ui @@ -0,0 +1,162 @@ + + + AboutDialog + + + + 0 + 0 + 593 + 319 + + + + About Bitcoin + + + + + + + 0 + 0 + + + + + + + :/images/about + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + <b>Bitcoin</b> version + + + + + + + 0.3.666-beta + + + Qt::RichText + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Copyright (c) 2009-2011 Bitcoin Developers + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file license.txt or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 360 + 308 + + + 157 + 274 + + + + + buttonBox + rejected() + AboutDialog + reject() + + + 428 + 308 + + + 286 + 274 + + + + + diff --git a/gui/forms/addressbookdialog.ui b/gui/forms/addressbookdialog.ui new file mode 100644 index 0000000000..d66962a2be --- /dev/null +++ b/gui/forms/addressbookdialog.ui @@ -0,0 +1,172 @@ + + + AddressBookDialog + + + + 0 + 0 + 591 + 347 + + + + Address Book + + + + + + 1 + + + + Sending + + + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + + + + + + Receiving + + + + + + These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. The highlighted address is displayed in the main window. + + + Qt::AutoText + + + true + + + + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &New Address... + + + + + + + &Copy to Clipboard + + + + + + + &Edit... + + + + + + + &Delete + + + + + + + + 0 + 0 + + + + QDialogButtonBox::Ok + + + + + + + + + + + receiveTableView + doubleClicked(QModelIndex) + editButton + click() + + + 334 + 249 + + + 333 + 326 + + + + + sendTableView + doubleClicked(QModelIndex) + editButton + click() + + + 329 + 261 + + + 332 + 326 + + + + + diff --git a/gui/forms/editaddressdialog.ui b/gui/forms/editaddressdialog.ui new file mode 100644 index 0000000000..2d8aa880f6 --- /dev/null +++ b/gui/forms/editaddressdialog.ui @@ -0,0 +1,77 @@ + + + EditAddressDialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + EditAddressDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + EditAddressDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/gui/forms/sendcoinsdialog.ui b/gui/forms/sendcoinsdialog.ui new file mode 100644 index 0000000000..31a0b99e20 --- /dev/null +++ b/gui/forms/sendcoinsdialog.ui @@ -0,0 +1,167 @@ + + + SendCoinsDialog + + + + 0 + 0 + 736 + 140 + + + + Send Coins + + + + + + + + &Amount: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + payAmount + + + + + + + Pay &To: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + payTo + + + + + + + 34 + + + + + + + + 145 + 16777215 + + + + + + + + &Paste + + + + + + + Address &Book... + + + + + + + + 9 + + + + Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Send + + + + :/icons/send:/icons/send + + + + + + + + 0 + 0 + + + + QDialogButtonBox::Cancel + + + + + + + + + + + + + payAmount + returnPressed() + sendButton + click() + + + 191 + 65 + + + 570 + 121 + + + + + diff --git a/gui/include/aboutdialog.h b/gui/include/aboutdialog.h new file mode 100644 index 0000000000..827cc741c3 --- /dev/null +++ b/gui/include/aboutdialog.h @@ -0,0 +1,25 @@ +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +namespace Ui { + class AboutDialog; +} + +class AboutDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AboutDialog(QWidget *parent = 0); + ~AboutDialog(); + +private: + Ui::AboutDialog *ui; + +private slots: + void on_buttonBox_accepted(); +}; + +#endif // ABOUTDIALOG_H diff --git a/gui/include/addressbookdialog.h b/gui/include/addressbookdialog.h new file mode 100644 index 0000000000..bf7c2a65a6 --- /dev/null +++ b/gui/include/addressbookdialog.h @@ -0,0 +1,47 @@ +#ifndef ADDRESSBOOKDIALOG_H +#define ADDRESSBOOKDIALOG_H + +#include + +namespace Ui { + class AddressBookDialog; +} +class AddressTableModel; + +QT_BEGIN_NAMESPACE +class QTableView; +QT_END_NAMESPACE + +class AddressBookDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AddressBookDialog(QWidget *parent = 0); + ~AddressBookDialog(); + + enum { + SendingTab = 0, + ReceivingTab = 1 + } Tabs; + + void setModel(AddressTableModel *model); + void setTab(int tab); + const QString &getReturnValue() const { return returnValue; } +private: + Ui::AddressBookDialog *ui; + AddressTableModel *model; + QString returnValue; + + QTableView *getCurrentTable(); + +private slots: + void on_buttonBox_accepted(); + void on_deleteButton_clicked(); + void on_tabWidget_currentChanged(int index); + void on_newAddressButton_clicked(); + void on_editButton_clicked(); + void on_copyToClipboard_clicked(); +}; + +#endif // ADDRESSBOOKDIALOG_H diff --git a/gui/include/addresstablemodel.h b/gui/include/addresstablemodel.h new file mode 100644 index 0000000000..50ed80dd39 --- /dev/null +++ b/gui/include/addresstablemodel.h @@ -0,0 +1,32 @@ +#ifndef ADDRESSTABLEMODEL_H +#define ADDRESSTABLEMODEL_H + +#include + +class AddressTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit AddressTableModel(QObject *parent = 0); + + enum { + Label = 0, /* User specified label */ + Address = 1, /* Bitcoin address */ + Type = 2 /* Send/Receive, used for filter */ + } ColumnIndex; + + static const QString Send; /* Send addres */ + static const QString Receive; /* Receive address */ + + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; +signals: + +public slots: + +}; + +#endif // ADDRESSTABLEMODEL_H diff --git a/gui/include/bitcoinaddressvalidator.h b/gui/include/bitcoinaddressvalidator.h new file mode 100644 index 0000000000..8b57a24714 --- /dev/null +++ b/gui/include/bitcoinaddressvalidator.h @@ -0,0 +1,21 @@ +#ifndef BITCOINADDRESSVALIDATOR_H +#define BITCOINADDRESSVALIDATOR_H + +#include + +#include + +class BitcoinAddressValidator : public QRegExpValidator +{ + Q_OBJECT +public: + explicit BitcoinAddressValidator(QObject *parent = 0); + + static const QString valid_chars; +signals: + +public slots: + +}; + +#endif // BITCOINADDRESSVALIDATOR_H diff --git a/gui/include/bitcoingui.h b/gui/include/bitcoingui.h new file mode 100644 index 0000000000..9142b6b84b --- /dev/null +++ b/gui/include/bitcoingui.h @@ -0,0 +1,69 @@ +#ifndef BITCOINGUI_H +#define BITCOINGUI_H + +#include +#include + +/* Forward declarations */ +class TransactionTableModel; + +QT_BEGIN_NAMESPACE +class QLabel; +class QLineEdit; +QT_END_NAMESPACE + +class BitcoinGUI : public QMainWindow +{ + Q_OBJECT +public: + explicit BitcoinGUI(QWidget *parent = 0); + + /* Transaction table tab indices */ + enum { + AllTransactions = 0, + SentReceived = 1, + Sent = 2, + Received = 3 + } TabIndex; +private: + TransactionTableModel *transaction_model; + + QLineEdit *address; + QLabel *labelBalance; + QLabel *labelConnections; + QLabel *labelBlocks; + QLabel *labelTransactions; + + QAction *quit; + QAction *sendcoins; + QAction *addressbook; + QAction *about; + QAction *receiving_addresses; + QAction *options; + QAction *openBitCoin; + + QSystemTrayIcon *trayIcon; + + void createActions(); + QWidget *createTabs(); + void createTrayIcon(); + +public slots: + void setBalance(double balance); + void setAddress(const QString &address); + void setNumConnections(int count); + void setNumBlocks(int count); + void setNumTransactions(int count); + +private slots: + void sendcoinsClicked(); + void addressbookClicked(); + void optionsClicked(); + void receivingAddressesClicked(); + void aboutClicked(); + + void newAddressClicked(); + void copyClipboardClicked(); +}; + +#endif diff --git a/gui/include/editaddressdialog.h b/gui/include/editaddressdialog.h new file mode 100644 index 0000000000..650ed534a0 --- /dev/null +++ b/gui/include/editaddressdialog.h @@ -0,0 +1,22 @@ +#ifndef EDITADDRESSDIALOG_H +#define EDITADDRESSDIALOG_H + +#include + +namespace Ui { + class EditAddressDialog; +} + +class EditAddressDialog : public QDialog +{ + Q_OBJECT + +public: + explicit EditAddressDialog(QWidget *parent = 0); + ~EditAddressDialog(); + +private: + Ui::EditAddressDialog *ui; +}; + +#endif // EDITADDRESSDIALOG_H diff --git a/gui/include/mainoptionspage.h b/gui/include/mainoptionspage.h new file mode 100644 index 0000000000..de2ef9fcd0 --- /dev/null +++ b/gui/include/mainoptionspage.h @@ -0,0 +1,18 @@ +#ifndef MAINOPTIONSPAGE_H +#define MAINOPTIONSPAGE_H + +#include + +class MainOptionsPage : public QWidget +{ + Q_OBJECT +public: + explicit MainOptionsPage(QWidget *parent = 0); + +signals: + +public slots: + +}; + +#endif // MAINOPTIONSPAGE_H diff --git a/gui/include/optionsdialog.h b/gui/include/optionsdialog.h new file mode 100644 index 0000000000..501c82e9f3 --- /dev/null +++ b/gui/include/optionsdialog.h @@ -0,0 +1,29 @@ +#ifndef OPTIONSDIALOG_H +#define OPTIONSDIALOG_H + +#include + +QT_BEGIN_NAMESPACE +class QStackedWidget; +class QListWidget; +class QListWidgetItem; +QT_END_NAMESPACE + +class OptionsDialog : public QDialog +{ + Q_OBJECT +public: + explicit OptionsDialog(QWidget *parent = 0); + +signals: + +public slots: + void changePage(QListWidgetItem *current, QListWidgetItem *previous); +private: + QListWidget *contents_widget; + QStackedWidget *pages_widget; + + void setupMainPage(); +}; + +#endif // OPTIONSDIALOG_H diff --git a/gui/include/sendcoinsdialog.h b/gui/include/sendcoinsdialog.h new file mode 100644 index 0000000000..a2fcdd0762 --- /dev/null +++ b/gui/include/sendcoinsdialog.h @@ -0,0 +1,28 @@ +#ifndef SENDCOINSDIALOG_H +#define SENDCOINSDIALOG_H + +#include + +namespace Ui { + class SendCoinsDialog; +} + +class SendCoinsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SendCoinsDialog(QWidget *parent = 0); + ~SendCoinsDialog(); + +private: + Ui::SendCoinsDialog *ui; + +private slots: + void on_buttonBox_rejected(); + void on_addressBookButton_clicked(); + void on_pasteButton_clicked(); + void on_sendButton_clicked(); +}; + +#endif // SENDCOINSDIALOG_H diff --git a/gui/include/transactiontablemodel.h b/gui/include/transactiontablemodel.h new file mode 100644 index 0000000000..77ad730673 --- /dev/null +++ b/gui/include/transactiontablemodel.h @@ -0,0 +1,37 @@ +#ifndef TRANSACTIONTABLEMODEL_H +#define TRANSACTIONTABLEMODEL_H + +#include +#include + +class TransactionTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit TransactionTableModel(QObject *parent = 0); + + enum { + Status = 0, + Date = 1, + Description = 2, + Debit = 3, + Credit = 4, + Type = 5 + } ColumnIndex; + + /* Transaction type */ + static const QString Sent; + static const QString Received; + static const QString Generated; + + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; +private: + QStringList columns; +}; + +#endif + diff --git a/gui/res/icons/address-book.png b/gui/res/icons/address-book.png new file mode 100644 index 0000000000..abfb3c3a51 Binary files /dev/null and b/gui/res/icons/address-book.png differ diff --git a/gui/res/icons/bitcoin.png b/gui/res/icons/bitcoin.png new file mode 100644 index 0000000000..09520aca23 Binary files /dev/null and b/gui/res/icons/bitcoin.png differ diff --git a/gui/res/icons/quit.png b/gui/res/icons/quit.png new file mode 100644 index 0000000000..fb510fbea6 Binary files /dev/null and b/gui/res/icons/quit.png differ diff --git a/gui/res/icons/send.png b/gui/res/icons/send.png new file mode 100644 index 0000000000..0ba5359d7b Binary files /dev/null and b/gui/res/icons/send.png differ diff --git a/gui/res/icons/toolbar.png b/gui/res/icons/toolbar.png new file mode 100644 index 0000000000..f2dcb20636 Binary files /dev/null and b/gui/res/icons/toolbar.png differ diff --git a/gui/res/images/about.png b/gui/res/images/about.png new file mode 100644 index 0000000000..c9ab9511ef Binary files /dev/null and b/gui/res/images/about.png differ diff --git a/gui/src/aboutdialog.cpp b/gui/src/aboutdialog.cpp new file mode 100644 index 0000000000..3d7a3f98af --- /dev/null +++ b/gui/src/aboutdialog.cpp @@ -0,0 +1,19 @@ +#include "aboutdialog.h" +#include "ui_aboutdialog.h" + +AboutDialog::AboutDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AboutDialog) +{ + ui->setupUi(this); +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} + +void AboutDialog::on_buttonBox_accepted() +{ + close(); +} diff --git a/gui/src/addressbookdialog.cpp b/gui/src/addressbookdialog.cpp new file mode 100644 index 0000000000..71543f11a7 --- /dev/null +++ b/gui/src/addressbookdialog.cpp @@ -0,0 +1,132 @@ +#include "addressbookdialog.h" +#include "ui_addressbookdialog.h" + +#include "addresstablemodel.h" +#include "editaddressdialog.h" + +#include +#include + +AddressBookDialog::AddressBookDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AddressBookDialog), + model(0) +{ + ui->setupUi(this); + + model = new AddressTableModel(this); + setModel(model); +} + +AddressBookDialog::~AddressBookDialog() +{ + delete ui; +} + +void AddressBookDialog::setModel(AddressTableModel *model) +{ + /* Receive filter */ + QSortFilterProxyModel *receive_model = new QSortFilterProxyModel(this); + receive_model->setSourceModel(model); + receive_model->setDynamicSortFilter(true); + receive_model->setFilterRole(Qt::UserRole); + receive_model->setFilterKeyColumn(AddressTableModel::Type); + receive_model->setFilterFixedString(AddressTableModel::Receive); + ui->receiveTableView->setModel(receive_model); + + /* Send filter */ + QSortFilterProxyModel *send_model = new QSortFilterProxyModel(this); + send_model->setSourceModel(model); + send_model->setDynamicSortFilter(true); + send_model->setFilterRole(Qt::UserRole); + send_model->setFilterKeyColumn(AddressTableModel::Type); + send_model->setFilterFixedString(AddressTableModel::Send); + ui->sendTableView->setModel(send_model); + + /* Set column widths */ + ui->receiveTableView->horizontalHeader()->resizeSection( + AddressTableModel::Address, 320); + ui->receiveTableView->horizontalHeader()->setResizeMode( + AddressTableModel::Label, QHeaderView::Stretch); + ui->sendTableView->horizontalHeader()->resizeSection( + AddressTableModel::Address, 320); + ui->sendTableView->horizontalHeader()->setResizeMode( + AddressTableModel::Label, QHeaderView::Stretch); + + /* Hide "Type" column in both views as it is only used for filtering */ + ui->receiveTableView->setColumnHidden(AddressTableModel::Type, true); + ui->sendTableView->setColumnHidden(AddressTableModel::Type, true); +} + +void AddressBookDialog::setTab(int tab) +{ + ui->tabWidget->setCurrentIndex(tab); +} + +QTableView *AddressBookDialog::getCurrentTable() +{ + switch(ui->tabWidget->currentIndex()) + { + case SendingTab: + return ui->sendTableView; + case ReceivingTab: + return ui->receiveTableView; + default: + return 0; + } +} + +void AddressBookDialog::on_copyToClipboard_clicked() +{ + /* Copy currently selected address to clipboard */ + +} + +void AddressBookDialog::on_editButton_clicked() +{ + /* Double click should trigger edit button */ + EditAddressDialog dlg; + dlg.exec(); +} + +void AddressBookDialog::on_newAddressButton_clicked() +{ + EditAddressDialog dlg; + dlg.exec(); +} + +void AddressBookDialog::on_tabWidget_currentChanged(int index) +{ + switch(index) + { + case SendingTab: + ui->deleteButton->show(); + break; + case ReceivingTab: + ui->deleteButton->hide(); + break; + } +} + +void AddressBookDialog::on_deleteButton_clicked() +{ + QTableView *table = getCurrentTable(); + QModelIndexList indexes = table->selectionModel()->selectedRows(); + + foreach (QModelIndex index, indexes) { + table->model()->removeRow(index.row()); + } +} + +void AddressBookDialog::on_buttonBox_accepted() +{ + QTableView *table = getCurrentTable(); + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) { + QVariant address = table->model()->data(index); + returnValue = address.toString(); + } + + accept(); +} diff --git a/gui/src/addresstablemodel.cpp b/gui/src/addresstablemodel.cpp new file mode 100644 index 0000000000..d985bcee3c --- /dev/null +++ b/gui/src/addresstablemodel.cpp @@ -0,0 +1,57 @@ +#include "addresstablemodel.h" + +const QString AddressTableModel::Send = "S"; +const QString AddressTableModel::Receive = "R"; + +AddressTableModel::AddressTableModel(QObject *parent) : + QAbstractTableModel(parent) +{ + +} + +int AddressTableModel::rowCount(const QModelIndex &parent) const +{ + return 5; +} + +int AddressTableModel::columnCount(const QModelIndex &parent) const +{ + return 3; +} + +QVariant AddressTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + + if(role == Qt::DisplayRole) + { + /* index.row(), index.column() */ + /* Return QString */ + if(index.column() == Address) + return "1PC9aZC4hNX2rmmrt7uHTfYAS3hRbph4UN" + QString::number(index.row()); + else + return "Description"; + } else if (role == Qt::UserRole) + { + switch(index.row() % 2) + { + case 0: return Send; + case 1: return Receive; + } + } + return QVariant(); +} + +QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + return QVariant(); +} + +Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemIsEnabled; + + return QAbstractTableModel::flags(index); +} diff --git a/gui/src/bitcoin.cpp b/gui/src/bitcoin.cpp new file mode 100644 index 0000000000..403e4c517b --- /dev/null +++ b/gui/src/bitcoin.cpp @@ -0,0 +1,24 @@ +/* + * W.J. van der Laan 2011 + */ +#include "bitcoingui.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + BitcoinGUI window; + window.setBalance(1234.567890); + window.setNumConnections(4); + window.setNumTransactions(4); + window.setNumBlocks(33); + window.setAddress("123456789"); + + window.show(); + + /* Depending on settings: QApplication::setQuitOnLastWindowClosed(false); */ + + return app.exec(); +} diff --git a/gui/src/bitcoinaddressvalidator.cpp b/gui/src/bitcoinaddressvalidator.cpp new file mode 100644 index 0000000000..408027b4d5 --- /dev/null +++ b/gui/src/bitcoinaddressvalidator.cpp @@ -0,0 +1,8 @@ +#include "bitcoinaddressvalidator.h" + +const QString BitcoinAddressValidator::valid_chars = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; + +BitcoinAddressValidator::BitcoinAddressValidator(QObject *parent) : + QRegExpValidator(QRegExp("^["+valid_chars+"]+"), parent) +{ +} diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp new file mode 100644 index 0000000000..4af703cf8d --- /dev/null +++ b/gui/src/bitcoingui.cpp @@ -0,0 +1,275 @@ +/* + * Qt4 bitcoin GUI. + * + * W.J. van der Laan 2011 + */ +#include "bitcoingui.h" +#include "transactiontablemodel.h" +#include "addressbookdialog.h" +#include "sendcoinsdialog.h" +#include "optionsdialog.h" +#include "aboutdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +BitcoinGUI::BitcoinGUI(QWidget *parent): + QMainWindow(parent), trayIcon(0) +{ + resize(850, 550); + setWindowTitle(tr("Bitcoin")); + setWindowIcon(QIcon(":icons/bitcoin")); + + createActions(); + + /* Menus */ + QMenu *file = menuBar()->addMenu("&File"); + file->addAction(sendcoins); + file->addSeparator(); + file->addAction(quit); + + QMenu *settings = menuBar()->addMenu("&Settings"); + settings->addAction(receiving_addresses); + settings->addAction(options); + + QMenu *help = menuBar()->addMenu("&Help"); + help->addAction(about); + + /* Toolbar */ + QToolBar *toolbar = addToolBar("Main toolbar"); + toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolbar->addAction(sendcoins); + toolbar->addAction(addressbook); + + /* Address:
: New... : Paste to clipboard */ + QHBoxLayout *hbox_address = new QHBoxLayout(); + hbox_address->addWidget(new QLabel(tr("Your Bitcoin Address:"))); + address = new QLineEdit(); + address->setReadOnly(true); + hbox_address->addWidget(address); + + QPushButton *button_new = new QPushButton(tr("&New...")); + QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); + hbox_address->addWidget(button_new); + hbox_address->addWidget(button_clipboard); + + /* Balance: */ + QHBoxLayout *hbox_balance = new QHBoxLayout(); + hbox_balance->addWidget(new QLabel(tr("Balance:"))); + hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ + + labelBalance = new QLabel(); + labelBalance->setFont(QFont("Teletype")); + hbox_balance->addWidget(labelBalance); + hbox_balance->addStretch(1); + + QVBoxLayout *vbox = new QVBoxLayout(); + vbox->addLayout(hbox_address); + vbox->addLayout(hbox_balance); + + transaction_model = new TransactionTableModel(this); + + vbox->addWidget(createTabs()); + + QWidget *centralwidget = new QWidget(this); + centralwidget->setLayout(vbox); + setCentralWidget(centralwidget); + + /* Create status bar */ + statusBar(); + + labelConnections = new QLabel(); + labelConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); + labelConnections->setMinimumWidth(130); + + labelBlocks = new QLabel(); + labelBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); + labelBlocks->setMinimumWidth(130); + + labelTransactions = new QLabel(); + labelTransactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); + labelTransactions->setMinimumWidth(130); + + statusBar()->addPermanentWidget(labelConnections); + statusBar()->addPermanentWidget(labelBlocks); + statusBar()->addPermanentWidget(labelTransactions); + + /* Action bindings */ + connect(button_new, SIGNAL(clicked()), this, SLOT(newAddressClicked())); + connect(button_clipboard, SIGNAL(clicked()), this, SLOT(copyClipboardClicked())); + + createTrayIcon(); +} + +void BitcoinGUI::createActions() +{ + quit = new QAction(QIcon(":/icons/quit"), tr("&Exit"), this); + sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); + addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); + about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); + receiving_addresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); + options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); + openBitCoin = new QAction(QIcon(":/icons/bitcoin"), "Open Bitcoin", this); + + connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked())); + connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked())); + connect(receiving_addresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked())); + connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); + connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); +} + +void BitcoinGUI::createTrayIcon() +{ + QMenu *trayIconMenu = new QMenu(this); + trayIconMenu->addAction(openBitCoin); + trayIconMenu->addAction(sendcoins); + trayIconMenu->addAction(options); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(quit); + + trayIcon = new QSystemTrayIcon(this); + trayIcon->setContextMenu(trayIconMenu); + trayIcon->setIcon(QIcon(":/icons/toolbar")); + trayIcon->show(); +} + +QWidget *BitcoinGUI::createTabs() +{ + QStringList tab_filters, tab_labels; + tab_filters << "^." + << "^["+TransactionTableModel::Sent+TransactionTableModel::Received+"]" + << "^["+TransactionTableModel::Sent+"]" + << "^["+TransactionTableModel::Received+"]"; + tab_labels << tr("All transactions") + << tr("Sent/Received") + << tr("Sent") + << tr("Received"); + QTabWidget *tabs = new QTabWidget(this); + + for(int i = 0; i < tab_labels.size(); ++i) + { + QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this); + proxy_model->setSourceModel(transaction_model); + proxy_model->setDynamicSortFilter(true); + proxy_model->setFilterRole(Qt::UserRole); + proxy_model->setFilterKeyColumn(TransactionTableModel::Type); + proxy_model->setFilterRegExp(QRegExp(tab_filters.at(i))); + + QTableView *transaction_table = new QTableView(this); + transaction_table->setModel(proxy_model); + transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); + transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); + transaction_table->verticalHeader()->hide(); + + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Status, 112); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Date, 112); + transaction_table->horizontalHeader()->setResizeMode( + TransactionTableModel::Description, QHeaderView::Stretch); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Debit, 79); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Credit, 79); + transaction_table->setColumnHidden(TransactionTableModel::Type, true); + + tabs->addTab(transaction_table, tab_labels.at(i)); + } + return tabs; +} + +void BitcoinGUI::sendcoinsClicked() +{ + qDebug() << "Send coins clicked"; + SendCoinsDialog dlg; + dlg.exec(); +} + +void BitcoinGUI::addressbookClicked() +{ + qDebug() << "Address book clicked"; + AddressBookDialog dlg; + dlg.setTab(AddressBookDialog::SendingTab); + dlg.exec(); +} + +void BitcoinGUI::receivingAddressesClicked() +{ + qDebug() << "Receiving addresses clicked"; + AddressBookDialog dlg; + dlg.setTab(AddressBookDialog::ReceivingTab); + dlg.exec(); +} + +void BitcoinGUI::optionsClicked() +{ + qDebug() << "Options clicked"; + OptionsDialog dlg; + dlg.exec(); +} + +void BitcoinGUI::aboutClicked() +{ + qDebug() << "About clicked"; + AboutDialog dlg; + dlg.exec(); +} + +void BitcoinGUI::newAddressClicked() +{ + qDebug() << "New address clicked"; + /* TODO: generate new address */ +} + +void BitcoinGUI::copyClipboardClicked() +{ + qDebug() << "Copy to clipboard"; + /* Copy text in address to clipboard */ + QApplication::clipboard()->setText(address->text()); +} + +void BitcoinGUI::setBalance(double balance) +{ + labelBalance->setText(QLocale::system().toString(balance, 8)); +} + +void BitcoinGUI::setAddress(const QString &addr) +{ + address->setText(addr); +} + +void BitcoinGUI::setNumConnections(int count) +{ + labelConnections->setText(QLocale::system().toString(count)+" "+tr("connections")); +} + +void BitcoinGUI::setNumBlocks(int count) +{ + labelBlocks->setText(QLocale::system().toString(count)+" "+tr("blocks")); +} + +void BitcoinGUI::setNumTransactions(int count) +{ + labelTransactions->setText(QLocale::system().toString(count)+" "+tr("transactions")); +} diff --git a/gui/src/editaddressdialog.cpp b/gui/src/editaddressdialog.cpp new file mode 100644 index 0000000000..bd5559792a --- /dev/null +++ b/gui/src/editaddressdialog.cpp @@ -0,0 +1,14 @@ +#include "editaddressdialog.h" +#include "ui_editaddressdialog.h" + +EditAddressDialog::EditAddressDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::EditAddressDialog) +{ + ui->setupUi(this); +} + +EditAddressDialog::~EditAddressDialog() +{ + delete ui; +} diff --git a/gui/src/mainoptionspage.cpp b/gui/src/mainoptionspage.cpp new file mode 100644 index 0000000000..021e1e470f --- /dev/null +++ b/gui/src/mainoptionspage.cpp @@ -0,0 +1,67 @@ +#include "mainoptionspage.h" + +#include +#include +#include +#include +#include + +MainOptionsPage::MainOptionsPage(QWidget *parent): + QWidget(parent) +{ + QVBoxLayout *layout = new QVBoxLayout(); + + QCheckBox *bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup")); + layout->addWidget(bitcoin_at_startup); + + QCheckBox *minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar")); + layout->addWidget(minimize_to_tray); + + QCheckBox *map_port_upnp = new QCheckBox(tr("Map port using &UPnP")); + layout->addWidget(map_port_upnp); + + QCheckBox *minimize_on_close = new QCheckBox(tr("M&inimize on close")); + layout->addWidget(minimize_on_close); + + QCheckBox *connect_socks4 = new QCheckBox(tr("&Connect through socks4 proxy:")); + layout->addWidget(connect_socks4); + + QHBoxLayout *proxy_hbox = new QHBoxLayout(); + proxy_hbox->addSpacing(18); + QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP: ")); + proxy_hbox->addWidget(proxy_ip_label); + QLineEdit *proxy_ip = new QLineEdit(); + proxy_ip->setMaximumWidth(140); + proxy_ip_label->setBuddy(proxy_ip); + proxy_hbox->addWidget(proxy_ip); + QLabel *proxy_port_label = new QLabel(tr("&Port: ")); + proxy_hbox->addWidget(proxy_port_label); + QLineEdit *proxy_port = new QLineEdit(); + proxy_port->setMaximumWidth(55); + proxy_port_label->setBuddy(proxy_port); + proxy_hbox->addWidget(proxy_port); + proxy_hbox->addStretch(1); + + layout->addLayout(proxy_hbox); + QLabel *fee_help = new QLabel(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended.")); + fee_help->setWordWrap(true); + layout->addWidget(fee_help); + + QHBoxLayout *fee_hbox = new QHBoxLayout(); + fee_hbox->addSpacing(18); + QLabel *fee_label = new QLabel(tr("Pay transaction &fee")); + fee_hbox->addWidget(fee_label); + QLineEdit *fee_edit = new QLineEdit(); + fee_edit->setMaximumWidth(70); + fee_label->setBuddy(fee_edit); + fee_hbox->addWidget(fee_edit); + fee_hbox->addStretch(1); + + layout->addLayout(fee_hbox); + + + layout->addStretch(1); /* Extra space at bottom */ + + setLayout(layout); +} + diff --git a/gui/src/optionsdialog.cpp b/gui/src/optionsdialog.cpp new file mode 100644 index 0000000000..70dd86323e --- /dev/null +++ b/gui/src/optionsdialog.cpp @@ -0,0 +1,57 @@ +#include "optionsdialog.h" +#include "mainoptionspage.h" + +#include +#include +#include +#include +#include + +OptionsDialog::OptionsDialog(QWidget *parent) : + QDialog(parent), contents_widget(0), pages_widget(0) +{ + contents_widget = new QListWidget(); + contents_widget->setMaximumWidth(128); + + pages_widget = new QStackedWidget(); + pages_widget->setMinimumWidth(300); + + QListWidgetItem *item_main = new QListWidgetItem(tr("Main")); + contents_widget->addItem(item_main); + pages_widget->addWidget(new MainOptionsPage(this)); + + contents_widget->setCurrentRow(0); + + QHBoxLayout *main_layout = new QHBoxLayout(); + main_layout->addWidget(contents_widget); + main_layout->addWidget(pages_widget, 1); + + QVBoxLayout *layout = new QVBoxLayout(); + layout->addLayout(main_layout); + + QHBoxLayout *buttons = new QHBoxLayout(); + buttons->addStretch(1); + QPushButton *ok_button = new QPushButton(tr("OK")); + buttons->addWidget(ok_button); + QPushButton *cancel_button = new QPushButton(tr("Cancel")); + buttons->addWidget(cancel_button); + QPushButton *apply_button = new QPushButton(tr("Apply")); + buttons->addWidget(apply_button); + + layout->addLayout(buttons); + + + setLayout(layout); + setWindowTitle(tr("Options")); + + +} + +void OptionsDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) +{ + Q_UNUSED(previous); + if(current) + { + pages_widget->setCurrentIndex(contents_widget->row(current)); + } +} diff --git a/gui/src/sendcoinsdialog.cpp b/gui/src/sendcoinsdialog.cpp new file mode 100644 index 0000000000..6f459fb669 --- /dev/null +++ b/gui/src/sendcoinsdialog.cpp @@ -0,0 +1,45 @@ +#include "sendcoinsdialog.h" +#include "ui_sendcoinsdialog.h" + +#include "addressbookdialog.h" +#include "bitcoinaddressvalidator.h" + +#include +#include + +SendCoinsDialog::SendCoinsDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::SendCoinsDialog) +{ + ui->setupUi(this); + ui->payTo->setValidator(new BitcoinAddressValidator(this)); + ui->payAmount->setValidator(new QDoubleValidator(this)); +} + +SendCoinsDialog::~SendCoinsDialog() +{ + delete ui; +} + +void SendCoinsDialog::on_sendButton_clicked() +{ + accept(); +} + +void SendCoinsDialog::on_pasteButton_clicked() +{ + /* Paste text from clipboard into recipient field */ + ui->payTo->setText(QApplication::clipboard()->text()); +} + +void SendCoinsDialog::on_addressBookButton_clicked() +{ + AddressBookDialog dlg; + dlg.exec(); + ui->payTo->setText(dlg.getReturnValue()); +} + +void SendCoinsDialog::on_buttonBox_rejected() +{ + reject(); +} diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp new file mode 100644 index 0000000000..ee58ecba40 --- /dev/null +++ b/gui/src/transactiontablemodel.cpp @@ -0,0 +1,88 @@ +#include "transactiontablemodel.h" + +#include + +const QString TransactionTableModel::Sent = "s"; +const QString TransactionTableModel::Received = "r"; +const QString TransactionTableModel::Generated = "g"; + +/* Credit and Debit columns are right-aligned as they contain numbers */ +static int column_alignments[] = { + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignRight|Qt::AlignVCenter, + Qt::AlignRight|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter + }; + +TransactionTableModel::TransactionTableModel(QObject *parent): + QAbstractTableModel(parent) +{ + columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit") << tr("Type"); +} + +int TransactionTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 5; +} + +int TransactionTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} + +QVariant TransactionTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + + if(role == Qt::DisplayRole) + { + /* index.row(), index.column() */ + /* Return QString */ + return QLocale::system().toString(index.row()); + } else if (role == Qt::TextAlignmentRole) + { + return column_alignments[index.column()]; + } else if (role == Qt::UserRole) + { + /* user role: transaction type for filtering + "s" (sent) + "r" (received) + "g" (generated) + */ + switch(index.row() % 3) + { + case 0: return QString("s"); + case 1: return QString("r"); + case 2: return QString("o"); + } + } + return QVariant(); +} + +QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(role == Qt::DisplayRole) + { + if(orientation == Qt::Horizontal) + { + return columns[section]; + } + } else if (role == Qt::TextAlignmentRole) + { + return column_alignments[section]; + } + return QVariant(); +} + +Qt::ItemFlags TransactionTableModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemIsEnabled; + + return QAbstractTableModel::flags(index); +} diff --git a/lib/include/base58.h b/lib/include/base58.h new file mode 100644 index 0000000000..7acdf63ae3 --- /dev/null +++ b/lib/include/base58.h @@ -0,0 +1,208 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + + +// +// Why base-58 instead of standard base-64 encoding? +// - Don't want 0OIl characters that look the same in some fonts and +// could be used to create visually identical looking account numbers. +// - A std::string with non-alphanumeric characters is not as easily accepted as an account number. +// - E-mail usually won't line-break if there's no punctuation to break at. +// - Doubleclicking selects the whole number as one word if it's all alphanumeric. +// +#ifndef BITCOIN_BASE58_H +#define BITCOIN_BASE58_H + +#include +#include +#include "bignum.h" + +static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + + +inline std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) +{ + CAutoBN_CTX pctx; + CBigNum bn58 = 58; + CBigNum bn0 = 0; + + // Convert big endian data to little endian + // Extra zero at the end make sure bignum will interpret as a positive number + std::vector vchTmp(pend-pbegin+1, 0); + reverse_copy(pbegin, pend, vchTmp.begin()); + + // Convert little endian data to bignum + CBigNum bn; + bn.setvch(vchTmp); + + // Convert bignum to std::string + std::string str; + str.reserve((pend - pbegin) * 138 / 100 + 1); + CBigNum dv; + CBigNum rem; + while (bn > bn0) + { + if (!BN_div(&dv, &rem, &bn, &bn58, pctx)) + throw bignum_error("EncodeBase58 : BN_div failed"); + bn = dv; + unsigned int c = rem.getulong(); + str += pszBase58[c]; + } + + // Leading zeroes encoded as base58 zeros + for (const unsigned char* p = pbegin; p < pend && *p == 0; p++) + str += pszBase58[0]; + + // Convert little endian std::string to big endian + reverse(str.begin(), str.end()); + return str; +} + +inline std::string EncodeBase58(const std::vector& vch) +{ + return EncodeBase58(&vch[0], &vch[0] + vch.size()); +} + +inline bool DecodeBase58(const char* psz, std::vector& vchRet) +{ + CAutoBN_CTX pctx; + vchRet.clear(); + CBigNum bn58 = 58; + CBigNum bn = 0; + CBigNum bnChar; + while (isspace(*psz)) + psz++; + + // Convert big endian std::string to bignum + for (const char* p = psz; *p; p++) + { + const char* p1 = strchr(pszBase58, *p); + if (p1 == NULL) + { + while (isspace(*p)) + p++; + if (*p != '\0') + return false; + break; + } + bnChar.setulong(p1 - pszBase58); + if (!BN_mul(&bn, &bn, &bn58, pctx)) + throw bignum_error("DecodeBase58 : BN_mul failed"); + bn += bnChar; + } + + // Get bignum as little endian data + std::vector vchTmp = bn.getvch(); + + // Trim off sign byte if present + if (vchTmp.size() >= 2 && vchTmp.end()[-1] == 0 && vchTmp.end()[-2] >= 0x80) + vchTmp.erase(vchTmp.end()-1); + + // Restore leading zeros + int nLeadingZeros = 0; + for (const char* p = psz; *p == pszBase58[0]; p++) + nLeadingZeros++; + vchRet.assign(nLeadingZeros + vchTmp.size(), 0); + + // Convert little endian data to big endian + reverse_copy(vchTmp.begin(), vchTmp.end(), vchRet.end() - vchTmp.size()); + return true; +} + +inline bool DecodeBase58(const std::string& str, std::vector& vchRet) +{ + return DecodeBase58(str.c_str(), vchRet); +} + + + + + +inline std::string EncodeBase58Check(const std::vector& vchIn) +{ + // add 4-byte hash check to the end + std::vector vch(vchIn); + uint256 hash = Hash(vch.begin(), vch.end()); + vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4); + return EncodeBase58(vch); +} + +inline bool DecodeBase58Check(const char* psz, std::vector& vchRet) +{ + if (!DecodeBase58(psz, vchRet)) + return false; + if (vchRet.size() < 4) + { + vchRet.clear(); + return false; + } + uint256 hash = Hash(vchRet.begin(), vchRet.end()-4); + if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) + { + vchRet.clear(); + return false; + } + vchRet.resize(vchRet.size()-4); + return true; +} + +inline bool DecodeBase58Check(const std::string& str, std::vector& vchRet) +{ + return DecodeBase58Check(str.c_str(), vchRet); +} + + + + + + +#define ADDRESSVERSION ((unsigned char)(fTestNet ? 111 : 0)) + +inline std::string Hash160ToAddress(uint160 hash160) +{ + // add 1-byte version number to the front + std::vector vch(1, ADDRESSVERSION); + vch.insert(vch.end(), UBEGIN(hash160), UEND(hash160)); + return EncodeBase58Check(vch); +} + +inline bool AddressToHash160(const char* psz, uint160& hash160Ret) +{ + std::vector vch; + if (!DecodeBase58Check(psz, vch)) + return false; + if (vch.empty()) + return false; + unsigned char nVersion = vch[0]; + if (vch.size() != sizeof(hash160Ret) + 1) + return false; + memcpy(&hash160Ret, &vch[1], sizeof(hash160Ret)); + return (nVersion <= ADDRESSVERSION); +} + +inline bool AddressToHash160(const std::string& str, uint160& hash160Ret) +{ + return AddressToHash160(str.c_str(), hash160Ret); +} + +inline bool IsValidBitcoinAddress(const char* psz) +{ + uint160 hash160; + return AddressToHash160(psz, hash160); +} + +inline bool IsValidBitcoinAddress(const std::string& str) +{ + return IsValidBitcoinAddress(str.c_str()); +} + + + + +inline std::string PubKeyToAddress(const std::vector& vchPubKey) +{ + return Hash160ToAddress(Hash160(vchPubKey)); +} + +#endif diff --git a/lib/include/bignum.h b/lib/include/bignum.h new file mode 100644 index 0000000000..e353532963 --- /dev/null +++ b/lib/include/bignum.h @@ -0,0 +1,537 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_BIGNUM_H +#define BITCOIN_BIGNUM_H + +#include +#include +#include +#include + + + + + +class bignum_error : public std::runtime_error +{ +public: + explicit bignum_error(const std::string& str) : std::runtime_error(str) {} +}; + + + +class CAutoBN_CTX +{ +protected: + BN_CTX* pctx; + BN_CTX* operator=(BN_CTX* pnew) { return pctx = pnew; } + +public: + CAutoBN_CTX() + { + pctx = BN_CTX_new(); + if (pctx == NULL) + throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned NULL"); + } + + ~CAutoBN_CTX() + { + if (pctx != NULL) + BN_CTX_free(pctx); + } + + operator BN_CTX*() { return pctx; } + BN_CTX& operator*() { return *pctx; } + BN_CTX** operator&() { return &pctx; } + bool operator!() { return (pctx == NULL); } +}; + + + +class CBigNum : public BIGNUM +{ +public: + CBigNum() + { + BN_init(this); + } + + CBigNum(const CBigNum& b) + { + BN_init(this); + if (!BN_copy(this, &b)) + { + BN_clear_free(this); + throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_copy failed"); + } + } + + CBigNum& operator=(const CBigNum& b) + { + if (!BN_copy(this, &b)) + throw bignum_error("CBigNum::operator= : BN_copy failed"); + return (*this); + } + + ~CBigNum() + { + BN_clear_free(this); + } + + CBigNum(char n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(short n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(long n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int64 n) { BN_init(this); setint64(n); } + CBigNum(unsigned char n) { BN_init(this); setulong(n); } + CBigNum(unsigned short n) { BN_init(this); setulong(n); } + CBigNum(unsigned int n) { BN_init(this); setulong(n); } + CBigNum(unsigned long n) { BN_init(this); setulong(n); } + CBigNum(uint64 n) { BN_init(this); setuint64(n); } + explicit CBigNum(uint256 n) { BN_init(this); setuint256(n); } + + explicit CBigNum(const std::vector& vch) + { + BN_init(this); + setvch(vch); + } + + void setulong(unsigned long n) + { + if (!BN_set_word(this, n)) + throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed"); + } + + unsigned long getulong() const + { + return BN_get_word(this); + } + + unsigned int getuint() const + { + return BN_get_word(this); + } + + int getint() const + { + unsigned long n = BN_get_word(this); + if (!BN_is_negative(this)) + return (n > INT_MAX ? INT_MAX : n); + else + return (n > INT_MAX ? INT_MIN : -(int)n); + } + + void setint64(int64 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fNegative = false; + if (n < (int64)0) + { + n = -n; + fNegative = true; + } + bool fLeadingZeroes = true; + for (int i = 0; i < 8; i++) + { + unsigned char c = (n >> 56) & 0xff; + n <<= 8; + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = (fNegative ? 0x80 : 0); + else if (fNegative) + c |= 0x80; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + void setuint64(uint64 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fLeadingZeroes = true; + for (int i = 0; i < 8; i++) + { + unsigned char c = (n >> 56) & 0xff; + n <<= 8; + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + void setuint256(uint256 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fLeadingZeroes = true; + unsigned char* pbegin = (unsigned char*)&n; + unsigned char* psrc = pbegin + sizeof(n); + while (psrc != pbegin) + { + unsigned char c = *(--psrc); + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize >> 0) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + uint256 getuint256() + { + unsigned int nSize = BN_bn2mpi(this, NULL); + if (nSize < 4) + return 0; + std::vector vch(nSize); + BN_bn2mpi(this, &vch[0]); + if (vch.size() > 4) + vch[4] &= 0x7f; + uint256 n = 0; + for (int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) + ((unsigned char*)&n)[i] = vch[j]; + return n; + } + + void setvch(const std::vector& vch) + { + std::vector vch2(vch.size() + 4); + unsigned int nSize = vch.size(); + vch2[0] = (nSize >> 24) & 0xff; + vch2[1] = (nSize >> 16) & 0xff; + vch2[2] = (nSize >> 8) & 0xff; + vch2[3] = (nSize >> 0) & 0xff; + reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4); + BN_mpi2bn(&vch2[0], vch2.size(), this); + } + + std::vector getvch() const + { + unsigned int nSize = BN_bn2mpi(this, NULL); + if (nSize < 4) + return std::vector(); + std::vector vch(nSize); + BN_bn2mpi(this, &vch[0]); + vch.erase(vch.begin(), vch.begin() + 4); + reverse(vch.begin(), vch.end()); + return vch; + } + + CBigNum& SetCompact(unsigned int nCompact) + { + unsigned int nSize = nCompact >> 24; + std::vector vch(4 + nSize); + vch[3] = nSize; + if (nSize >= 1) vch[4] = (nCompact >> 16) & 0xff; + if (nSize >= 2) vch[5] = (nCompact >> 8) & 0xff; + if (nSize >= 3) vch[6] = (nCompact >> 0) & 0xff; + BN_mpi2bn(&vch[0], vch.size(), this); + return *this; + } + + unsigned int GetCompact() const + { + unsigned int nSize = BN_bn2mpi(this, NULL); + std::vector vch(nSize); + nSize -= 4; + BN_bn2mpi(this, &vch[0]); + unsigned int nCompact = nSize << 24; + if (nSize >= 1) nCompact |= (vch[4] << 16); + if (nSize >= 2) nCompact |= (vch[5] << 8); + if (nSize >= 3) nCompact |= (vch[6] << 0); + return nCompact; + } + + void SetHex(const std::string& str) + { + // skip 0x + const char* psz = str.c_str(); + while (isspace(*psz)) + psz++; + bool fNegative = false; + if (*psz == '-') + { + fNegative = true; + psz++; + } + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + while (isspace(*psz)) + psz++; + + // hex string to bignum + static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; + *this = 0; + while (isxdigit(*psz)) + { + *this <<= 4; + int n = phexdigit[*psz++]; + *this += n; + } + if (fNegative) + *this = 0 - *this; + } + + std::string ToString(int nBase=10) const + { + CAutoBN_CTX pctx; + CBigNum bnBase = nBase; + CBigNum bn0 = 0; + string str; + CBigNum bn = *this; + BN_set_negative(&bn, false); + CBigNum dv; + CBigNum rem; + if (BN_cmp(&bn, &bn0) == 0) + return "0"; + while (BN_cmp(&bn, &bn0) > 0) + { + if (!BN_div(&dv, &rem, &bn, &bnBase, pctx)) + throw bignum_error("CBigNum::ToString() : BN_div failed"); + bn = dv; + unsigned int c = rem.getulong(); + str += "0123456789abcdef"[c]; + } + if (BN_is_negative(this)) + str += "-"; + reverse(str.begin(), str.end()); + return str; + } + + std::string GetHex() const + { + return ToString(16); + } + + unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const + { + return ::GetSerializeSize(getvch(), nType, nVersion); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const + { + ::Serialize(s, getvch(), nType, nVersion); + } + + template + void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) + { + vector vch; + ::Unserialize(s, vch, nType, nVersion); + setvch(vch); + } + + + bool operator!() const + { + return BN_is_zero(this); + } + + CBigNum& operator+=(const CBigNum& b) + { + if (!BN_add(this, this, &b)) + throw bignum_error("CBigNum::operator+= : BN_add failed"); + return *this; + } + + CBigNum& operator-=(const CBigNum& b) + { + *this = *this - b; + return *this; + } + + CBigNum& operator*=(const CBigNum& b) + { + CAutoBN_CTX pctx; + if (!BN_mul(this, this, &b, pctx)) + throw bignum_error("CBigNum::operator*= : BN_mul failed"); + return *this; + } + + CBigNum& operator/=(const CBigNum& b) + { + *this = *this / b; + return *this; + } + + CBigNum& operator%=(const CBigNum& b) + { + *this = *this % b; + return *this; + } + + CBigNum& operator<<=(unsigned int shift) + { + if (!BN_lshift(this, this, shift)) + throw bignum_error("CBigNum:operator<<= : BN_lshift failed"); + return *this; + } + + CBigNum& operator>>=(unsigned int shift) + { + // Note: BN_rshift segfaults on 64-bit if 2^shift is greater than the number + // if built on ubuntu 9.04 or 9.10, probably depends on version of openssl + CBigNum a = 1; + a <<= shift; + if (BN_cmp(&a, this) > 0) + { + *this = 0; + return *this; + } + + if (!BN_rshift(this, this, shift)) + throw bignum_error("CBigNum:operator>>= : BN_rshift failed"); + return *this; + } + + + CBigNum& operator++() + { + // prefix operator + if (!BN_add(this, this, BN_value_one())) + throw bignum_error("CBigNum::operator++ : BN_add failed"); + return *this; + } + + const CBigNum operator++(int) + { + // postfix operator + const CBigNum ret = *this; + ++(*this); + return ret; + } + + CBigNum& operator--() + { + // prefix operator + CBigNum r; + if (!BN_sub(&r, this, BN_value_one())) + throw bignum_error("CBigNum::operator-- : BN_sub failed"); + *this = r; + return *this; + } + + const CBigNum operator--(int) + { + // postfix operator + const CBigNum ret = *this; + --(*this); + return ret; + } + + + friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator/(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator%(const CBigNum& a, const CBigNum& b); +}; + + + +inline const CBigNum operator+(const CBigNum& a, const CBigNum& b) +{ + CBigNum r; + if (!BN_add(&r, &a, &b)) + throw bignum_error("CBigNum::operator+ : BN_add failed"); + return r; +} + +inline const CBigNum operator-(const CBigNum& a, const CBigNum& b) +{ + CBigNum r; + if (!BN_sub(&r, &a, &b)) + throw bignum_error("CBigNum::operator- : BN_sub failed"); + return r; +} + +inline const CBigNum operator-(const CBigNum& a) +{ + CBigNum r(a); + BN_set_negative(&r, !BN_is_negative(&r)); + return r; +} + +inline const CBigNum operator*(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_mul(&r, &a, &b, pctx)) + throw bignum_error("CBigNum::operator* : BN_mul failed"); + return r; +} + +inline const CBigNum operator/(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_div(&r, NULL, &a, &b, pctx)) + throw bignum_error("CBigNum::operator/ : BN_div failed"); + return r; +} + +inline const CBigNum operator%(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_mod(&r, &a, &b, pctx)) + throw bignum_error("CBigNum::operator% : BN_div failed"); + return r; +} + +inline const CBigNum operator<<(const CBigNum& a, unsigned int shift) +{ + CBigNum r; + if (!BN_lshift(&r, &a, shift)) + throw bignum_error("CBigNum:operator<< : BN_lshift failed"); + return r; +} + +inline const CBigNum operator>>(const CBigNum& a, unsigned int shift) +{ + CBigNum r = a; + r >>= shift; + return r; +} + +inline bool operator==(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) == 0); } +inline bool operator!=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) != 0); } +inline bool operator<=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) <= 0); } +inline bool operator>=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) >= 0); } +inline bool operator<(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) < 0); } +inline bool operator>(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) > 0); } + +#endif diff --git a/lib/include/serialize.h b/lib/include/serialize.h new file mode 100644 index 0000000000..f810b339c2 --- /dev/null +++ b/lib/include/serialize.h @@ -0,0 +1,1268 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_SERIALIZE_H +#define BITCOIN_SERIALIZE_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false) ; else for +#endif +class CScript; +class CDataStream; +class CAutoFile; +static const unsigned int MAX_SIZE = 0x02000000; + +static const int VERSION = 32200; +static const char* pszSubVer = ""; +static const bool VERSION_IS_BETA = true; + + + + + + +///////////////////////////////////////////////////////////////// +// +// Templates for serializing to anything that looks like a stream, +// i.e. anything that supports .read(char*, int) and .write(char*, int) +// + +enum +{ + // primary actions + SER_NETWORK = (1 << 0), + SER_DISK = (1 << 1), + SER_GETHASH = (1 << 2), + + // modifiers + SER_SKIPSIG = (1 << 16), + SER_BLOCKHEADERONLY = (1 << 17), +}; + +#define IMPLEMENT_SERIALIZE(statements) \ + unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const \ + { \ + CSerActionGetSerializeSize ser_action; \ + const bool fGetSize = true; \ + const bool fWrite = false; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + ser_streamplaceholder s; \ + s.nType = nType; \ + s.nVersion = nVersion; \ + {statements} \ + return nSerSize; \ + } \ + template \ + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const \ + { \ + CSerActionSerialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = true; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + {statements} \ + } \ + template \ + void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) \ + { \ + CSerActionUnserialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = false; \ + const bool fRead = true; \ + unsigned int nSerSize = 0; \ + {statements} \ + } + +#define READWRITE(obj) (nSerSize += ::SerReadWrite(s, (obj), nType, nVersion, ser_action)) + + + + + + +// +// Basic types +// +#define WRITEDATA(s, obj) s.write((char*)&(obj), sizeof(obj)) +#define READDATA(s, obj) s.read((char*)&(obj), sizeof(obj)) + +inline unsigned int GetSerializeSize(char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed short a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned short a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed int a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned int a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(int64 a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(uint64 a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(float a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(double a, int, int=0) { return sizeof(a); } + +template inline void Serialize(Stream& s, char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed short a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned short a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed int a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned int a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed long a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned long a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, int64 a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, uint64 a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, float a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, double a, int, int=0) { WRITEDATA(s, a); } + +template inline void Unserialize(Stream& s, char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed short& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned short& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed int& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned int& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed long& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned long& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, int64& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, uint64& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, float& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, double& a, int, int=0) { READDATA(s, a); } + +inline unsigned int GetSerializeSize(bool a, int, int=0) { return sizeof(char); } +template inline void Serialize(Stream& s, bool a, int, int=0) { char f=a; WRITEDATA(s, f); } +template inline void Unserialize(Stream& s, bool& a, int, int=0) { char f; READDATA(s, f); a=f; } + + + + + + +// +// Compact size +// size < 253 -- 1 byte +// size <= USHRT_MAX -- 3 bytes (253 + 2 bytes) +// size <= UINT_MAX -- 5 bytes (254 + 4 bytes) +// size > UINT_MAX -- 9 bytes (255 + 8 bytes) +// +inline unsigned int GetSizeOfCompactSize(uint64 nSize) +{ + if (nSize < 253) return sizeof(unsigned char); + else if (nSize <= USHRT_MAX) return sizeof(unsigned char) + sizeof(unsigned short); + else if (nSize <= UINT_MAX) return sizeof(unsigned char) + sizeof(unsigned int); + else return sizeof(unsigned char) + sizeof(uint64); +} + +template +void WriteCompactSize(Stream& os, uint64 nSize) +{ + if (nSize < 253) + { + unsigned char chSize = nSize; + WRITEDATA(os, chSize); + } + else if (nSize <= USHRT_MAX) + { + unsigned char chSize = 253; + unsigned short xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + else if (nSize <= UINT_MAX) + { + unsigned char chSize = 254; + unsigned int xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + else + { + unsigned char chSize = 255; + uint64 xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + return; +} + +template +uint64 ReadCompactSize(Stream& is) +{ + unsigned char chSize; + READDATA(is, chSize); + uint64 nSizeRet = 0; + if (chSize < 253) + { + nSizeRet = chSize; + } + else if (chSize == 253) + { + unsigned short xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + else if (chSize == 254) + { + unsigned int xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + else + { + uint64 xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + if (nSizeRet > (uint64)MAX_SIZE) + throw std::ios_base::failure("ReadCompactSize() : size too large"); + return nSizeRet; +} + + + +// +// Wrapper for serializing arrays and POD +// There's a clever template way to make arrays serialize normally, but MSVC6 doesn't support it +// +#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) +class CFlatData +{ +protected: + char* pbegin; + char* pend; +public: + CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } + char* begin() { return pbegin; } + const char* begin() const { return pbegin; } + char* end() { return pend; } + const char* end() const { return pend; } + + unsigned int GetSerializeSize(int, int=0) const + { + return pend - pbegin; + } + + template + void Serialize(Stream& s, int, int=0) const + { + s.write(pbegin, pend - pbegin); + } + + template + void Unserialize(Stream& s, int, int=0) + { + s.read(pbegin, pend - pbegin); + } +}; + + + +// +// string stored as a fixed length field +// +template +class CFixedFieldString +{ +protected: + const std::string* pcstr; + std::string* pstr; +public: + explicit CFixedFieldString(const std::string& str) : pcstr(&str), pstr(NULL) { } + explicit CFixedFieldString(std::string& str) : pcstr(&str), pstr(&str) { } + + unsigned int GetSerializeSize(int, int=0) const + { + return LEN; + } + + template + void Serialize(Stream& s, int, int=0) const + { + char pszBuf[LEN]; + strncpy(pszBuf, pcstr->c_str(), LEN); + s.write(pszBuf, LEN); + } + + template + void Unserialize(Stream& s, int, int=0) + { + if (pstr == NULL) + throw std::ios_base::failure("CFixedFieldString::Unserialize : trying to unserialize to const string"); + char pszBuf[LEN+1]; + s.read(pszBuf, LEN); + pszBuf[LEN] = '\0'; + *pstr = pszBuf; + } +}; + + + + + +// +// Forward declarations +// + +// string +template unsigned int GetSerializeSize(const std::basic_string& str, int, int=0); +template void Serialize(Stream& os, const std::basic_string& str, int, int=0); +template void Unserialize(Stream& is, std::basic_string& str, int, int=0); + +// vector +template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&); +template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion=VERSION); +template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&); +template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion=VERSION); +template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&); +template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion=VERSION); + +// others derived from vector +extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const CScript& v, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, CScript& v, int nType, int nVersion=VERSION); + +// pair +template unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const std::pair& item, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, std::pair& item, int nType, int nVersion=VERSION); + +// 3 tuple +template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion=VERSION); + +// 4 tuple +template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion=VERSION); + +// map +template unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const std::map& m, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, std::map& m, int nType, int nVersion=VERSION); + +// set +template unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const std::set& m, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, std::set& m, int nType, int nVersion=VERSION); + + + + + +// +// If none of the specialized versions above matched, default to calling member function. +// "int nType" is changed to "long nType" to keep from getting an ambiguous overload error. +// The compiler will only cast int to long if none of the other templates matched. +// Thanks to Boost serialization for this idea. +// +template +inline unsigned int GetSerializeSize(const T& a, long nType, int nVersion=VERSION) +{ + return a.GetSerializeSize((int)nType, nVersion); +} + +template +inline void Serialize(Stream& os, const T& a, long nType, int nVersion=VERSION) +{ + a.Serialize(os, (int)nType, nVersion); +} + +template +inline void Unserialize(Stream& is, T& a, long nType, int nVersion=VERSION) +{ + a.Unserialize(is, (int)nType, nVersion); +} + + + + + +// +// string +// +template +unsigned int GetSerializeSize(const std::basic_string& str, int, int) +{ + return GetSizeOfCompactSize(str.size()) + str.size() * sizeof(str[0]); +} + +template +void Serialize(Stream& os, const std::basic_string& str, int, int) +{ + WriteCompactSize(os, str.size()); + if (!str.empty()) + os.write((char*)&str[0], str.size() * sizeof(str[0])); +} + +template +void Unserialize(Stream& is, std::basic_string& str, int, int) +{ + unsigned int nSize = ReadCompactSize(is); + str.resize(nSize); + if (nSize != 0) + is.read((char*)&str[0], nSize * sizeof(str[0])); +} + + + +// +// vector +// +template +unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); +} + +template +unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + unsigned int nSize = GetSizeOfCompactSize(v.size()); + for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + nSize += GetSerializeSize((*vi), nType, nVersion); + return nSize; +} + +template +inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion) +{ + return GetSerializeSize_impl(v, nType, nVersion, boost::is_fundamental()); +} + + +template +void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + WriteCompactSize(os, v.size()); + if (!v.empty()) + os.write((char*)&v[0], v.size() * sizeof(T)); +} + +template +void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + WriteCompactSize(os, v.size()); + for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + ::Serialize(os, (*vi), nType, nVersion); +} + +template +inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion) +{ + Serialize_impl(os, v, nType, nVersion, boost::is_fundamental()); +} + + +template +void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + //unsigned int nSize = ReadCompactSize(is); + //v.resize(nSize); + //is.read((char*)&v[0], nSize * sizeof(T)); + + // Limit size per read so bogus size value won't cause out of memory + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + while (i < nSize) + { + unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); + v.resize(i + blk); + is.read((char*)&v[i], blk * sizeof(T)); + i += blk; + } +} + +template +void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + //unsigned int nSize = ReadCompactSize(is); + //v.resize(nSize); + //for (std::vector::iterator vi = v.begin(); vi != v.end(); ++vi) + // Unserialize(is, (*vi), nType, nVersion); + + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + unsigned int nMid = 0; + while (nMid < nSize) + { + nMid += 5000000 / sizeof(T); + if (nMid > nSize) + nMid = nSize; + v.resize(nMid); + for (; i < nMid; i++) + Unserialize(is, v[i], nType, nVersion); + } +} + +template +inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion) +{ + Unserialize_impl(is, v, nType, nVersion, boost::is_fundamental()); +} + + + +// +// others derived from vector +// +inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion) +{ + return GetSerializeSize((const std::vector&)v, nType, nVersion); +} + +template +void Serialize(Stream& os, const CScript& v, int nType, int nVersion) +{ + Serialize(os, (const std::vector&)v, nType, nVersion); +} + +template +void Unserialize(Stream& is, CScript& v, int nType, int nVersion) +{ + Unserialize(is, (std::vector&)v, nType, nVersion); +} + + + +// +// pair +// +template +unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion) +{ + return GetSerializeSize(item.first, nType, nVersion) + GetSerializeSize(item.second, nType, nVersion); +} + +template +void Serialize(Stream& os, const std::pair& item, int nType, int nVersion) +{ + Serialize(os, item.first, nType, nVersion); + Serialize(os, item.second, nType, nVersion); +} + +template +void Unserialize(Stream& is, std::pair& item, int nType, int nVersion) +{ + Unserialize(is, item.first, nType, nVersion); + Unserialize(is, item.second, nType, nVersion); +} + + + +// +// 3 tuple +// +template +unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) +{ + unsigned int nSize = 0; + nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) +{ + Serialize(os, boost::get<0>(item), nType, nVersion); + Serialize(os, boost::get<1>(item), nType, nVersion); + Serialize(os, boost::get<2>(item), nType, nVersion); +} + +template +void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) +{ + Unserialize(is, boost::get<0>(item), nType, nVersion); + Unserialize(is, boost::get<1>(item), nType, nVersion); + Unserialize(is, boost::get<2>(item), nType, nVersion); +} + + + +// +// 4 tuple +// +template +unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) +{ + unsigned int nSize = 0; + nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<3>(item), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) +{ + Serialize(os, boost::get<0>(item), nType, nVersion); + Serialize(os, boost::get<1>(item), nType, nVersion); + Serialize(os, boost::get<2>(item), nType, nVersion); + Serialize(os, boost::get<3>(item), nType, nVersion); +} + +template +void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) +{ + Unserialize(is, boost::get<0>(item), nType, nVersion); + Unserialize(is, boost::get<1>(item), nType, nVersion); + Unserialize(is, boost::get<2>(item), nType, nVersion); + Unserialize(is, boost::get<3>(item), nType, nVersion); +} + + + +// +// map +// +template +unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion) +{ + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) + nSize += GetSerializeSize((*mi), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const std::map& m, int nType, int nVersion) +{ + WriteCompactSize(os, m.size()); + for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) + Serialize(os, (*mi), nType, nVersion); +} + +template +void Unserialize(Stream& is, std::map& m, int nType, int nVersion) +{ + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::map::iterator mi = m.begin(); + for (unsigned int i = 0; i < nSize; i++) + { + std::pair item; + Unserialize(is, item, nType, nVersion); + mi = m.insert(mi, item); + } +} + + + +// +// set +// +template +unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion) +{ + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) + nSize += GetSerializeSize((*it), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const std::set& m, int nType, int nVersion) +{ + WriteCompactSize(os, m.size()); + for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) + Serialize(os, (*it), nType, nVersion); +} + +template +void Unserialize(Stream& is, std::set& m, int nType, int nVersion) +{ + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::set::iterator it = m.begin(); + for (unsigned int i = 0; i < nSize; i++) + { + K key; + Unserialize(is, key, nType, nVersion); + it = m.insert(it, key); + } +} + + + +// +// Support for IMPLEMENT_SERIALIZE and READWRITE macro +// +class CSerActionGetSerializeSize { }; +class CSerActionSerialize { }; +class CSerActionUnserialize { }; + +template +inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionGetSerializeSize ser_action) +{ + return ::GetSerializeSize(obj, nType, nVersion); +} + +template +inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionSerialize ser_action) +{ + ::Serialize(s, obj, nType, nVersion); + return 0; +} + +template +inline unsigned int SerReadWrite(Stream& s, T& obj, int nType, int nVersion, CSerActionUnserialize ser_action) +{ + ::Unserialize(s, obj, nType, nVersion); + return 0; +} + +struct ser_streamplaceholder +{ + int nType; + int nVersion; +}; + + + + + + + + + +// +// Allocator that clears its contents before deletion +// +template +struct secure_allocator : public std::allocator +{ + // MSVC8 default copy constructor is broken + typedef std::allocator base; + typedef typename base::size_type size_type; + typedef typename base::difference_type difference_type; + typedef typename base::pointer pointer; + typedef typename base::const_pointer const_pointer; + typedef typename base::reference reference; + typedef typename base::const_reference const_reference; + typedef typename base::value_type value_type; + secure_allocator() throw() {} + secure_allocator(const secure_allocator& a) throw() : base(a) {} + template + secure_allocator(const secure_allocator& a) throw() : base(a) {} + ~secure_allocator() throw() {} + template struct rebind + { typedef secure_allocator<_Other> other; }; + + void deallocate(T* p, std::size_t n) + { + if (p != NULL) + memset(p, 0, sizeof(T) * n); + std::allocator::deallocate(p, n); + } +}; + + + +// +// Double ended buffer combining vector and stream-like interfaces. +// >> and << read and write unformatted data using the above serialization templates. +// Fills with data in linear time; some stringstream implementations take N^2 time. +// +class CDataStream +{ +protected: + typedef std::vector > vector_type; + vector_type vch; + unsigned int nReadPos; + short state; + short exceptmask; +public: + int nType; + int nVersion; + + typedef vector_type::allocator_type allocator_type; + typedef vector_type::size_type size_type; + typedef vector_type::difference_type difference_type; + typedef vector_type::reference reference; + typedef vector_type::const_reference const_reference; + typedef vector_type::value_type value_type; + typedef vector_type::iterator iterator; + typedef vector_type::const_iterator const_iterator; + typedef vector_type::reverse_iterator reverse_iterator; + + explicit CDataStream(int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(pbegin, pend) + { + Init(nTypeIn, nVersionIn); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + CDataStream(const char* pbegin, const char* pend, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(pbegin, pend) + { + Init(nTypeIn, nVersionIn); + } +#endif + + CDataStream(const vector_type& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end()) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end()) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch((char*)&vchIn.begin()[0], (char*)&vchIn.end()[0]) + { + Init(nTypeIn, nVersionIn); + } + + void Init(int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) + { + nReadPos = 0; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = std::ios::badbit | std::ios::failbit; + } + + CDataStream& operator+=(const CDataStream& b) + { + vch.insert(vch.end(), b.begin(), b.end()); + return *this; + } + + friend CDataStream operator+(const CDataStream& a, const CDataStream& b) + { + CDataStream ret = a; + ret += b; + return (ret); + } + + std::string str() const + { + return (std::string(begin(), end())); + } + + + // + // Vector subset + // + const_iterator begin() const { return vch.begin() + nReadPos; } + iterator begin() { return vch.begin() + nReadPos; } + const_iterator end() const { return vch.end(); } + iterator end() { return vch.end(); } + size_type size() const { return vch.size() - nReadPos; } + bool empty() const { return vch.size() == nReadPos; } + void resize(size_type n, value_type c=0) { vch.resize(n + nReadPos, c); } + void reserve(size_type n) { vch.reserve(n + nReadPos); } + const_reference operator[](size_type pos) const { return vch[pos + nReadPos]; } + reference operator[](size_type pos) { return vch[pos + nReadPos]; } + void clear() { vch.clear(); nReadPos = 0; } + iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); } + void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); } + + void insert(iterator it, const_iterator first, const_iterator last) + { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } + else + vch.insert(it, first, last); + } + + void insert(iterator it, std::vector::const_iterator first, std::vector::const_iterator last) + { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } + else + vch.insert(it, first, last); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + void insert(iterator it, const char* first, const char* last) + { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } + else + vch.insert(it, first, last); + } +#endif + + iterator erase(iterator it) + { + if (it == vch.begin() + nReadPos) + { + // special case for erasing from the front + if (++nReadPos >= vch.size()) + { + // whenever we reach the end, we take the opportunity to clear the buffer + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } + return vch.begin() + nReadPos; + } + else + return vch.erase(it); + } + + iterator erase(iterator first, iterator last) + { + if (first == vch.begin() + nReadPos) + { + // special case for erasing from the front + if (last == vch.end()) + { + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } + else + { + nReadPos = (last - vch.begin()); + return last; + } + } + else + return vch.erase(first, last); + } + + inline void Compact() + { + vch.erase(vch.begin(), vch.begin() + nReadPos); + nReadPos = 0; + } + + bool Rewind(size_type n) + { + // Rewind by n characters if the buffer hasn't been compacted yet + if (n > nReadPos) + return false; + nReadPos -= n; + return true; + } + + + // + // Stream subset + // + void setstate(short bits, const char* psz) + { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + bool eof() const { return size() == 0; } + bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } + bool good() const { return !eof() && (state == 0); } + void clear(short n) { state = n; } // name conflict with vector clear() + short exceptions() { return exceptmask; } + short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CDataStream"); return prev; } + CDataStream* rdbuf() { return this; } + int in_avail() { return size(); } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CDataStream& read(char* pch, int nSize) + { + // Read from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) + { + if (nReadPosNext > vch.size()) + { + setstate(std::ios::failbit, "CDataStream::read() : end of data"); + memset(pch, 0, nSize); + nSize = vch.size() - nReadPos; + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = 0; + vch.clear(); + return (*this); + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream& ignore(int nSize) + { + // Ignore from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) + { + if (nReadPosNext > vch.size()) + { + setstate(std::ios::failbit, "CDataStream::ignore() : end of data"); + nSize = vch.size() - nReadPos; + } + nReadPos = 0; + vch.clear(); + return (*this); + } + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream& write(const char* pch, int nSize) + { + // Write to the end of the buffer + assert(nSize >= 0); + vch.insert(vch.end(), pch, pch + nSize); + return (*this); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const + { + // Special case: stream << stream concatenates like stream += stream + if (!vch.empty()) + s.write((char*)&vch[0], vch.size() * sizeof(vch[0])); + } + + template + unsigned int GetSerializeSize(const T& obj) + { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template + CDataStream& operator<<(const T& obj) + { + // Serialize to this stream + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template + CDataStream& operator>>(T& obj) + { + // Unserialize from this stream + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } +}; + +#ifdef TESTCDATASTREAM +// VC6sp6 +// CDataStream: +// n=1000 0 seconds +// n=2000 0 seconds +// n=4000 0 seconds +// n=8000 0 seconds +// n=16000 0 seconds +// n=32000 0 seconds +// n=64000 1 seconds +// n=128000 1 seconds +// n=256000 2 seconds +// n=512000 4 seconds +// n=1024000 8 seconds +// n=2048000 16 seconds +// n=4096000 32 seconds +// stringstream: +// n=1000 1 seconds +// n=2000 1 seconds +// n=4000 13 seconds +// n=8000 87 seconds +// n=16000 400 seconds +// n=32000 1660 seconds +// n=64000 6749 seconds +// n=128000 27241 seconds +// n=256000 109804 seconds +#include +int main(int argc, char *argv[]) +{ + vector vch(0xcc, 250); + printf("CDataStream:\n"); + for (int n = 1000; n <= 4500000; n *= 2) + { + CDataStream ss; + time_t nStart = time(NULL); + for (int i = 0; i < n; i++) + ss.write((char*)&vch[0], vch.size()); + printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); + } + printf("stringstream:\n"); + for (int n = 1000; n <= 4500000; n *= 2) + { + stringstream ss; + time_t nStart = time(NULL); + for (int i = 0; i < n; i++) + ss.write((char*)&vch[0], vch.size()); + printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); + } +} +#endif + + + + + + + + + + +// +// Automatic closing wrapper for FILE* +// - Will automatically close the file when it goes out of scope if not null. +// - If you're returning the file pointer, return file.release(). +// - If you need to close the file early, use file.fclose() instead of fclose(file). +// +class CAutoFile +{ +protected: + FILE* file; + short state; + short exceptmask; +public: + int nType; + int nVersion; + + typedef FILE element_type; + + CAutoFile(FILE* filenew=NULL, int nTypeIn=SER_DISK, int nVersionIn=VERSION) + { + file = filenew; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = std::ios::badbit | std::ios::failbit; + } + + ~CAutoFile() + { + fclose(); + } + + void fclose() + { + if (file != NULL && file != stdin && file != stdout && file != stderr) + ::fclose(file); + file = NULL; + } + + FILE* release() { FILE* ret = file; file = NULL; return ret; } + operator FILE*() { return file; } + FILE* operator->() { return file; } + FILE& operator*() { return *file; } + FILE** operator&() { return &file; } + FILE* operator=(FILE* pnew) { return file = pnew; } + bool operator!() { return (file == NULL); } + + + // + // Stream subset + // + void setstate(short bits, const char* psz) + { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } + bool good() const { return state == 0; } + void clear(short n = 0) { state = n; } + short exceptions() { return exceptmask; } + short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CAutoFile"); return prev; } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CAutoFile& read(char* pch, int nSize) + { + if (!file) + throw std::ios_base::failure("CAutoFile::read : file handle is NULL"); + if (fread(pch, 1, nSize, file) != nSize) + setstate(std::ios::failbit, feof(file) ? "CAutoFile::read : end of file" : "CAutoFile::read : fread failed"); + return (*this); + } + + CAutoFile& write(const char* pch, int nSize) + { + if (!file) + throw std::ios_base::failure("CAutoFile::write : file handle is NULL"); + if (fwrite(pch, 1, nSize, file) != nSize) + setstate(std::ios::failbit, "CAutoFile::write : write failed"); + return (*this); + } + + template + unsigned int GetSerializeSize(const T& obj) + { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template + CAutoFile& operator<<(const T& obj) + { + // Serialize to this stream + if (!file) + throw std::ios_base::failure("CAutoFile::operator<< : file handle is NULL"); + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template + CAutoFile& operator>>(T& obj) + { + // Unserialize from this stream + if (!file) + throw std::ios_base::failure("CAutoFile::operator>> : file handle is NULL"); + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } +}; + +#endif diff --git a/lib/include/uint256.h b/lib/include/uint256.h new file mode 100644 index 0000000000..356b2dc8e9 --- /dev/null +++ b/lib/include/uint256.h @@ -0,0 +1,765 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UINT256_H +#define BITCOIN_UINT256_H + +#include "serialize.h" + +#include +#include +#include + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false) ; else for +#endif + + +inline int Testuint256AdHoc(std::vector vArg); + + + +// We have to keep a separate base class without constructors +// so the compiler will let us use it in a union +template +class base_uint +{ +protected: + enum { WIDTH=BITS/32 }; + unsigned int pn[WIDTH]; +public: + + bool operator!() const + { + for (int i = 0; i < WIDTH; i++) + if (pn[i] != 0) + return false; + return true; + } + + const base_uint operator~() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + return ret; + } + + const base_uint operator-() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + ret++; + return ret; + } + + + base_uint& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + base_uint& operator^=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] ^= b.pn[i]; + return *this; + } + + base_uint& operator&=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] &= b.pn[i]; + return *this; + } + + base_uint& operator|=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] |= b.pn[i]; + return *this; + } + + base_uint& operator^=(uint64 b) + { + pn[0] ^= (unsigned int)b; + pn[1] ^= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator&=(uint64 b) + { + pn[0] &= (unsigned int)b; + pn[1] &= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator|=(uint64 b) + { + pn[0] |= (unsigned int)b; + pn[1] |= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator<<=(unsigned int shift) + { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i+k+1 < WIDTH && shift != 0) + pn[i+k+1] |= (a.pn[i] >> (32-shift)); + if (i+k < WIDTH) + pn[i+k] |= (a.pn[i] << shift); + } + return *this; + } + + base_uint& operator>>=(unsigned int shift) + { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i-k-1 >= 0 && shift != 0) + pn[i-k-1] |= (a.pn[i] << (32-shift)); + if (i-k >= 0) + pn[i-k] |= (a.pn[i] >> shift); + } + return *this; + } + + base_uint& operator+=(const base_uint& b) + { + uint64 carry = 0; + for (int i = 0; i < WIDTH; i++) + { + uint64 n = carry + pn[i] + b.pn[i]; + pn[i] = n & 0xffffffff; + carry = n >> 32; + } + return *this; + } + + base_uint& operator-=(const base_uint& b) + { + *this += -b; + return *this; + } + + base_uint& operator+=(uint64 b64) + { + base_uint b; + b = b64; + *this += b; + return *this; + } + + base_uint& operator-=(uint64 b64) + { + base_uint b; + b = b64; + *this += -b; + return *this; + } + + + base_uint& operator++() + { + // prefix operator + int i = 0; + while (++pn[i] == 0 && i < WIDTH-1) + i++; + return *this; + } + + const base_uint operator++(int) + { + // postfix operator + const base_uint ret = *this; + ++(*this); + return ret; + } + + base_uint& operator--() + { + // prefix operator + int i = 0; + while (--pn[i] == -1 && i < WIDTH-1) + i++; + return *this; + } + + const base_uint operator--(int) + { + // postfix operator + const base_uint ret = *this; + --(*this); + return ret; + } + + + friend inline bool operator<(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator<=(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator>(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator>=(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator==(const base_uint& a, const base_uint& b) + { + for (int i = 0; i < base_uint::WIDTH; i++) + if (a.pn[i] != b.pn[i]) + return false; + return true; + } + + friend inline bool operator==(const base_uint& a, uint64 b) + { + if (a.pn[0] != (unsigned int)b) + return false; + if (a.pn[1] != (unsigned int)(b >> 32)) + return false; + for (int i = 2; i < base_uint::WIDTH; i++) + if (a.pn[i] != 0) + return false; + return true; + } + + friend inline bool operator!=(const base_uint& a, const base_uint& b) + { + return (!(a == b)); + } + + friend inline bool operator!=(const base_uint& a, uint64 b) + { + return (!(a == b)); + } + + + + std::string GetHex() const + { + char psz[sizeof(pn)*2 + 1]; + for (int i = 0; i < sizeof(pn); i++) + sprintf(psz + i*2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]); + return string(psz, psz + sizeof(pn)*2); + } + + void SetHex(const char* psz) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + + // skip leading spaces + while (isspace(*psz)) + psz++; + + // skip 0x + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + + // hex string to uint + static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; + const char* pbegin = psz; + while (phexdigit[*psz] || *psz == '0') + psz++; + psz--; + unsigned char* p1 = (unsigned char*)pn; + unsigned char* pend = p1 + WIDTH * 4; + while (psz >= pbegin && p1 < pend) + { + *p1 = phexdigit[(unsigned char)*psz--]; + if (psz >= pbegin) + { + *p1 |= (phexdigit[(unsigned char)*psz--] << 4); + p1++; + } + } + } + + void SetHex(const std::string& str) + { + SetHex(str.c_str()); + } + + std::string ToString() const + { + return (GetHex()); + } + + unsigned char* begin() + { + return (unsigned char*)&pn[0]; + } + + unsigned char* end() + { + return (unsigned char*)&pn[WIDTH]; + } + + unsigned int size() + { + return sizeof(pn); + } + + + unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const + { + return sizeof(pn); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const + { + s.write((char*)pn, sizeof(pn)); + } + + template + void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) + { + s.read((char*)pn, sizeof(pn)); + } + + + friend class uint160; + friend class uint256; + friend inline int Testuint256AdHoc(std::vector vArg); +}; + +typedef base_uint<160> base_uint160; +typedef base_uint<256> base_uint256; + + + +// +// uint160 and uint256 could be implemented as templates, but to keep +// compile errors and debugging cleaner, they're copy and pasted. +// + + + +////////////////////////////////////////////////////////////////////////////// +// +// uint160 +// + +class uint160 : public base_uint160 +{ +public: + typedef base_uint160 basetype; + + uint160() + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint160(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint160& operator=(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint160(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint160& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint160(const std::string& str) + { + SetHex(str); + } + + explicit uint160(const std::vector& vch) + { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint160& a, uint64 b) { return (base_uint160)a == b; } +inline bool operator!=(const uint160& a, uint64 b) { return (base_uint160)a != b; } +inline const uint160 operator<<(const base_uint160& a, unsigned int shift) { return uint160(a) <<= shift; } +inline const uint160 operator>>(const base_uint160& a, unsigned int shift) { return uint160(a) >>= shift; } +inline const uint160 operator<<(const uint160& a, unsigned int shift) { return uint160(a) <<= shift; } +inline const uint160 operator>>(const uint160& a, unsigned int shift) { return uint160(a) >>= shift; } + +inline const uint160 operator^(const base_uint160& a, const base_uint160& b) { return uint160(a) ^= b; } +inline const uint160 operator&(const base_uint160& a, const base_uint160& b) { return uint160(a) &= b; } +inline const uint160 operator|(const base_uint160& a, const base_uint160& b) { return uint160(a) |= b; } +inline const uint160 operator+(const base_uint160& a, const base_uint160& b) { return uint160(a) += b; } +inline const uint160 operator-(const base_uint160& a, const base_uint160& b) { return uint160(a) -= b; } + +inline bool operator<(const base_uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const base_uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const base_uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const base_uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const base_uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const base_uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const base_uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const base_uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const base_uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const base_uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const base_uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160& a, const base_uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160& a, const base_uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160& a, const base_uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160& a, const base_uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160& a, const base_uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160& a, const base_uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160& a, const base_uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160& a, const base_uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160& a, const base_uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160& a, const base_uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160& a, const base_uint160& b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// uint256 +// + +class uint256 : public base_uint256 +{ +public: + typedef base_uint256 basetype; + + uint256() + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint256(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint256& operator=(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint256(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint256& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint256(const std::string& str) + { + SetHex(str); + } + + explicit uint256(const std::vector& vch) + { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint256& a, uint64 b) { return (base_uint256)a == b; } +inline bool operator!=(const uint256& a, uint64 b) { return (base_uint256)a != b; } +inline const uint256 operator<<(const base_uint256& a, unsigned int shift) { return uint256(a) <<= shift; } +inline const uint256 operator>>(const base_uint256& a, unsigned int shift) { return uint256(a) >>= shift; } +inline const uint256 operator<<(const uint256& a, unsigned int shift) { return uint256(a) <<= shift; } +inline const uint256 operator>>(const uint256& a, unsigned int shift) { return uint256(a) >>= shift; } + +inline const uint256 operator^(const base_uint256& a, const base_uint256& b) { return uint256(a) ^= b; } +inline const uint256 operator&(const base_uint256& a, const base_uint256& b) { return uint256(a) &= b; } +inline const uint256 operator|(const base_uint256& a, const base_uint256& b) { return uint256(a) |= b; } +inline const uint256 operator+(const base_uint256& a, const base_uint256& b) { return uint256(a) += b; } +inline const uint256 operator-(const base_uint256& a, const base_uint256& b) { return uint256(a) -= b; } + +inline bool operator<(const base_uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const base_uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const base_uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const base_uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const base_uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const base_uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const base_uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const base_uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const base_uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const base_uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const base_uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } + +inline bool operator<(const uint256& a, const base_uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const uint256& a, const base_uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const uint256& a, const base_uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const uint256& a, const base_uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const uint256& a, const base_uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const uint256& a, const base_uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const uint256& a, const base_uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const uint256& a, const base_uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const uint256& a, const base_uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const uint256& a, const base_uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const uint256& a, const base_uint256& b) { return (base_uint256)a - (base_uint256)b; } + +inline bool operator<(const uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } + + + + + + + + + + + + +inline int Testuint256AdHoc(vector vArg) +{ + uint256 g(0); + + + printf("%s\n", g.ToString().c_str()); + g--; printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g--; printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + + + + uint256 a(7); + printf("a=7\n"); + printf("%s\n", a.ToString().c_str()); + + uint256 b; + printf("b undefined\n"); + printf("%s\n", b.ToString().c_str()); + int c = 3; + + a = c; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + uint256 k(c); + + a = 5; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + b = 1; + b <<= 52; + + a |= b; + + a ^= 0x500; + + printf("a %s\n", a.ToString().c_str()); + + a = a | b | (uint256)0x1000; + + + printf("a %s\n", a.ToString().c_str()); + printf("b %s\n", b.ToString().c_str()); + + a = 0xfffffffe; + a.pn[4] = 9; + + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + uint256 d = a--; + printf("%s\n", d.ToString().c_str()); + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + + d = a; + + printf("%s\n", d.ToString().c_str()); + for (int i = uint256::WIDTH-1; i >= 0; i--) printf("%08x", d.pn[i]); printf("\n"); + + uint256 neg = d; + neg = ~neg; + printf("%s\n", neg.ToString().c_str()); + + + uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + printf("\n"); + printf("%s\n", e.ToString().c_str()); + + + printf("\n"); + uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + uint256 x2; + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) + { + x2 = x1 << i; + printf("%s\n", x2.ToString().c_str()); + } + + printf("\n"); + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) + { + x2 = x1; + x2 >>= i; + printf("%s\n", x2.ToString().c_str()); + } + + + for (int i = 0; i < 100; i++) + { + uint256 k = (~uint256(0) >> i); + printf("%s\n", k.ToString().c_str()); + } + + for (int i = 0; i < 100; i++) + { + uint256 k = (~uint256(0) << i); + printf("%s\n", k.ToString().c_str()); + } + + return (0); +} + +#endif diff --git a/lib/include/util.h b/lib/include/util.h new file mode 100644 index 0000000000..af8cfcf659 --- /dev/null +++ b/lib/include/util.h @@ -0,0 +1,673 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UTIL_H +#define BITCOIN_UTIL_H + +#include "uint256.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false) ; else for +#endif +#ifndef _MSC_VER +#define __forceinline inline +#endif + +//#define foreach BOOST_FOREACH +#define loop for (;;) +#define BEGIN(a) ((char*)&(a)) +#define END(a) ((char*)&((&(a))[1])) +#define UBEGIN(a) ((unsigned char*)&(a)) +#define UEND(a) ((unsigned char*)&((&(a))[1])) +#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) +#define printf OutputDebugStringF + +#ifdef snprintf +#undef snprintf +#endif +#define snprintf my_snprintf + +#ifndef PRI64d +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MSVCRT__) +#define PRI64d "I64d" +#define PRI64u "I64u" +#define PRI64x "I64x" +#else +#define PRI64d "lld" +#define PRI64u "llu" +#define PRI64x "llx" +#endif +#endif + +// This is needed because the foreach macro can't get over the comma in pair +#define PAIRTYPE(t1, t2) pair + +// Used to bypass the rule against non-const reference to temporary +// where it makes sense with wrappers such as CFlatData or CTxDB +template +inline T& REF(const T& val) +{ + return (T&)val; +} + +// Align by increasing pointer, must have extra space at end of buffer +template +T* alignup(T* p) +{ + union + { + T* ptr; + size_t n; + } u; + u.ptr = p; + u.n = (u.n + (nBytes-1)) & ~(nBytes-1); + return u.ptr; +} + +#ifdef __WXMSW__ +#define MSG_NOSIGNAL 0 +#define MSG_DONTWAIT 0 +#ifndef UINT64_MAX +#define UINT64_MAX _UI64_MAX +#define INT64_MAX _I64_MAX +#define INT64_MIN _I64_MIN +#endif +#ifndef S_IRUSR +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#endif +#define unlink _unlink +typedef int socklen_t; +#else +#define WSAGetLastError() errno +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEMSGSIZE EMSGSIZE +#define WSAEINTR EINTR +#define WSAEINPROGRESS EINPROGRESS +#define WSAEADDRINUSE EADDRINUSE +#define WSAENOTSOCK EBADF +#define INVALID_SOCKET (SOCKET)(~0) +#define SOCKET_ERROR -1 +typedef u_int SOCKET; +#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) +#define strlwr(psz) to_lower(psz) +#define _strlwr(psz) to_lower(psz) +#define MAX_PATH 1024 +#define Beep(n1,n2) (0) +inline void Sleep(int64 n) +{ + boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(n)); +} +#endif + +inline int myclosesocket(SOCKET& hSocket) +{ + if (hSocket == INVALID_SOCKET) + return WSAENOTSOCK; +#ifdef __WXMSW__ + int ret = closesocket(hSocket); +#else + int ret = close(hSocket); +#endif + hSocket = INVALID_SOCKET; + return ret; +} +#define closesocket(s) myclosesocket(s) + +#ifndef GUI +inline const char* _(const char* psz) +{ + return psz; +} +#endif + + + + + + + + + + +extern std::map mapArgs; +extern std::map > mapMultiArgs; +extern bool fDebug; +extern bool fPrintToConsole; +extern bool fPrintToDebugger; +extern char pszSetDataDir[MAX_PATH]; +extern bool fRequestShutdown; +extern bool fShutdown; +extern bool fDaemon; +extern bool fServer; +extern bool fCommandLine; +extern std::string strMiscWarning; +extern bool fTestNet; +extern bool fNoListen; +extern bool fLogTimestamps; + +void RandAddSeed(); +void RandAddSeedPerfmon(); +int OutputDebugStringF(const char* pszFormat, ...); +int my_snprintf(char* buffer, size_t limit, const char* format, ...); +std::string strprintf(const char* format, ...); +bool error(const char* format, ...); +void LogException(std::exception* pex, const char* pszThread); +void PrintException(std::exception* pex, const char* pszThread); +void PrintExceptionContinue(std::exception* pex, const char* pszThread); +void ParseString(const std::string& str, char c, std::vector& v); +std::string FormatMoney(int64 n, bool fPlus=false); +bool ParseMoney(const std::string& str, int64& nRet); +bool ParseMoney(const char* pszIn, int64& nRet); +std::vector ParseHex(const char* psz); +std::vector ParseHex(const std::string& str); +void ParseParameters(int argc, char* argv[]); +const char* wxGetTranslation(const char* psz); +bool WildcardMatch(const char* psz, const char* mask); +bool WildcardMatch(const std::string& str, const std::string& mask); +int GetFilesize(FILE* file); +void GetDataDir(char* pszDirRet); +std::string GetConfigFile(); +std::string GetPidFile(); +void CreatePidFile(std::string pidFile, pid_t pid); +void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); +#ifdef __WXMSW__ +string MyGetSpecialFolderPath(int nFolder, bool fCreate); +#endif +std::string GetDefaultDataDir(); +std::string GetDataDir(); +void ShrinkDebugFile(); +int GetRandInt(int nMax); +uint64 GetRand(uint64 nMax); +int64 GetTime(); +int64 GetAdjustedTime(); +void AddTimeData(unsigned int ip, int64 nTime); +std::string FormatFullVersion(); + + + + + + + + + + + + + +// Wrapper to automatically initialize critical sections +class CCriticalSection +{ +#ifdef __WXMSW__ +protected: + CRITICAL_SECTION cs; +public: + explicit CCriticalSection() { InitializeCriticalSection(&cs); } + ~CCriticalSection() { DeleteCriticalSection(&cs); } + void Enter() { EnterCriticalSection(&cs); } + void Leave() { LeaveCriticalSection(&cs); } + bool TryEnter() { return TryEnterCriticalSection(&cs); } +#else +protected: + boost::interprocess::interprocess_recursive_mutex mutex; +public: + explicit CCriticalSection() { } + ~CCriticalSection() { } + void Enter() { mutex.lock(); } + void Leave() { mutex.unlock(); } + bool TryEnter() { return mutex.try_lock(); } +#endif +public: + const char* pszFile; + int nLine; +}; + +// Automatically leave critical section when leaving block, needed for exception safety +class CCriticalBlock +{ +protected: + CCriticalSection* pcs; +public: + CCriticalBlock(CCriticalSection& csIn) { pcs = &csIn; pcs->Enter(); } + ~CCriticalBlock() { pcs->Leave(); } +}; + +// WARNING: This will catch continue and break! +// break is caught with an assertion, but there's no way to detect continue. +// I'd rather be careful than suffer the other more error prone syntax. +// The compiler will optimise away all this loop junk. +#define CRITICAL_BLOCK(cs) \ + for (bool fcriticalblockonce=true; fcriticalblockonce; assert(("break caught by CRITICAL_BLOCK!", !fcriticalblockonce)), fcriticalblockonce=false) \ + for (CCriticalBlock criticalblock(cs); fcriticalblockonce && (cs.pszFile=__FILE__, cs.nLine=__LINE__, true); fcriticalblockonce=false, cs.pszFile=NULL, cs.nLine=0) + +class CTryCriticalBlock +{ +protected: + CCriticalSection* pcs; +public: + CTryCriticalBlock(CCriticalSection& csIn) { pcs = (csIn.TryEnter() ? &csIn : NULL); } + ~CTryCriticalBlock() { if (pcs) pcs->Leave(); } + bool Entered() { return pcs != NULL; } +}; + +#define TRY_CRITICAL_BLOCK(cs) \ + for (bool fcriticalblockonce=true; fcriticalblockonce; assert(("break caught by TRY_CRITICAL_BLOCK!", !fcriticalblockonce)), fcriticalblockonce=false) \ + for (CTryCriticalBlock criticalblock(cs); fcriticalblockonce && (fcriticalblockonce = criticalblock.Entered()) && (cs.pszFile=__FILE__, cs.nLine=__LINE__, true); fcriticalblockonce=false, cs.pszFile=NULL, cs.nLine=0) + + + + + + + + + + +inline std::string i64tostr(int64 n) +{ + return strprintf("%"PRI64d, n); +} + +inline std::string itostr(int n) +{ + return strprintf("%d", n); +} + +inline int64 atoi64(const char* psz) +{ +#ifdef _MSC_VER + return _atoi64(psz); +#else + return strtoll(psz, NULL, 10); +#endif +} + +inline int64 atoi64(const std::string& str) +{ +#ifdef _MSC_VER + return _atoi64(str.c_str()); +#else + return strtoll(str.c_str(), NULL, 10); +#endif +} + +inline int atoi(const std::string& str) +{ + return atoi(str.c_str()); +} + +inline int roundint(double d) +{ + return (int)(d > 0 ? d + 0.5 : d - 0.5); +} + +inline int64 roundint64(double d) +{ + return (int64)(d > 0 ? d + 0.5 : d - 0.5); +} + +inline int64 abs64(int64 n) +{ + return (n >= 0 ? n : -n); +} + +template +std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) +{ + if (itbegin == itend) + return ""; + const unsigned char* pbegin = (const unsigned char*)&itbegin[0]; + const unsigned char* pend = pbegin + (itend - itbegin) * sizeof(itbegin[0]); + std::string str; + str.reserve((pend-pbegin) * (fSpaces ? 3 : 2)); + for (const unsigned char* p = pbegin; p != pend; p++) + str += strprintf((fSpaces && p != pend-1 ? "%02x " : "%02x"), *p); + return str; +} + +inline std::string HexStr(const std::vector& vch, bool fSpaces=false) +{ + return HexStr(vch.begin(), vch.end(), fSpaces); +} + +template +std::string HexNumStr(const T itbegin, const T itend, bool f0x=true) +{ + if (itbegin == itend) + return ""; + const unsigned char* pbegin = (const unsigned char*)&itbegin[0]; + const unsigned char* pend = pbegin + (itend - itbegin) * sizeof(itbegin[0]); + std::string str = (f0x ? "0x" : ""); + str.reserve(str.size() + (pend-pbegin) * 2); + for (const unsigned char* p = pend-1; p >= pbegin; p--) + str += strprintf("%02x", *p); + return str; +} + +inline std::string HexNumStr(const std::vector& vch, bool f0x=true) +{ + return HexNumStr(vch.begin(), vch.end(), f0x); +} + +template +void PrintHex(const T pbegin, const T pend, const char* pszFormat="%s", bool fSpaces=true) +{ + printf(pszFormat, HexStr(pbegin, pend, fSpaces).c_str()); +} + +inline void PrintHex(const std::vector& vch, const char* pszFormat="%s", bool fSpaces=true) +{ + printf(pszFormat, HexStr(vch, fSpaces).c_str()); +} + +inline int64 GetPerformanceCounter() +{ + int64 nCounter = 0; +#ifdef __WXMSW__ + QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); +#else + timeval t; + gettimeofday(&t, NULL); + nCounter = t.tv_sec * 1000000 + t.tv_usec; +#endif + return nCounter; +} + +inline int64 GetTimeMillis() +{ + return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - + boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); +} + +inline std::string DateTimeStrFormat(const char* pszFormat, int64 nTime) +{ + time_t n = nTime; + struct tm* ptmTime = gmtime(&n); + char pszTime[200]; + strftime(pszTime, sizeof(pszTime), pszFormat, ptmTime); + return pszTime; +} + +template +void skipspaces(T& it) +{ + while (isspace(*it)) + ++it; +} + +inline bool IsSwitchChar(char c) +{ +#ifdef __WXMSW__ + return c == '-' || c == '/'; +#else + return c == '-'; +#endif +} + +inline std::string GetArg(const std::string& strArg, const std::string& strDefault) +{ + if (mapArgs.count(strArg)) + return mapArgs[strArg]; + return strDefault; +} + +inline int64 GetArg(const std::string& strArg, int64 nDefault) +{ + if (mapArgs.count(strArg)) + return atoi64(mapArgs[strArg]); + return nDefault; +} + +inline bool GetBoolArg(const std::string& strArg) +{ + if (mapArgs.count(strArg)) + { + if (mapArgs[strArg].empty()) + return true; + return (atoi(mapArgs[strArg]) != 0); + } + return false; +} + + + + + + + + + + +inline void heapchk() +{ +#ifdef __WXMSW__ + /// for debugging + //if (_heapchk() != _HEAPOK) + // DebugBreak(); +#endif +} + +// Randomize the stack to help protect against buffer overrun exploits +#define IMPLEMENT_RANDOMIZE_STACK(ThreadFn) \ + { \ + static char nLoops; \ + if (nLoops <= 0) \ + nLoops = GetRand(20) + 1; \ + if (nLoops-- > 1) \ + { \ + ThreadFn; \ + return; \ + } \ + } + +#define CATCH_PRINT_EXCEPTION(pszFn) \ + catch (std::exception& e) { \ + PrintException(&e, (pszFn)); \ + } catch (...) { \ + PrintException(NULL, (pszFn)); \ + } + + + + + + + + + + +template +inline uint256 Hash(const T1 pbegin, const T1 pend) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +inline uint256 Hash(const T1 p1begin, const T1 p1end, + const T2 p2begin, const T2 p2end) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); + SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); + SHA256_Final((unsigned char*)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +inline uint256 Hash(const T1 p1begin, const T1 p1end, + const T2 p2begin, const T2 p2end, + const T3 p3begin, const T3 p3end) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); + SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); + SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0])); + SHA256_Final((unsigned char*)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=VERSION) +{ + // Most of the time is spent allocating and deallocating CDataStream's + // buffer. If this ever needs to be optimized further, make a CStaticStream + // class with its buffer on the stack. + CDataStream ss(nType, nVersion); + ss.reserve(10000); + ss << obj; + return Hash(ss.begin(), ss.end()); +} + +inline uint160 Hash160(const vector& vch) +{ + uint256 hash1; + SHA256(&vch[0], vch.size(), (unsigned char*)&hash1); + uint160 hash2; + RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + + + + + + + + + + + +// Note: It turns out we might have been able to use boost::thread +// by using TerminateThread(boost::thread.native_handle(), 0); +#ifdef __WXMSW__ +typedef HANDLE pthread_t; + +inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) +{ + DWORD nUnused = 0; + HANDLE hthread = + CreateThread( + NULL, // default security + 0, // inherit stack size from parent + (LPTHREAD_START_ROUTINE)pfn, // function pointer + parg, // argument + 0, // creation option, start immediately + &nUnused); // thread identifier + if (hthread == NULL) + { + printf("Error: CreateThread() returned %d\n", GetLastError()); + return (pthread_t)0; + } + if (!fWantHandle) + { + CloseHandle(hthread); + return (pthread_t)-1; + } + return hthread; +} + +inline void SetThreadPriority(int nPriority) +{ + SetThreadPriority(GetCurrentThread(), nPriority); +} +#else +inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) +{ + pthread_t hthread = 0; + int ret = pthread_create(&hthread, NULL, (void*(*)(void*))pfn, parg); + if (ret != 0) + { + printf("Error: pthread_create() returned %d\n", ret); + return (pthread_t)0; + } + if (!fWantHandle) + return (pthread_t)-1; + return hthread; +} + +#define THREAD_PRIORITY_LOWEST PRIO_MAX +#define THREAD_PRIORITY_BELOW_NORMAL 2 +#define THREAD_PRIORITY_NORMAL 0 +#define THREAD_PRIORITY_ABOVE_NORMAL 0 + +inline void SetThreadPriority(int nPriority) +{ + // It's unclear if it's even possible to change thread priorities on Linux, + // but we really and truly need it for the generation threads. +#ifdef PRIO_THREAD + setpriority(PRIO_THREAD, 0, nPriority); +#else + setpriority(PRIO_PROCESS, 0, nPriority); +#endif +} + +inline bool TerminateThread(pthread_t hthread, unsigned int nExitCode) +{ + return (pthread_cancel(hthread) == 0); +} + +inline void ExitThread(unsigned int nExitCode) +{ + pthread_exit((void*)nExitCode); +} +#endif + + + + + +inline bool AffinityBugWorkaround(void(*pfn)(void*)) +{ +#ifdef __WXMSW__ + // Sometimes after a few hours affinity gets stuck on one processor + DWORD dwProcessAffinityMask = -1; + DWORD dwSystemAffinityMask = -1; + GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask); + DWORD dwPrev1 = SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinityMask); + DWORD dwPrev2 = SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinityMask); + if (dwPrev2 != dwProcessAffinityMask) + { + printf("AffinityBugWorkaround() : SetThreadAffinityMask=%d, ProcessAffinityMask=%d, restarting thread\n", dwPrev2, dwProcessAffinityMask); + if (!CreateThread(pfn, NULL)) + printf("Error: CreateThread() failed\n"); + return true; + } +#endif + return false; +} + +#endif diff --git a/mainoptionspage.cpp b/mainoptionspage.cpp deleted file mode 100644 index 021e1e470f..0000000000 --- a/mainoptionspage.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "mainoptionspage.h" - -#include -#include -#include -#include -#include - -MainOptionsPage::MainOptionsPage(QWidget *parent): - QWidget(parent) -{ - QVBoxLayout *layout = new QVBoxLayout(); - - QCheckBox *bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup")); - layout->addWidget(bitcoin_at_startup); - - QCheckBox *minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar")); - layout->addWidget(minimize_to_tray); - - QCheckBox *map_port_upnp = new QCheckBox(tr("Map port using &UPnP")); - layout->addWidget(map_port_upnp); - - QCheckBox *minimize_on_close = new QCheckBox(tr("M&inimize on close")); - layout->addWidget(minimize_on_close); - - QCheckBox *connect_socks4 = new QCheckBox(tr("&Connect through socks4 proxy:")); - layout->addWidget(connect_socks4); - - QHBoxLayout *proxy_hbox = new QHBoxLayout(); - proxy_hbox->addSpacing(18); - QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP: ")); - proxy_hbox->addWidget(proxy_ip_label); - QLineEdit *proxy_ip = new QLineEdit(); - proxy_ip->setMaximumWidth(140); - proxy_ip_label->setBuddy(proxy_ip); - proxy_hbox->addWidget(proxy_ip); - QLabel *proxy_port_label = new QLabel(tr("&Port: ")); - proxy_hbox->addWidget(proxy_port_label); - QLineEdit *proxy_port = new QLineEdit(); - proxy_port->setMaximumWidth(55); - proxy_port_label->setBuddy(proxy_port); - proxy_hbox->addWidget(proxy_port); - proxy_hbox->addStretch(1); - - layout->addLayout(proxy_hbox); - QLabel *fee_help = new QLabel(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended.")); - fee_help->setWordWrap(true); - layout->addWidget(fee_help); - - QHBoxLayout *fee_hbox = new QHBoxLayout(); - fee_hbox->addSpacing(18); - QLabel *fee_label = new QLabel(tr("Pay transaction &fee")); - fee_hbox->addWidget(fee_label); - QLineEdit *fee_edit = new QLineEdit(); - fee_edit->setMaximumWidth(70); - fee_label->setBuddy(fee_edit); - fee_hbox->addWidget(fee_edit); - fee_hbox->addStretch(1); - - layout->addLayout(fee_hbox); - - - layout->addStretch(1); /* Extra space at bottom */ - - setLayout(layout); -} - diff --git a/mainoptionspage.h b/mainoptionspage.h deleted file mode 100644 index de2ef9fcd0..0000000000 --- a/mainoptionspage.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef MAINOPTIONSPAGE_H -#define MAINOPTIONSPAGE_H - -#include - -class MainOptionsPage : public QWidget -{ - Q_OBJECT -public: - explicit MainOptionsPage(QWidget *parent = 0); - -signals: - -public slots: - -}; - -#endif // MAINOPTIONSPAGE_H diff --git a/optionsdialog.cpp b/optionsdialog.cpp deleted file mode 100644 index 70dd86323e..0000000000 --- a/optionsdialog.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "optionsdialog.h" -#include "mainoptionspage.h" - -#include -#include -#include -#include -#include - -OptionsDialog::OptionsDialog(QWidget *parent) : - QDialog(parent), contents_widget(0), pages_widget(0) -{ - contents_widget = new QListWidget(); - contents_widget->setMaximumWidth(128); - - pages_widget = new QStackedWidget(); - pages_widget->setMinimumWidth(300); - - QListWidgetItem *item_main = new QListWidgetItem(tr("Main")); - contents_widget->addItem(item_main); - pages_widget->addWidget(new MainOptionsPage(this)); - - contents_widget->setCurrentRow(0); - - QHBoxLayout *main_layout = new QHBoxLayout(); - main_layout->addWidget(contents_widget); - main_layout->addWidget(pages_widget, 1); - - QVBoxLayout *layout = new QVBoxLayout(); - layout->addLayout(main_layout); - - QHBoxLayout *buttons = new QHBoxLayout(); - buttons->addStretch(1); - QPushButton *ok_button = new QPushButton(tr("OK")); - buttons->addWidget(ok_button); - QPushButton *cancel_button = new QPushButton(tr("Cancel")); - buttons->addWidget(cancel_button); - QPushButton *apply_button = new QPushButton(tr("Apply")); - buttons->addWidget(apply_button); - - layout->addLayout(buttons); - - - setLayout(layout); - setWindowTitle(tr("Options")); - - -} - -void OptionsDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) -{ - Q_UNUSED(previous); - if(current) - { - pages_widget->setCurrentIndex(contents_widget->row(current)); - } -} diff --git a/optionsdialog.h b/optionsdialog.h deleted file mode 100644 index 501c82e9f3..0000000000 --- a/optionsdialog.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef OPTIONSDIALOG_H -#define OPTIONSDIALOG_H - -#include - -QT_BEGIN_NAMESPACE -class QStackedWidget; -class QListWidget; -class QListWidgetItem; -QT_END_NAMESPACE - -class OptionsDialog : public QDialog -{ - Q_OBJECT -public: - explicit OptionsDialog(QWidget *parent = 0); - -signals: - -public slots: - void changePage(QListWidgetItem *current, QListWidgetItem *previous); -private: - QListWidget *contents_widget; - QStackedWidget *pages_widget; - - void setupMainPage(); -}; - -#endif // OPTIONSDIALOG_H diff --git a/res/icons/address-book.png b/res/icons/address-book.png deleted file mode 100644 index abfb3c3a51..0000000000 Binary files a/res/icons/address-book.png and /dev/null differ diff --git a/res/icons/bitcoin.png b/res/icons/bitcoin.png deleted file mode 100644 index 09520aca23..0000000000 Binary files a/res/icons/bitcoin.png and /dev/null differ diff --git a/res/icons/quit.png b/res/icons/quit.png deleted file mode 100644 index fb510fbea6..0000000000 Binary files a/res/icons/quit.png and /dev/null differ diff --git a/res/icons/send.png b/res/icons/send.png deleted file mode 100644 index 0ba5359d7b..0000000000 Binary files a/res/icons/send.png and /dev/null differ diff --git a/res/icons/toolbar.png b/res/icons/toolbar.png deleted file mode 100644 index f2dcb20636..0000000000 Binary files a/res/icons/toolbar.png and /dev/null differ diff --git a/res/images/about.png b/res/images/about.png deleted file mode 100644 index c9ab9511ef..0000000000 Binary files a/res/images/about.png and /dev/null differ diff --git a/sendcoinsdialog.cpp b/sendcoinsdialog.cpp deleted file mode 100644 index 6f459fb669..0000000000 --- a/sendcoinsdialog.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "sendcoinsdialog.h" -#include "ui_sendcoinsdialog.h" - -#include "addressbookdialog.h" -#include "bitcoinaddressvalidator.h" - -#include -#include - -SendCoinsDialog::SendCoinsDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::SendCoinsDialog) -{ - ui->setupUi(this); - ui->payTo->setValidator(new BitcoinAddressValidator(this)); - ui->payAmount->setValidator(new QDoubleValidator(this)); -} - -SendCoinsDialog::~SendCoinsDialog() -{ - delete ui; -} - -void SendCoinsDialog::on_sendButton_clicked() -{ - accept(); -} - -void SendCoinsDialog::on_pasteButton_clicked() -{ - /* Paste text from clipboard into recipient field */ - ui->payTo->setText(QApplication::clipboard()->text()); -} - -void SendCoinsDialog::on_addressBookButton_clicked() -{ - AddressBookDialog dlg; - dlg.exec(); - ui->payTo->setText(dlg.getReturnValue()); -} - -void SendCoinsDialog::on_buttonBox_rejected() -{ - reject(); -} diff --git a/sendcoinsdialog.h b/sendcoinsdialog.h deleted file mode 100644 index a2fcdd0762..0000000000 --- a/sendcoinsdialog.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef SENDCOINSDIALOG_H -#define SENDCOINSDIALOG_H - -#include - -namespace Ui { - class SendCoinsDialog; -} - -class SendCoinsDialog : public QDialog -{ - Q_OBJECT - -public: - explicit SendCoinsDialog(QWidget *parent = 0); - ~SendCoinsDialog(); - -private: - Ui::SendCoinsDialog *ui; - -private slots: - void on_buttonBox_rejected(); - void on_addressBookButton_clicked(); - void on_pasteButton_clicked(); - void on_sendButton_clicked(); -}; - -#endif // SENDCOINSDIALOG_H diff --git a/sendcoinsdialog.ui b/sendcoinsdialog.ui deleted file mode 100644 index f14ec2af1b..0000000000 --- a/sendcoinsdialog.ui +++ /dev/null @@ -1,167 +0,0 @@ - - - SendCoinsDialog - - - - 0 - 0 - 736 - 140 - - - - Send Coins - - - - - - - - &Amount: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - payAmount - - - - - - - Pay &To: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - payTo - - - - - - - 34 - - - - - - - - 145 - 16777215 - - - - - - - - &Paste - - - - - - - Address &Book... - - - - - - - - 9 - - - - Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - &Send - - - - :/icons/send:/icons/send - - - - - - - - 0 - 0 - - - - QDialogButtonBox::Cancel - - - - - - - - - - - - - payAmount - returnPressed() - sendButton - click() - - - 191 - 65 - - - 570 - 121 - - - - - diff --git a/transactiontablemodel.cpp b/transactiontablemodel.cpp deleted file mode 100644 index ee58ecba40..0000000000 --- a/transactiontablemodel.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "transactiontablemodel.h" - -#include - -const QString TransactionTableModel::Sent = "s"; -const QString TransactionTableModel::Received = "r"; -const QString TransactionTableModel::Generated = "g"; - -/* Credit and Debit columns are right-aligned as they contain numbers */ -static int column_alignments[] = { - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignRight|Qt::AlignVCenter, - Qt::AlignRight|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter - }; - -TransactionTableModel::TransactionTableModel(QObject *parent): - QAbstractTableModel(parent) -{ - columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit") << tr("Type"); -} - -int TransactionTableModel::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return 5; -} - -int TransactionTableModel::columnCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return columns.length(); -} - -QVariant TransactionTableModel::data(const QModelIndex &index, int role) const -{ - if(!index.isValid()) - return QVariant(); - - if(role == Qt::DisplayRole) - { - /* index.row(), index.column() */ - /* Return QString */ - return QLocale::system().toString(index.row()); - } else if (role == Qt::TextAlignmentRole) - { - return column_alignments[index.column()]; - } else if (role == Qt::UserRole) - { - /* user role: transaction type for filtering - "s" (sent) - "r" (received) - "g" (generated) - */ - switch(index.row() % 3) - { - case 0: return QString("s"); - case 1: return QString("r"); - case 2: return QString("o"); - } - } - return QVariant(); -} - -QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if(role == Qt::DisplayRole) - { - if(orientation == Qt::Horizontal) - { - return columns[section]; - } - } else if (role == Qt::TextAlignmentRole) - { - return column_alignments[section]; - } - return QVariant(); -} - -Qt::ItemFlags TransactionTableModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::ItemIsEnabled; - - return QAbstractTableModel::flags(index); -} diff --git a/transactiontablemodel.h b/transactiontablemodel.h deleted file mode 100644 index 77ad730673..0000000000 --- a/transactiontablemodel.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef TRANSACTIONTABLEMODEL_H -#define TRANSACTIONTABLEMODEL_H - -#include -#include - -class TransactionTableModel : public QAbstractTableModel -{ - Q_OBJECT -public: - explicit TransactionTableModel(QObject *parent = 0); - - enum { - Status = 0, - Date = 1, - Description = 2, - Debit = 3, - Credit = 4, - Type = 5 - } ColumnIndex; - - /* Transaction type */ - static const QString Sent; - static const QString Received; - static const QString Generated; - - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - Qt::ItemFlags flags(const QModelIndex &index) const; -private: - QStringList columns; -}; - -#endif - -- cgit v1.2.3 From 6644d98d9eeaa24c197d18665b1461b8cd4300ea Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 14 May 2011 11:18:39 +0200 Subject: integration phase --- bitcoin.pro | 19 +- cryptopp/include/cryptopp/config.h | 462 +++++++++ cryptopp/include/cryptopp/cpu.h | 263 ++++++ cryptopp/include/cryptopp/cryptlib.h | 1668 +++++++++++++++++++++++++++++++++ cryptopp/include/cryptopp/iterhash.h | 29 + cryptopp/include/cryptopp/misc.h | 1134 ++++++++++++++++++++++ cryptopp/include/cryptopp/pch.h | 21 + cryptopp/include/cryptopp/secblock.h | 501 ++++++++++ cryptopp/include/cryptopp/sha.h | 63 ++ cryptopp/include/cryptopp/simple.h | 1 + cryptopp/include/cryptopp/smartptr.h | 223 +++++ cryptopp/include/cryptopp/stdcpp.h | 27 + cryptopp/src/cpu.cpp | 199 ++++ cryptopp/src/sha.cpp | 899 ++++++++++++++++++ gui/include/bitcoinaddressvalidator.h | 2 +- gui/src/bitcoinaddressvalidator.cpp | 4 +- gui/src/sendcoinsdialog.cpp | 1 + lib/include/bignum.h | 4 +- lib/include/uint256.h | 4 +- lib/include/util.h | 8 +- 20 files changed, 5521 insertions(+), 11 deletions(-) create mode 100644 cryptopp/include/cryptopp/config.h create mode 100644 cryptopp/include/cryptopp/cpu.h create mode 100644 cryptopp/include/cryptopp/cryptlib.h create mode 100644 cryptopp/include/cryptopp/iterhash.h create mode 100644 cryptopp/include/cryptopp/misc.h create mode 100644 cryptopp/include/cryptopp/pch.h create mode 100644 cryptopp/include/cryptopp/secblock.h create mode 100644 cryptopp/include/cryptopp/sha.h create mode 100644 cryptopp/include/cryptopp/simple.h create mode 100644 cryptopp/include/cryptopp/smartptr.h create mode 100644 cryptopp/include/cryptopp/stdcpp.h create mode 100644 cryptopp/src/cpu.cpp create mode 100644 cryptopp/src/sha.cpp diff --git a/bitcoin.pro b/bitcoin.pro index 88b92bc6cd..dd31f3ee18 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -1,7 +1,7 @@ TEMPLATE = app TARGET = DEPENDPATH += . -INCLUDEPATH += gui/include lib/include +INCLUDEPATH += gui/include lib/include cryptopp/include # Input HEADERS += gui/include/bitcoingui.h \ @@ -18,7 +18,18 @@ HEADERS += gui/include/bitcoingui.h \ lib/include/bignum.h \ lib/include/util.h \ lib/include/uint256.h \ - lib/include/serialize.h + lib/include/serialize.h \ + cryptopp/include/cryptopp/stdcpp.h \ + cryptopp/include/cryptopp/smartptr.h \ + cryptopp/include/cryptopp/simple.h \ + cryptopp/include/cryptopp/sha.h \ + cryptopp/include/cryptopp/secblock.h \ + cryptopp/include/cryptopp/pch.h \ + cryptopp/include/cryptopp/misc.h \ + cryptopp/include/cryptopp/iterhash.h \ + cryptopp/include/cryptopp/cryptlib.h \ + cryptopp/include/cryptopp/cpu.h \ + cryptopp/include/cryptopp/config.h SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/transactiontablemodel.cpp \ gui/src/addresstablemodel.cpp \ @@ -28,7 +39,9 @@ SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/addressbookdialog.cpp \ gui/src/aboutdialog.cpp \ gui/src/editaddressdialog.cpp \ - gui/src/bitcoinaddressvalidator.cpp + gui/src/bitcoinaddressvalidator.cpp \ + cryptopp/src/sha.cpp \ + cryptopp/src/cpu.cpp RESOURCES += \ gui/bitcoin.qrc diff --git a/cryptopp/include/cryptopp/config.h b/cryptopp/include/cryptopp/config.h new file mode 100644 index 0000000000..0737027f41 --- /dev/null +++ b/cryptopp/include/cryptopp/config.h @@ -0,0 +1,462 @@ +#ifndef CRYPTOPP_CONFIG_H +#define CRYPTOPP_CONFIG_H + +//// Bitcoin: disable SSE2 on 32-bit +#if !defined(_M_X64) && !defined(__x86_64__) +#define CRYPTOPP_DISABLE_SSE2 1 +#endif +//////////// end of Bitcoin changes + + +// ***************** Important Settings ******************** + +// define this if running on a big-endian CPU +#if !defined(IS_LITTLE_ENDIAN) && (defined(__BIG_ENDIAN__) || defined(__sparc) || defined(__sparc__) || defined(__hppa__) || defined(__mips__) || (defined(__MWERKS__) && !defined(__INTEL__))) +# define IS_BIG_ENDIAN +#endif + +// define this if running on a little-endian CPU +// big endian will be assumed if IS_LITTLE_ENDIAN is not defined +#ifndef IS_BIG_ENDIAN +# define IS_LITTLE_ENDIAN +#endif + +// define this if you want to disable all OS-dependent features, +// such as sockets and OS-provided random number generators +// #define NO_OS_DEPENDENCE + +// Define this to use features provided by Microsoft's CryptoAPI. +// Currently the only feature used is random number generation. +// This macro will be ignored if NO_OS_DEPENDENCE is defined. +#define USE_MS_CRYPTOAPI + +// Define this to 1 to enforce the requirement in FIPS 186-2 Change Notice 1 that only 1024 bit moduli be used +#ifndef DSA_1024_BIT_MODULUS_ONLY +# define DSA_1024_BIT_MODULUS_ONLY 1 +#endif + +// ***************** Less Important Settings *************** + +// define this to retain (as much as possible) old deprecated function and class names +// #define CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + +#define GZIP_OS_CODE 0 + +// Try this if your CPU has 256K internal cache or a slow multiply instruction +// and you want a (possibly) faster IDEA implementation using log tables +// #define IDEA_LARGECACHE + +// Define this if, for the linear congruential RNG, you want to use +// the original constants as specified in S.K. Park and K.W. Miller's +// CACM paper. +// #define LCRNG_ORIGINAL_NUMBERS + +// choose which style of sockets to wrap (mostly useful for cygwin which has both) +#define PREFER_BERKELEY_STYLE_SOCKETS +// #define PREFER_WINDOWS_STYLE_SOCKETS + +// set the name of Rijndael cipher, was "Rijndael" before version 5.3 +#define CRYPTOPP_RIJNDAEL_NAME "AES" + +// ***************** Important Settings Again ******************** +// But the defaults should be ok. + +// namespace support is now required +#ifdef NO_NAMESPACE +# error namespace support is now required +#endif + +// Define this to workaround a Microsoft CryptoAPI bug where +// each call to CryptAcquireContext causes a 100 KB memory leak. +// Defining this will cause Crypto++ to make only one call to CryptAcquireContext. +#define WORKAROUND_MS_BUG_Q258000 + +#ifdef CRYPTOPP_DOXYGEN_PROCESSING +// Avoid putting "CryptoPP::" in front of everything in Doxygen output +# define CryptoPP +# define NAMESPACE_BEGIN(x) +# define NAMESPACE_END +// Get Doxygen to generate better documentation for these typedefs +# define DOCUMENTED_TYPEDEF(x, y) class y : public x {}; +#else +# define NAMESPACE_BEGIN(x) namespace x { +# define NAMESPACE_END } +# define DOCUMENTED_TYPEDEF(x, y) typedef x y; +#endif +#define ANONYMOUS_NAMESPACE_BEGIN namespace { +#define USING_NAMESPACE(x) using namespace x; +#define DOCUMENTED_NAMESPACE_BEGIN(x) namespace x { +#define DOCUMENTED_NAMESPACE_END } + +// What is the type of the third parameter to bind? +// For Unix, the new standard is ::socklen_t (typically unsigned int), and the old standard is int. +// Unfortunately there is no way to tell whether or not socklen_t is defined. +// To work around this, TYPE_OF_SOCKLEN_T is a macro so that you can change it from the makefile. +#ifndef TYPE_OF_SOCKLEN_T +# if defined(_WIN32) || defined(__CYGWIN__) +# define TYPE_OF_SOCKLEN_T int +# else +# define TYPE_OF_SOCKLEN_T ::socklen_t +# endif +#endif + +#if defined(__CYGWIN__) && defined(PREFER_WINDOWS_STYLE_SOCKETS) +# define __USE_W32_SOCKETS +#endif + +typedef unsigned char byte; // put in global namespace to avoid ambiguity with other byte typedefs + +NAMESPACE_BEGIN(CryptoPP) + +typedef unsigned short word16; +typedef unsigned int word32; + +#if defined(_MSC_VER) || defined(__BORLANDC__) + typedef unsigned __int64 word64; + #define W64LIT(x) x##ui64 +#else + typedef unsigned long long word64; + #define W64LIT(x) x##ULL +#endif + +// define large word type, used for file offsets and such +typedef word64 lword; +const lword LWORD_MAX = W64LIT(0xffffffffffffffff); + +#ifdef __GNUC__ + #define CRYPTOPP_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif + +// define hword, word, and dword. these are used for multiprecision integer arithmetic +// Intel compiler won't have _umul128 until version 10.0. See http://softwarecommunity.intel.com/isn/Community/en-US/forums/thread/30231625.aspx +#if (defined(_MSC_VER) && (!defined(__INTEL_COMPILER) || __INTEL_COMPILER >= 1000) && (defined(_M_X64) || defined(_M_IA64))) || (defined(__DECCXX) && defined(__alpha__)) || (defined(__INTEL_COMPILER) && defined(__x86_64__)) || (defined(__SUNPRO_CC) && defined(__x86_64__)) + typedef word32 hword; + typedef word64 word; +#else + #define CRYPTOPP_NATIVE_DWORD_AVAILABLE + #if defined(__alpha__) || defined(__ia64__) || defined(_ARCH_PPC64) || defined(__x86_64__) || defined(__mips64) || defined(__sparc64__) + #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !(CRYPTOPP_GCC_VERSION == 40001 && defined(__APPLE__)) && CRYPTOPP_GCC_VERSION >= 30400 + // GCC 4.0.1 on MacOS X is missing __umodti3 and __udivti3 + // mode(TI) division broken on amd64 with GCC earlier than GCC 3.4 + typedef word32 hword; + typedef word64 word; + typedef __uint128_t dword; + typedef __uint128_t word128; + #define CRYPTOPP_WORD128_AVAILABLE + #else + // if we're here, it means we're on a 64-bit CPU but we don't have a way to obtain 128-bit multiplication results + typedef word16 hword; + typedef word32 word; + typedef word64 dword; + #endif + #else + // being here means the native register size is probably 32 bits or less + #define CRYPTOPP_BOOL_SLOW_WORD64 1 + typedef word16 hword; + typedef word32 word; + typedef word64 dword; + #endif +#endif +#ifndef CRYPTOPP_BOOL_SLOW_WORD64 + #define CRYPTOPP_BOOL_SLOW_WORD64 0 +#endif + +const unsigned int WORD_SIZE = sizeof(word); +const unsigned int WORD_BITS = WORD_SIZE * 8; + +NAMESPACE_END + +#ifndef CRYPTOPP_L1_CACHE_LINE_SIZE + // This should be a lower bound on the L1 cache line size. It's used for defense against timing attacks. + #if defined(_M_X64) || defined(__x86_64__) + #define CRYPTOPP_L1_CACHE_LINE_SIZE 64 + #else + // L1 cache line size is 32 on Pentium III and earlier + #define CRYPTOPP_L1_CACHE_LINE_SIZE 32 + #endif +#endif + +#if defined(_MSC_VER) + #if _MSC_VER == 1200 + #include + #endif + #if _MSC_VER > 1200 || defined(_mm_free) + #define CRYPTOPP_MSVC6PP_OR_LATER // VC 6 processor pack or later + #else + #define CRYPTOPP_MSVC6_NO_PP // VC 6 without processor pack + #endif +#endif + +#ifndef CRYPTOPP_ALIGN_DATA + #if defined(CRYPTOPP_MSVC6PP_OR_LATER) + #define CRYPTOPP_ALIGN_DATA(x) __declspec(align(x)) + #elif defined(__GNUC__) + #define CRYPTOPP_ALIGN_DATA(x) __attribute__((aligned(x))) + #else + #define CRYPTOPP_ALIGN_DATA(x) + #endif +#endif + +#ifndef CRYPTOPP_SECTION_ALIGN16 + #if defined(__GNUC__) && !defined(__APPLE__) + // the alignment attribute doesn't seem to work without this section attribute when -fdata-sections is turned on + #define CRYPTOPP_SECTION_ALIGN16 __attribute__((section ("CryptoPP_Align16"))) + #else + #define CRYPTOPP_SECTION_ALIGN16 + #endif +#endif + +#if defined(_MSC_VER) || defined(__fastcall) + #define CRYPTOPP_FASTCALL __fastcall +#else + #define CRYPTOPP_FASTCALL +#endif + +// VC60 workaround: it doesn't allow typename in some places +#if defined(_MSC_VER) && (_MSC_VER < 1300) +#define CPP_TYPENAME +#else +#define CPP_TYPENAME typename +#endif + +// VC60 workaround: can't cast unsigned __int64 to float or double +#if defined(_MSC_VER) && !defined(CRYPTOPP_MSVC6PP_OR_LATER) +#define CRYPTOPP_VC6_INT64 (__int64) +#else +#define CRYPTOPP_VC6_INT64 +#endif + +#ifdef _MSC_VER +#define CRYPTOPP_NO_VTABLE __declspec(novtable) +#else +#define CRYPTOPP_NO_VTABLE +#endif + +#ifdef _MSC_VER + // 4231: nonstandard extension used : 'extern' before template explicit instantiation + // 4250: dominance + // 4251: member needs to have dll-interface + // 4275: base needs to have dll-interface + // 4660: explicitly instantiating a class that's already implicitly instantiated + // 4661: no suitable definition provided for explicit template instantiation request + // 4786: identifer was truncated in debug information + // 4355: 'this' : used in base member initializer list + // 4910: '__declspec(dllexport)' and 'extern' are incompatible on an explicit instantiation +# pragma warning(disable: 4231 4250 4251 4275 4660 4661 4786 4355 4910) +#endif + +#ifdef __BORLANDC__ +// 8037: non-const function called for const object. needed to work around BCB2006 bug +# pragma warn -8037 +#endif + +#if (defined(_MSC_VER) && _MSC_VER <= 1300) || defined(__MWERKS__) || defined(_STLPORT_VERSION) +#define CRYPTOPP_DISABLE_UNCAUGHT_EXCEPTION +#endif + +#ifndef CRYPTOPP_DISABLE_UNCAUGHT_EXCEPTION +#define CRYPTOPP_UNCAUGHT_EXCEPTION_AVAILABLE +#endif + +#ifdef CRYPTOPP_DISABLE_X86ASM // for backwards compatibility: this macro had both meanings +#define CRYPTOPP_DISABLE_ASM +#define CRYPTOPP_DISABLE_SSE2 +#endif + +#if !defined(CRYPTOPP_DISABLE_ASM) && ((defined(_MSC_VER) && defined(_M_IX86)) || (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))) + #define CRYPTOPP_X86_ASM_AVAILABLE + + #if !defined(CRYPTOPP_DISABLE_SSE2) && (defined(CRYPTOPP_MSVC6PP_OR_LATER) || CRYPTOPP_GCC_VERSION >= 30300) + #define CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE 1 + #else + #define CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE 0 + #endif + + // SSSE3 was actually introduced in GNU as 2.17, which was released 6/23/2006, but we can't tell what version of binutils is installed. + // GCC 4.1.2 was released on 2/13/2007, so we'll use that as a proxy for the binutils version. + #if !defined(CRYPTOPP_DISABLE_SSSE3) && (_MSC_VER >= 1400 || CRYPTOPP_GCC_VERSION >= 40102) + #define CRYPTOPP_BOOL_SSSE3_ASM_AVAILABLE 1 + #else + #define CRYPTOPP_BOOL_SSSE3_ASM_AVAILABLE 0 + #endif +#endif + +#if !defined(CRYPTOPP_DISABLE_ASM) && defined(_MSC_VER) && defined(_M_X64) + #define CRYPTOPP_X64_MASM_AVAILABLE +#endif + +#if !defined(CRYPTOPP_DISABLE_ASM) && defined(__GNUC__) && defined(__x86_64__) + #define CRYPTOPP_X64_ASM_AVAILABLE +#endif + +#if !defined(CRYPTOPP_DISABLE_SSE2) && (defined(CRYPTOPP_MSVC6PP_OR_LATER) || defined(__SSE2__)) + #define CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE 1 +#else + #define CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE 0 +#endif + +#if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE || CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE) + #define CRYPTOPP_BOOL_ALIGN16_ENABLED 1 +#else + #define CRYPTOPP_BOOL_ALIGN16_ENABLED 0 +#endif + +// how to allocate 16-byte aligned memory (for SSE2) +#if defined(CRYPTOPP_MSVC6PP_OR_LATER) + #define CRYPTOPP_MM_MALLOC_AVAILABLE +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define CRYPTOPP_MALLOC_ALIGNMENT_IS_16 +#elif defined(__linux__) || defined(__sun__) || defined(__CYGWIN__) + #define CRYPTOPP_MEMALIGN_AVAILABLE +#else + #define CRYPTOPP_NO_ALIGNED_ALLOC +#endif + +// how to disable inlining +#if defined(_MSC_VER) && _MSC_VER >= 1300 +# define CRYPTOPP_NOINLINE_DOTDOTDOT +# define CRYPTOPP_NOINLINE __declspec(noinline) +#elif defined(__GNUC__) +# define CRYPTOPP_NOINLINE_DOTDOTDOT +# define CRYPTOPP_NOINLINE __attribute__((noinline)) +#else +# define CRYPTOPP_NOINLINE_DOTDOTDOT ... +# define CRYPTOPP_NOINLINE +#endif + +// how to declare class constants +#if (defined(_MSC_VER) && _MSC_VER <= 1300) || defined(__INTEL_COMPILER) +# define CRYPTOPP_CONSTANT(x) enum {x}; +#else +# define CRYPTOPP_CONSTANT(x) static const int x; +#endif + +#if defined(_M_X64) || defined(__x86_64__) + #define CRYPTOPP_BOOL_X64 1 +#else + #define CRYPTOPP_BOOL_X64 0 +#endif + +// see http://predef.sourceforge.net/prearch.html +#if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(_X86_) || defined(__I86__) || defined(__INTEL__) + #define CRYPTOPP_BOOL_X86 1 +#else + #define CRYPTOPP_BOOL_X86 0 +#endif + +#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X86 || defined(__powerpc__) + #define CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS +#endif + +#define CRYPTOPP_VERSION 560 + +// ***************** determine availability of OS features ******************** + +#ifndef NO_OS_DEPENDENCE + +#if defined(_WIN32) || defined(__CYGWIN__) +#define CRYPTOPP_WIN32_AVAILABLE +#endif + +#if defined(__unix__) || defined(__MACH__) || defined(__NetBSD__) || defined(__sun) +#define CRYPTOPP_UNIX_AVAILABLE +#endif + +#if defined(CRYPTOPP_WIN32_AVAILABLE) || defined(CRYPTOPP_UNIX_AVAILABLE) +# define HIGHRES_TIMER_AVAILABLE +#endif + +#ifdef CRYPTOPP_UNIX_AVAILABLE +# define HAS_BERKELEY_STYLE_SOCKETS +#endif + +#ifdef CRYPTOPP_WIN32_AVAILABLE +# define HAS_WINDOWS_STYLE_SOCKETS +#endif + +#if defined(HIGHRES_TIMER_AVAILABLE) && (defined(HAS_BERKELEY_STYLE_SOCKETS) || defined(HAS_WINDOWS_STYLE_SOCKETS)) +# define SOCKETS_AVAILABLE +#endif + +#if defined(HAS_WINDOWS_STYLE_SOCKETS) && (!defined(HAS_BERKELEY_STYLE_SOCKETS) || defined(PREFER_WINDOWS_STYLE_SOCKETS)) +# define USE_WINDOWS_STYLE_SOCKETS +#else +# define USE_BERKELEY_STYLE_SOCKETS +#endif + +#if defined(HIGHRES_TIMER_AVAILABLE) && defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(USE_BERKELEY_STYLE_SOCKETS) +# define WINDOWS_PIPES_AVAILABLE +#endif + +#if defined(CRYPTOPP_WIN32_AVAILABLE) && defined(USE_MS_CRYPTOAPI) +# define NONBLOCKING_RNG_AVAILABLE +# define OS_RNG_AVAILABLE +#endif + +#if defined(CRYPTOPP_UNIX_AVAILABLE) || defined(CRYPTOPP_DOXYGEN_PROCESSING) +# define NONBLOCKING_RNG_AVAILABLE +# define BLOCKING_RNG_AVAILABLE +# define OS_RNG_AVAILABLE +# define HAS_PTHREADS +# define THREADS_AVAILABLE +#endif + +#ifdef CRYPTOPP_WIN32_AVAILABLE +# define HAS_WINTHREADS +# define THREADS_AVAILABLE +#endif + +#endif // NO_OS_DEPENDENCE + +// ***************** DLL related ******************** + +#ifdef CRYPTOPP_WIN32_AVAILABLE + +#ifdef CRYPTOPP_EXPORTS +#define CRYPTOPP_IS_DLL +#define CRYPTOPP_DLL __declspec(dllexport) +#elif defined(CRYPTOPP_IMPORTS) +#define CRYPTOPP_IS_DLL +#define CRYPTOPP_DLL __declspec(dllimport) +#else +#define CRYPTOPP_DLL +#endif + +#define CRYPTOPP_API __cdecl + +#else // CRYPTOPP_WIN32_AVAILABLE + +#define CRYPTOPP_DLL +#define CRYPTOPP_API + +#endif // CRYPTOPP_WIN32_AVAILABLE + +#if defined(__MWERKS__) +#define CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS extern class CRYPTOPP_DLL +#elif defined(__BORLANDC__) || defined(__SUNPRO_CC) +#define CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS template class CRYPTOPP_DLL +#else +#define CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS extern template class CRYPTOPP_DLL +#endif + +#if defined(CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES) && !defined(CRYPTOPP_IMPORTS) +#define CRYPTOPP_DLL_TEMPLATE_CLASS template class CRYPTOPP_DLL +#else +#define CRYPTOPP_DLL_TEMPLATE_CLASS CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS +#endif + +#if defined(__MWERKS__) +#define CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS extern class +#elif defined(__BORLANDC__) || defined(__SUNPRO_CC) +#define CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS template class +#else +#define CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS extern template class +#endif + +#if defined(CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES) && !defined(CRYPTOPP_EXPORTS) +#define CRYPTOPP_STATIC_TEMPLATE_CLASS template class +#else +#define CRYPTOPP_STATIC_TEMPLATE_CLASS CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS +#endif + +#endif diff --git a/cryptopp/include/cryptopp/cpu.h b/cryptopp/include/cryptopp/cpu.h new file mode 100644 index 0000000000..113b8a1f3b --- /dev/null +++ b/cryptopp/include/cryptopp/cpu.h @@ -0,0 +1,263 @@ +#ifndef CRYPTOPP_CPU_H +#define CRYPTOPP_CPU_H + +#ifdef CRYPTOPP_GENERATE_X64_MASM + +#define CRYPTOPP_X86_ASM_AVAILABLE +#define CRYPTOPP_BOOL_X64 1 +#define CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE 1 +#define NAMESPACE_END + +#else + +#include "cryptopp/config.h" + +#ifdef CRYPTOPP_MSVC6PP_OR_LATER + #include +#endif + +NAMESPACE_BEGIN(CryptoPP) + +#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || (_MSC_VER >= 1400 && CRYPTOPP_BOOL_X64) + +#define CRYPTOPP_CPUID_AVAILABLE + +// these should not be used directly +extern CRYPTOPP_DLL bool g_x86DetectionDone; +extern CRYPTOPP_DLL bool g_hasSSE2; +extern CRYPTOPP_DLL bool g_hasISSE; +extern CRYPTOPP_DLL bool g_hasMMX; +extern CRYPTOPP_DLL bool g_hasSSSE3; +extern CRYPTOPP_DLL bool g_isP4; +extern CRYPTOPP_DLL word32 g_cacheLineSize; +CRYPTOPP_DLL void CRYPTOPP_API DetectX86Features(); + +CRYPTOPP_DLL bool CRYPTOPP_API CpuId(word32 input, word32 *output); + +#if CRYPTOPP_BOOL_X64 +inline bool HasSSE2() {return true;} +inline bool HasISSE() {return true;} +inline bool HasMMX() {return true;} +#else + +inline bool HasSSE2() +{ + if (!g_x86DetectionDone) + DetectX86Features(); + return g_hasSSE2; +} + +inline bool HasISSE() +{ + if (!g_x86DetectionDone) + DetectX86Features(); + return g_hasISSE; +} + +inline bool HasMMX() +{ + if (!g_x86DetectionDone) + DetectX86Features(); + return g_hasMMX; +} + +#endif + +inline bool HasSSSE3() +{ + if (!g_x86DetectionDone) + DetectX86Features(); + return g_hasSSSE3; +} + +inline bool IsP4() +{ + if (!g_x86DetectionDone) + DetectX86Features(); + return g_isP4; +} + +inline int GetCacheLineSize() +{ + if (!g_x86DetectionDone) + DetectX86Features(); + return g_cacheLineSize; +} + +#else + +inline int GetCacheLineSize() +{ + return CRYPTOPP_L1_CACHE_LINE_SIZE; +} + +inline bool HasSSSE3() {return false;} +inline bool IsP4() {return false;} + +// assume MMX and SSE2 if intrinsics are enabled +#if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE || CRYPTOPP_BOOL_X64 +inline bool HasSSE2() {return true;} +inline bool HasISSE() {return true;} +inline bool HasMMX() {return true;} +#else +inline bool HasSSE2() {return false;} +inline bool HasISSE() {return false;} +inline bool HasMMX() {return false;} +#endif + +#endif // #ifdef CRYPTOPP_X86_ASM_AVAILABLE || _MSC_VER >= 1400 + +#endif + +#ifdef CRYPTOPP_GENERATE_X64_MASM + #define AS1(x) x*newline* + #define AS2(x, y) x, y*newline* + #define AS3(x, y, z) x, y, z*newline* + #define ASS(x, y, a, b, c, d) x, y, a*64+b*16+c*4+d*newline* + #define ASL(x) label##x:*newline* + #define ASJ(x, y, z) x label##y*newline* + #define ASC(x, y) x label##y*newline* + #define AS_HEX(y) 0##y##h +#elif defined(__GNUC__) + // define these in two steps to allow arguments to be expanded + #define GNU_AS1(x) #x ";" + #define GNU_AS2(x, y) #x ", " #y ";" + #define GNU_AS3(x, y, z) #x ", " #y ", " #z ";" + #define GNU_ASL(x) "\n" #x ":" + #define GNU_ASJ(x, y, z) #x " " #y #z ";" + #define AS1(x) GNU_AS1(x) + #define AS2(x, y) GNU_AS2(x, y) + #define AS3(x, y, z) GNU_AS3(x, y, z) + #define ASS(x, y, a, b, c, d) #x ", " #y ", " #a "*64+" #b "*16+" #c "*4+" #d ";" + #define ASL(x) GNU_ASL(x) + #define ASJ(x, y, z) GNU_ASJ(x, y, z) + #define ASC(x, y) #x " " #y ";" + #define CRYPTOPP_NAKED + #define AS_HEX(y) 0x##y +#else + #define AS1(x) __asm {x} + #define AS2(x, y) __asm {x, y} + #define AS3(x, y, z) __asm {x, y, z} + #define ASS(x, y, a, b, c, d) __asm {x, y, _MM_SHUFFLE(a, b, c, d)} + #define ASL(x) __asm {label##x:} + #define ASJ(x, y, z) __asm {x label##y} + #define ASC(x, y) __asm {x label##y} + #define CRYPTOPP_NAKED __declspec(naked) + #define AS_HEX(y) 0x##y +#endif + +#define IF0(y) +#define IF1(y) y + +#ifdef CRYPTOPP_GENERATE_X64_MASM +#define ASM_MOD(x, y) ((x) MOD (y)) +#define XMMWORD_PTR XMMWORD PTR +#else +// GNU assembler doesn't seem to have mod operator +#define ASM_MOD(x, y) ((x)-((x)/(y))*(y)) +// GAS 2.15 doesn't support XMMWORD PTR. it seems necessary only for MASM +#define XMMWORD_PTR +#endif + +#if CRYPTOPP_BOOL_X86 + #define AS_REG_1 ecx + #define AS_REG_2 edx + #define AS_REG_3 esi + #define AS_REG_4 edi + #define AS_REG_5 eax + #define AS_REG_6 ebx + #define AS_REG_7 ebp + #define AS_REG_1d ecx + #define AS_REG_2d edx + #define AS_REG_3d esi + #define AS_REG_4d edi + #define AS_REG_5d eax + #define AS_REG_6d ebx + #define AS_REG_7d ebp + #define WORD_SZ 4 + #define WORD_REG(x) e##x + #define WORD_PTR DWORD PTR + #define AS_PUSH_IF86(x) AS1(push e##x) + #define AS_POP_IF86(x) AS1(pop e##x) + #define AS_JCXZ jecxz +#elif CRYPTOPP_BOOL_X64 + #ifdef CRYPTOPP_GENERATE_X64_MASM + #define AS_REG_1 rcx + #define AS_REG_2 rdx + #define AS_REG_3 r8 + #define AS_REG_4 r9 + #define AS_REG_5 rax + #define AS_REG_6 r10 + #define AS_REG_7 r11 + #define AS_REG_1d ecx + #define AS_REG_2d edx + #define AS_REG_3d r8d + #define AS_REG_4d r9d + #define AS_REG_5d eax + #define AS_REG_6d r10d + #define AS_REG_7d r11d + #else + #define AS_REG_1 rdi + #define AS_REG_2 rsi + #define AS_REG_3 rdx + #define AS_REG_4 rcx + #define AS_REG_5 r8 + #define AS_REG_6 r9 + #define AS_REG_7 r10 + #define AS_REG_1d edi + #define AS_REG_2d esi + #define AS_REG_3d edx + #define AS_REG_4d ecx + #define AS_REG_5d r8d + #define AS_REG_6d r9d + #define AS_REG_7d r10d + #endif + #define WORD_SZ 8 + #define WORD_REG(x) r##x + #define WORD_PTR QWORD PTR + #define AS_PUSH_IF86(x) + #define AS_POP_IF86(x) + #define AS_JCXZ jrcxz +#endif + +// helper macro for stream cipher output +#define AS_XMM_OUTPUT4(labelPrefix, inputPtr, outputPtr, x0, x1, x2, x3, t, p0, p1, p2, p3, increment)\ + AS2( test inputPtr, inputPtr)\ + ASC( jz, labelPrefix##3)\ + AS2( test inputPtr, 15)\ + ASC( jnz, labelPrefix##7)\ + AS2( pxor xmm##x0, [inputPtr+p0*16])\ + AS2( pxor xmm##x1, [inputPtr+p1*16])\ + AS2( pxor xmm##x2, [inputPtr+p2*16])\ + AS2( pxor xmm##x3, [inputPtr+p3*16])\ + AS2( add inputPtr, increment*16)\ + ASC( jmp, labelPrefix##3)\ + ASL(labelPrefix##7)\ + AS2( movdqu xmm##t, [inputPtr+p0*16])\ + AS2( pxor xmm##x0, xmm##t)\ + AS2( movdqu xmm##t, [inputPtr+p1*16])\ + AS2( pxor xmm##x1, xmm##t)\ + AS2( movdqu xmm##t, [inputPtr+p2*16])\ + AS2( pxor xmm##x2, xmm##t)\ + AS2( movdqu xmm##t, [inputPtr+p3*16])\ + AS2( pxor xmm##x3, xmm##t)\ + AS2( add inputPtr, increment*16)\ + ASL(labelPrefix##3)\ + AS2( test outputPtr, 15)\ + ASC( jnz, labelPrefix##8)\ + AS2( movdqa [outputPtr+p0*16], xmm##x0)\ + AS2( movdqa [outputPtr+p1*16], xmm##x1)\ + AS2( movdqa [outputPtr+p2*16], xmm##x2)\ + AS2( movdqa [outputPtr+p3*16], xmm##x3)\ + ASC( jmp, labelPrefix##9)\ + ASL(labelPrefix##8)\ + AS2( movdqu [outputPtr+p0*16], xmm##x0)\ + AS2( movdqu [outputPtr+p1*16], xmm##x1)\ + AS2( movdqu [outputPtr+p2*16], xmm##x2)\ + AS2( movdqu [outputPtr+p3*16], xmm##x3)\ + ASL(labelPrefix##9)\ + AS2( add outputPtr, increment*16) + +NAMESPACE_END + +#endif diff --git a/cryptopp/include/cryptopp/cryptlib.h b/cryptopp/include/cryptopp/cryptlib.h new file mode 100644 index 0000000000..1461af7b7b --- /dev/null +++ b/cryptopp/include/cryptopp/cryptlib.h @@ -0,0 +1,1668 @@ +// cryptlib.h - written and placed in the public domain by Wei Dai +/*! \file + This file contains the declarations for the abstract base + classes that provide a uniform interface to this library. +*/ + +/*! \mainpage Crypto++ Library 5.6.0 API Reference +
+
Abstract Base Classes
+ cryptlib.h +
Authenticated Encryption
+ AuthenticatedSymmetricCipherDocumentation +
Symmetric Ciphers
+ SymmetricCipherDocumentation +
Hash Functions
+ SHA1, SHA224, SHA256, SHA384, SHA512, Tiger, Whirlpool, RIPEMD160, RIPEMD320, RIPEMD128, RIPEMD256, Weak1::MD2, Weak1::MD4, Weak1::MD5 +
Non-Cryptographic Checksums
+ CRC32, Adler32 +
Message Authentication Codes
+ VMAC, HMAC, CBC_MAC, CMAC, DMAC, TTMAC, GCM (GMAC) +
Random Number Generators
+ NullRNG(), LC_RNG, RandomPool, BlockingRng, NonblockingRng, AutoSeededRandomPool, AutoSeededX917RNG, DefaultAutoSeededRNG +
Password-based Cryptography
+ PasswordBasedKeyDerivationFunction +
Public Key Cryptosystems
+ DLIES, ECIES, LUCES, RSAES, RabinES, LUC_IES +
Public Key Signature Schemes
+ DSA, GDSA, ECDSA, NR, ECNR, LUCSS, RSASS, RSASS_ISO, RabinSS, RWSS, ESIGN +
Key Agreement
+ #DH, DH2, #MQV, ECDH, ECMQV, XTR_DH +
Algebraic Structures
+ Integer, PolynomialMod2, PolynomialOver, RingOfPolynomialsOver, + ModularArithmetic, MontgomeryRepresentation, GFP2_ONB, + GF2NP, GF256, GF2_32, EC2N, ECP +
Secret Sharing and Information Dispersal
+ SecretSharing, SecretRecovery, InformationDispersal, InformationRecovery +
Compression
+ Deflator, Inflator, Gzip, Gunzip, ZlibCompressor, ZlibDecompressor +
Input Source Classes
+ StringSource, ArraySource, FileSource, SocketSource, WindowsPipeSource, RandomNumberSource +
Output Sink Classes
+ StringSinkTemplate, ArraySink, FileSink, SocketSink, WindowsPipeSink, RandomNumberSink +
Filter Wrappers
+ StreamTransformationFilter, HashFilter, HashVerificationFilter, SignerFilter, SignatureVerificationFilter +
Binary to Text Encoders and Decoders
+ HexEncoder, HexDecoder, Base64Encoder, Base64Decoder, Base32Encoder, Base32Decoder +
Wrappers for OS features
+ Timer, Socket, WindowsHandle, ThreadLocalStorage, ThreadUserTimer +
FIPS 140 related
+ fips140.h +
+ +In the FIPS 140-2 validated DLL version of Crypto++, only the following implementation class are available. +
+
Block Ciphers
+ AES, DES_EDE2, DES_EDE3, SKIPJACK +
Cipher Modes (replace template parameter BC with one of the block ciphers above)
+ ECB_Mode\, CTR_Mode\, CBC_Mode\, CFB_FIPS_Mode\, OFB_Mode\ +
Hash Functions
+ SHA1, SHA224, SHA256, SHA384, SHA512 +
Public Key Signature Schemes (replace template parameter H with one of the hash functions above)
+ RSASS\, RSASS\, RSASS_ISO\, RWSS\, DSA, ECDSA\, ECDSA\ +
Message Authentication Codes (replace template parameter H with one of the hash functions above)
+ HMAC\, CBC_MAC\, CBC_MAC\ +
Random Number Generators
+ DefaultAutoSeededRNG (AutoSeededX917RNG\) +
Key Agreement
+ #DH +
Public Key Cryptosystems
+ RSAES\ \> +
+ +

This reference manual is a work in progress. Some classes are still lacking detailed descriptions. +

Click here to download a zip archive containing this manual. +

Thanks to Ryan Phillips for providing the Doxygen configuration file +and getting me started with this manual. +*/ + +#ifndef CRYPTOPP_CRYPTLIB_H +#define CRYPTOPP_CRYPTLIB_H + +#include "cryptopp/config.h" +#include "cryptopp/stdcpp.h" + +NAMESPACE_BEGIN(CryptoPP) + +// forward declarations +class Integer; +class RandomNumberGenerator; +class BufferedTransformation; + +//! used to specify a direction for a cipher to operate in (encrypt or decrypt) +enum CipherDir {ENCRYPTION, DECRYPTION}; + +//! used to represent infinite time +const unsigned long INFINITE_TIME = ULONG_MAX; + +// VC60 workaround: using enums as template parameters causes problems +template +struct EnumToType +{ + static ENUM_TYPE ToEnum() {return (ENUM_TYPE)VALUE;} +}; + +enum ByteOrder {LITTLE_ENDIAN_ORDER = 0, BIG_ENDIAN_ORDER = 1}; +typedef EnumToType LittleEndian; +typedef EnumToType BigEndian; + +//! base class for all exceptions thrown by Crypto++ +class CRYPTOPP_DLL Exception : public std::exception +{ +public: + //! error types + enum ErrorType { + //! a method is not implemented + NOT_IMPLEMENTED, + //! invalid function argument + INVALID_ARGUMENT, + //! BufferedTransformation received a Flush(true) signal but can't flush buffers + CANNOT_FLUSH, + //! data integerity check (such as CRC or MAC) failed + DATA_INTEGRITY_CHECK_FAILED, + //! received input data that doesn't conform to expected format + INVALID_DATA_FORMAT, + //! error reading from input device or writing to output device + IO_ERROR, + //! some error not belong to any of the above categories + OTHER_ERROR + }; + + explicit Exception(ErrorType errorType, const std::string &s) : m_errorType(errorType), m_what(s) {} + virtual ~Exception() throw() {} + const char *what() const throw() {return (m_what.c_str());} + const std::string &GetWhat() const {return m_what;} + void SetWhat(const std::string &s) {m_what = s;} + ErrorType GetErrorType() const {return m_errorType;} + void SetErrorType(ErrorType errorType) {m_errorType = errorType;} + +private: + ErrorType m_errorType; + std::string m_what; +}; + +//! exception thrown when an invalid argument is detected +class CRYPTOPP_DLL InvalidArgument : public Exception +{ +public: + explicit InvalidArgument(const std::string &s) : Exception(INVALID_ARGUMENT, s) {} +}; + +//! exception thrown when input data is received that doesn't conform to expected format +class CRYPTOPP_DLL InvalidDataFormat : public Exception +{ +public: + explicit InvalidDataFormat(const std::string &s) : Exception(INVALID_DATA_FORMAT, s) {} +}; + +//! exception thrown by decryption filters when trying to decrypt an invalid ciphertext +class CRYPTOPP_DLL InvalidCiphertext : public InvalidDataFormat +{ +public: + explicit InvalidCiphertext(const std::string &s) : InvalidDataFormat(s) {} +}; + +//! exception thrown by a class if a non-implemented method is called +class CRYPTOPP_DLL NotImplemented : public Exception +{ +public: + explicit NotImplemented(const std::string &s) : Exception(NOT_IMPLEMENTED, s) {} +}; + +//! exception thrown by a class when Flush(true) is called but it can't completely flush its buffers +class CRYPTOPP_DLL CannotFlush : public Exception +{ +public: + explicit CannotFlush(const std::string &s) : Exception(CANNOT_FLUSH, s) {} +}; + +//! error reported by the operating system +class CRYPTOPP_DLL OS_Error : public Exception +{ +public: + OS_Error(ErrorType errorType, const std::string &s, const std::string& operation, int errorCode) + : Exception(errorType, s), m_operation(operation), m_errorCode(errorCode) {} + ~OS_Error() throw() {} + + // the operating system API that reported the error + const std::string & GetOperation() const {return m_operation;} + // the error code return by the operating system + int GetErrorCode() const {return m_errorCode;} + +protected: + std::string m_operation; + int m_errorCode; +}; + +//! used to return decoding results +struct CRYPTOPP_DLL DecodingResult +{ + explicit DecodingResult() : isValidCoding(false), messageLength(0) {} + explicit DecodingResult(size_t len) : isValidCoding(true), messageLength(len) {} + + bool operator==(const DecodingResult &rhs) const {return isValidCoding == rhs.isValidCoding && messageLength == rhs.messageLength;} + bool operator!=(const DecodingResult &rhs) const {return !operator==(rhs);} + + bool isValidCoding; + size_t messageLength; + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + operator size_t() const {return isValidCoding ? messageLength : 0;} +#endif +}; + +//! interface for retrieving values given their names +/*! \note This class is used to safely pass a variable number of arbitrarily typed arguments to functions + and to read values from keys and crypto parameters. + \note To obtain an object that implements NameValuePairs for the purpose of parameter + passing, use the MakeParameters() function. + \note To get a value from NameValuePairs, you need to know the name and the type of the value. + Call GetValueNames() on a NameValuePairs object to obtain a list of value names that it supports. + Then look at the Name namespace documentation to see what the type of each value is, or + alternatively, call GetIntValue() with the value name, and if the type is not int, a + ValueTypeMismatch exception will be thrown and you can get the actual type from the exception object. +*/ +class CRYPTOPP_NO_VTABLE NameValuePairs +{ +public: + virtual ~NameValuePairs() {} + + //! exception thrown when trying to retrieve a value using a different type than expected + class CRYPTOPP_DLL ValueTypeMismatch : public InvalidArgument + { + public: + ValueTypeMismatch(const std::string &name, const std::type_info &stored, const std::type_info &retrieving) + : InvalidArgument("NameValuePairs: type mismatch for '" + name + "', stored '" + stored.name() + "', trying to retrieve '" + retrieving.name() + "'") + , m_stored(stored), m_retrieving(retrieving) {} + + const std::type_info & GetStoredTypeInfo() const {return m_stored;} + const std::type_info & GetRetrievingTypeInfo() const {return m_retrieving;} + + private: + const std::type_info &m_stored; + const std::type_info &m_retrieving; + }; + + //! get a copy of this object or a subobject of it + template + bool GetThisObject(T &object) const + { + return GetValue((std::string("ThisObject:")+typeid(T).name()).c_str(), object); + } + + //! get a pointer to this object, as a pointer to T + template + bool GetThisPointer(T *&p) const + { + return GetValue((std::string("ThisPointer:")+typeid(T).name()).c_str(), p); + } + + //! get a named value, returns true if the name exists + template + bool GetValue(const char *name, T &value) const + { + return GetVoidValue(name, typeid(T), &value); + } + + //! get a named value, returns the default if the name doesn't exist + template + T GetValueWithDefault(const char *name, T defaultValue) const + { + GetValue(name, defaultValue); + return defaultValue; + } + + //! get a list of value names that can be retrieved + CRYPTOPP_DLL std::string GetValueNames() const + {std::string result; GetValue("ValueNames", result); return result;} + + //! get a named value with type int + /*! used to ensure we don't accidentally try to get an unsigned int + or some other type when we mean int (which is the most common case) */ + CRYPTOPP_DLL bool GetIntValue(const char *name, int &value) const + {return GetValue(name, value);} + + //! get a named value with type int, with default + CRYPTOPP_DLL int GetIntValueWithDefault(const char *name, int defaultValue) const + {return GetValueWithDefault(name, defaultValue);} + + //! used by derived classes to check for type mismatch + CRYPTOPP_DLL static void CRYPTOPP_API ThrowIfTypeMismatch(const char *name, const std::type_info &stored, const std::type_info &retrieving) + {if (stored != retrieving) throw ValueTypeMismatch(name, stored, retrieving);} + + template + void GetRequiredParameter(const char *className, const char *name, T &value) const + { + if (!GetValue(name, value)) + throw InvalidArgument(std::string(className) + ": missing required parameter '" + name + "'"); + } + + CRYPTOPP_DLL void GetRequiredIntParameter(const char *className, const char *name, int &value) const + { + if (!GetIntValue(name, value)) + throw InvalidArgument(std::string(className) + ": missing required parameter '" + name + "'"); + } + + //! to be implemented by derived classes, users should use one of the above functions instead + CRYPTOPP_DLL virtual bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const =0; +}; + +//! namespace containing value name definitions +/*! value names, types and semantics: + + ThisObject:ClassName (ClassName, copy of this object or a subobject) + ThisPointer:ClassName (const ClassName *, pointer to this object or a subobject) +*/ +DOCUMENTED_NAMESPACE_BEGIN(Name) +// more names defined in argnames.h +DOCUMENTED_NAMESPACE_END + +//! empty set of name-value pairs +class CRYPTOPP_DLL NullNameValuePairs : public NameValuePairs +{ +public: + bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const {return false;} +}; + +//! _ +extern CRYPTOPP_DLL const NullNameValuePairs g_nullNameValuePairs; + +// ******************************************************** + +//! interface for cloning objects, this is not implemented by most classes yet +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Clonable +{ +public: + virtual ~Clonable() {} + //! this is not implemented by most classes yet + virtual Clonable* Clone() const {throw NotImplemented("Clone() is not implemented yet.");} // TODO: make this =0 +}; + +//! interface for all crypto algorithms + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Algorithm : public Clonable +{ +public: + /*! When FIPS 140-2 compliance is enabled and checkSelfTestStatus == true, + this constructor throws SelfTestFailure if the self test hasn't been run or fails. */ + Algorithm(bool checkSelfTestStatus = true); + //! returns name of this algorithm, not universally implemented yet + virtual std::string AlgorithmName() const {return "unknown";} +}; + +//! keying interface for crypto algorithms that take byte strings as keys +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE SimpleKeyingInterface +{ +public: + virtual ~SimpleKeyingInterface() {} + + //! returns smallest valid key length in bytes */ + virtual size_t MinKeyLength() const =0; + //! returns largest valid key length in bytes */ + virtual size_t MaxKeyLength() const =0; + //! returns default (recommended) key length in bytes */ + virtual size_t DefaultKeyLength() const =0; + + //! returns the smallest valid key length in bytes that is >= min(n, GetMaxKeyLength()) + virtual size_t GetValidKeyLength(size_t n) const =0; + + //! returns whether n is a valid key length + virtual bool IsValidKeyLength(size_t n) const + {return n == GetValidKeyLength(n);} + + //! set or reset the key of this object + /*! \param params is used to specify Rounds, BlockSize, etc. */ + virtual void SetKey(const byte *key, size_t length, const NameValuePairs ¶ms = g_nullNameValuePairs); + + //! calls SetKey() with an NameValuePairs object that just specifies "Rounds" + void SetKeyWithRounds(const byte *key, size_t length, int rounds); + + //! calls SetKey() with an NameValuePairs object that just specifies "IV" + void SetKeyWithIV(const byte *key, size_t length, const byte *iv, size_t ivLength); + + //! calls SetKey() with an NameValuePairs object that just specifies "IV" + void SetKeyWithIV(const byte *key, size_t length, const byte *iv) + {SetKeyWithIV(key, length, iv, IVSize());} + + enum IV_Requirement {UNIQUE_IV = 0, RANDOM_IV, UNPREDICTABLE_RANDOM_IV, INTERNALLY_GENERATED_IV, NOT_RESYNCHRONIZABLE}; + //! returns the minimal requirement for secure IVs + virtual IV_Requirement IVRequirement() const =0; + + //! returns whether this object can be resynchronized (i.e. supports initialization vectors) + /*! If this function returns true, and no IV is passed to SetKey() and CanUseStructuredIVs()==true, an IV of all 0's will be assumed. */ + bool IsResynchronizable() const {return IVRequirement() < NOT_RESYNCHRONIZABLE;} + //! returns whether this object can use random IVs (in addition to ones returned by GetNextIV) + bool CanUseRandomIVs() const {return IVRequirement() <= UNPREDICTABLE_RANDOM_IV;} + //! returns whether this object can use random but possibly predictable IVs (in addition to ones returned by GetNextIV) + bool CanUsePredictableIVs() const {return IVRequirement() <= RANDOM_IV;} + //! returns whether this object can use structured IVs, for example a counter (in addition to ones returned by GetNextIV) + bool CanUseStructuredIVs() const {return IVRequirement() <= UNIQUE_IV;} + + virtual unsigned int IVSize() const {throw NotImplemented(GetAlgorithm().AlgorithmName() + ": this object doesn't support resynchronization");} + //! returns default length of IVs accepted by this object + unsigned int DefaultIVLength() const {return IVSize();} + //! returns minimal length of IVs accepted by this object + virtual unsigned int MinIVLength() const {return IVSize();} + //! returns maximal length of IVs accepted by this object + virtual unsigned int MaxIVLength() const {return IVSize();} + //! resynchronize with an IV. ivLength=-1 means use IVSize() + virtual void Resynchronize(const byte *iv, int ivLength=-1) {throw NotImplemented(GetAlgorithm().AlgorithmName() + ": this object doesn't support resynchronization");} + //! get a secure IV for the next message + /*! This method should be called after you finish encrypting one message and are ready to start the next one. + After calling it, you must call SetKey() or Resynchronize() before using this object again. + This method is not implemented on decryption objects. */ + virtual void GetNextIV(RandomNumberGenerator &rng, byte *IV); + +protected: + virtual const Algorithm & GetAlgorithm() const =0; + virtual void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) =0; + + void ThrowIfInvalidKeyLength(size_t length); + void ThrowIfResynchronizable(); // to be called when no IV is passed + void ThrowIfInvalidIV(const byte *iv); // check for NULL IV if it can't be used + size_t ThrowIfInvalidIVLength(int size); + const byte * GetIVAndThrowIfInvalid(const NameValuePairs ¶ms, size_t &size); + inline void AssertValidKeyLength(size_t length) const + {assert(IsValidKeyLength(length));} +}; + +//! interface for the data processing part of block ciphers + +/*! Classes derived from BlockTransformation are block ciphers + in ECB mode (for example the DES::Encryption class), which are stateless. + These classes should not be used directly, but only in combination with + a mode class (see CipherModeDocumentation in modes.h). +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE BlockTransformation : public Algorithm +{ +public: + //! encrypt or decrypt inBlock, xor with xorBlock, and write to outBlock + virtual void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const =0; + + //! encrypt or decrypt one block + /*! \pre size of inBlock and outBlock == BlockSize() */ + void ProcessBlock(const byte *inBlock, byte *outBlock) const + {ProcessAndXorBlock(inBlock, NULL, outBlock);} + + //! encrypt or decrypt one block in place + void ProcessBlock(byte *inoutBlock) const + {ProcessAndXorBlock(inoutBlock, NULL, inoutBlock);} + + //! block size of the cipher in bytes + virtual unsigned int BlockSize() const =0; + + //! returns how inputs and outputs should be aligned for optimal performance + virtual unsigned int OptimalDataAlignment() const; + + //! returns true if this is a permutation (i.e. there is an inverse transformation) + virtual bool IsPermutation() const {return true;} + + //! returns true if this is an encryption object + virtual bool IsForwardTransformation() const =0; + + //! return number of blocks that can be processed in parallel, for bit-slicing implementations + virtual unsigned int OptimalNumberOfParallelBlocks() const {return 1;} + + enum {BT_InBlockIsCounter=1, BT_DontIncrementInOutPointers=2, BT_XorInput=4, BT_ReverseDirection=8} FlagsForAdvancedProcessBlocks; + + //! encrypt and xor blocks according to flags (see FlagsForAdvancedProcessBlocks) + /*! /note If BT_InBlockIsCounter is set, last byte of inBlocks may be modified. */ + virtual size_t AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags) const; + + inline CipherDir GetCipherDirection() const {return IsForwardTransformation() ? ENCRYPTION : DECRYPTION;} +}; + +//! interface for the data processing part of stream ciphers + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE StreamTransformation : public Algorithm +{ +public: + //! return a reference to this object, + /*! This function is useful for passing a temporary StreamTransformation object to a + function that takes a non-const reference. */ + StreamTransformation& Ref() {return *this;} + + //! returns block size, if input must be processed in blocks, otherwise 1 + virtual unsigned int MandatoryBlockSize() const {return 1;} + + //! returns the input block size that is most efficient for this cipher + /*! \note optimal input length is n * OptimalBlockSize() - GetOptimalBlockSizeUsed() for any n > 0 */ + virtual unsigned int OptimalBlockSize() const {return MandatoryBlockSize();} + //! returns how much of the current block is used up + virtual unsigned int GetOptimalBlockSizeUsed() const {return 0;} + + //! returns how input should be aligned for optimal performance + virtual unsigned int OptimalDataAlignment() const; + + //! encrypt or decrypt an array of bytes of specified length + /*! \note either inString == outString, or they don't overlap */ + virtual void ProcessData(byte *outString, const byte *inString, size_t length) =0; + + //! for ciphers where the last block of data is special, encrypt or decrypt the last block of data + /*! For now the only use of this function is for CBC-CTS mode. */ + virtual void ProcessLastBlock(byte *outString, const byte *inString, size_t length); + //! returns the minimum size of the last block, 0 indicating the last block is not special + virtual unsigned int MinLastBlockSize() const {return 0;} + + //! same as ProcessData(inoutString, inoutString, length) + inline void ProcessString(byte *inoutString, size_t length) + {ProcessData(inoutString, inoutString, length);} + //! same as ProcessData(outString, inString, length) + inline void ProcessString(byte *outString, const byte *inString, size_t length) + {ProcessData(outString, inString, length);} + //! implemented as {ProcessData(&input, &input, 1); return input;} + inline byte ProcessByte(byte input) + {ProcessData(&input, &input, 1); return input;} + + //! returns whether this cipher supports random access + virtual bool IsRandomAccess() const =0; + //! for random access ciphers, seek to an absolute position + virtual void Seek(lword n) + { + assert(!IsRandomAccess()); + throw NotImplemented("StreamTransformation: this object doesn't support random access"); + } + + //! returns whether this transformation is self-inverting (e.g. xor with a keystream) + virtual bool IsSelfInverting() const =0; + //! returns whether this is an encryption object + virtual bool IsForwardTransformation() const =0; +}; + +//! interface for hash functions and data processing part of MACs + +/*! HashTransformation objects are stateful. They are created in an initial state, + change state as Update() is called, and return to the initial + state when Final() is called. This interface allows a large message to + be hashed in pieces by calling Update() on each piece followed by + calling Final(). +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE HashTransformation : public Algorithm +{ +public: + //! return a reference to this object, + /*! This function is useful for passing a temporary HashTransformation object to a + function that takes a non-const reference. */ + HashTransformation& Ref() {return *this;} + + //! process more input + virtual void Update(const byte *input, size_t length) =0; + + //! request space to write input into + virtual byte * CreateUpdateSpace(size_t &size) {size=0; return NULL;} + + //! compute hash for current message, then restart for a new message + /*! \pre size of digest == DigestSize(). */ + virtual void Final(byte *digest) + {TruncatedFinal(digest, DigestSize());} + + //! discard the current state, and restart with a new message + virtual void Restart() + {TruncatedFinal(NULL, 0);} + + //! size of the hash/digest/MAC returned by Final() + virtual unsigned int DigestSize() const =0; + + //! same as DigestSize() + unsigned int TagSize() const {return DigestSize();} + + + //! block size of underlying compression function, or 0 if not block based + virtual unsigned int BlockSize() const {return 0;} + + //! input to Update() should have length a multiple of this for optimal speed + virtual unsigned int OptimalBlockSize() const {return 1;} + + //! returns how input should be aligned for optimal performance + virtual unsigned int OptimalDataAlignment() const; + + //! use this if your input is in one piece and you don't want to call Update() and Final() separately + virtual void CalculateDigest(byte *digest, const byte *input, size_t length) + {Update(input, length); Final(digest);} + + //! verify that digest is a valid digest for the current message, then reinitialize the object + /*! Default implementation is to call Final() and do a bitwise comparison + between its output and digest. */ + virtual bool Verify(const byte *digest) + {return TruncatedVerify(digest, DigestSize());} + + //! use this if your input is in one piece and you don't want to call Update() and Verify() separately + virtual bool VerifyDigest(const byte *digest, const byte *input, size_t length) + {Update(input, length); return Verify(digest);} + + //! truncated version of Final() + virtual void TruncatedFinal(byte *digest, size_t digestSize) =0; + + //! truncated version of CalculateDigest() + virtual void CalculateTruncatedDigest(byte *digest, size_t digestSize, const byte *input, size_t length) + {Update(input, length); TruncatedFinal(digest, digestSize);} + + //! truncated version of Verify() + virtual bool TruncatedVerify(const byte *digest, size_t digestLength); + + //! truncated version of VerifyDigest() + virtual bool VerifyTruncatedDigest(const byte *digest, size_t digestLength, const byte *input, size_t length) + {Update(input, length); return TruncatedVerify(digest, digestLength);} + +protected: + void ThrowIfInvalidTruncatedSize(size_t size) const; +}; + +typedef HashTransformation HashFunction; + +//! interface for one direction (encryption or decryption) of a block cipher +/*! \note These objects usually should not be used directly. See BlockTransformation for more details. */ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE BlockCipher : public SimpleKeyingInterface, public BlockTransformation +{ +protected: + const Algorithm & GetAlgorithm() const {return *this;} +}; + +//! interface for one direction (encryption or decryption) of a stream cipher or cipher mode +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE SymmetricCipher : public SimpleKeyingInterface, public StreamTransformation +{ +protected: + const Algorithm & GetAlgorithm() const {return *this;} +}; + +//! interface for message authentication codes +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE MessageAuthenticationCode : public SimpleKeyingInterface, public HashTransformation +{ +protected: + const Algorithm & GetAlgorithm() const {return *this;} +}; + +//! interface for for one direction (encryption or decryption) of a stream cipher or block cipher mode with authentication +/*! The StreamTransformation part of this interface is used to encrypt/decrypt the data, and the MessageAuthenticationCode part of this + interface is used to input additional authenticated data (AAD, which is MAC'ed but not encrypted), and to generate/verify the MAC. */ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AuthenticatedSymmetricCipher : public MessageAuthenticationCode, public StreamTransformation +{ +public: + //! this indicates that a member function was called in the wrong state, for example trying to encrypt a message before having set the key or IV + class BadState : public Exception + { + public: + explicit BadState(const std::string &name, const char *message) : Exception(OTHER_ERROR, name + ": " + message) {} + explicit BadState(const std::string &name, const char *function, const char *state) : Exception(OTHER_ERROR, name + ": " + function + " was called before " + state) {} + }; + + //! the maximum length of AAD that can be input before the encrypted data + virtual lword MaxHeaderLength() const =0; + //! the maximum length of encrypted data + virtual lword MaxMessageLength() const =0; + //! the maximum length of AAD that can be input after the encrypted data + virtual lword MaxFooterLength() const {return 0;} + //! if this function returns true, SpecifyDataLengths() must be called before attempting to input data + /*! This is the case for some schemes, such as CCM. */ + virtual bool NeedsPrespecifiedDataLengths() const {return false;} + //! this function only needs to be called if NeedsPrespecifiedDataLengths() returns true + void SpecifyDataLengths(lword headerLength, lword messageLength, lword footerLength=0); + //! encrypt and generate MAC in one call. will truncate MAC if macSize < TagSize() + virtual void EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *header, size_t headerLength, const byte *message, size_t messageLength); + //! decrypt and verify MAC in one call, returning true iff MAC is valid. will assume MAC is truncated if macLength < TagSize() + virtual bool DecryptAndVerify(byte *message, const byte *mac, size_t macLength, const byte *iv, int ivLength, const byte *header, size_t headerLength, const byte *ciphertext, size_t ciphertextLength); + + // redeclare this to avoid compiler ambiguity errors + virtual std::string AlgorithmName() const =0; + +protected: + const Algorithm & GetAlgorithm() const {return *static_cast(this);} + virtual void UncheckedSpecifyDataLengths(lword headerLength, lword messageLength, lword footerLength) {} +}; + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY +typedef SymmetricCipher StreamCipher; +#endif + +//! interface for random number generators +/*! All return values are uniformly distributed over the range specified. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE RandomNumberGenerator : public Algorithm +{ +public: + //! update RNG state with additional unpredictable values + virtual void IncorporateEntropy(const byte *input, size_t length) {throw NotImplemented("RandomNumberGenerator: IncorporateEntropy not implemented");} + + //! returns true if IncorporateEntropy is implemented + virtual bool CanIncorporateEntropy() const {return false;} + + //! generate new random byte and return it + virtual byte GenerateByte(); + + //! generate new random bit and return it + /*! Default implementation is to call GenerateByte() and return its lowest bit. */ + virtual unsigned int GenerateBit(); + + //! generate a random 32 bit word in the range min to max, inclusive + virtual word32 GenerateWord32(word32 a=0, word32 b=0xffffffffL); + + //! generate random array of bytes + virtual void GenerateBlock(byte *output, size_t size); + + //! generate and discard n bytes + virtual void DiscardBytes(size_t n); + + //! generate random bytes as input to a BufferedTransformation + virtual void GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword length); + + //! randomly shuffle the specified array, resulting permutation is uniformly distributed + template void Shuffle(IT begin, IT end) + { + for (; begin != end; ++begin) + std::iter_swap(begin, begin + GenerateWord32(0, end-begin-1)); + } + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + byte GetByte() {return GenerateByte();} + unsigned int GetBit() {return GenerateBit();} + word32 GetLong(word32 a=0, word32 b=0xffffffffL) {return GenerateWord32(a, b);} + word16 GetShort(word16 a=0, word16 b=0xffff) {return (word16)GenerateWord32(a, b);} + void GetBlock(byte *output, size_t size) {GenerateBlock(output, size);} +#endif +}; + +//! returns a reference that can be passed to functions that ask for a RNG but doesn't actually use it +CRYPTOPP_DLL RandomNumberGenerator & CRYPTOPP_API NullRNG(); + +class WaitObjectContainer; +class CallStack; + +//! interface for objects that you can wait for + +class CRYPTOPP_NO_VTABLE Waitable +{ +public: + virtual ~Waitable() {} + + //! maximum number of wait objects that this object can return + virtual unsigned int GetMaxWaitObjectCount() const =0; + //! put wait objects into container + /*! \param callStack is used for tracing no wait loops, example: + something.GetWaitObjects(c, CallStack("my func after X", 0)); + - or in an outer GetWaitObjects() method that itself takes a callStack parameter: + innerThing.GetWaitObjects(c, CallStack("MyClass::GetWaitObjects at X", &callStack)); */ + virtual void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) =0; + //! wait on this object + /*! same as creating an empty container, calling GetWaitObjects(), and calling Wait() on the container */ + bool Wait(unsigned long milliseconds, CallStack const& callStack); +}; + +//! the default channel for BufferedTransformation, equal to the empty string +extern CRYPTOPP_DLL const std::string DEFAULT_CHANNEL; + +//! channel for additional authenticated data, equal to "AAD" +extern CRYPTOPP_DLL const std::string AAD_CHANNEL; + +//! interface for buffered transformations + +/*! BufferedTransformation is a generalization of BlockTransformation, + StreamTransformation, and HashTransformation. + + A buffered transformation is an object that takes a stream of bytes + as input (this may be done in stages), does some computation on them, and + then places the result into an internal buffer for later retrieval. Any + partial result already in the output buffer is not modified by further + input. + + If a method takes a "blocking" parameter, and you + pass "false" for it, the method will return before all input has been processed if + the input cannot be processed without waiting (for network buffers to become available, for example). + In this case the method will return true + or a non-zero integer value. When this happens you must continue to call the method with the same + parameters until it returns false or zero, before calling any other method on it or + attached BufferedTransformation. The integer return value in this case is approximately + the number of bytes left to be processed, and can be used to implement a progress bar. + + For functions that take a "propagation" parameter, propagation != 0 means pass on the signal to attached + BufferedTransformation objects, with propagation decremented at each step until it reaches 0. + -1 means unlimited propagation. + + \nosubgrouping +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE BufferedTransformation : public Algorithm, public Waitable +{ +public: + // placed up here for CW8 + static const std::string &NULL_CHANNEL; // same as DEFAULT_CHANNEL, for backwards compatibility + + BufferedTransformation() : Algorithm(false) {} + + //! return a reference to this object + /*! This function is useful for passing a temporary BufferedTransformation object to a + function that takes a non-const reference. */ + BufferedTransformation& Ref() {return *this;} + + //! \name INPUT + //@{ + //! input a byte for processing + size_t Put(byte inByte, bool blocking=true) + {return Put(&inByte, 1, blocking);} + //! input multiple bytes + size_t Put(const byte *inString, size_t length, bool blocking=true) + {return Put2(inString, length, 0, blocking);} + + //! input a 16-bit word + size_t PutWord16(word16 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); + //! input a 32-bit word + size_t PutWord32(word32 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); + + //! request space which can be written into by the caller, and then used as input to Put() + /*! \param size is requested size (as a hint) for input, and size of the returned space for output */ + /*! \note The purpose of this method is to help avoid doing extra memory allocations. */ + virtual byte * CreatePutSpace(size_t &size) {size=0; return NULL;} + + virtual bool CanModifyInput() const {return false;} + + //! input multiple bytes that may be modified by callee + size_t PutModifiable(byte *inString, size_t length, bool blocking=true) + {return PutModifiable2(inString, length, 0, blocking);} + + bool MessageEnd(int propagation=-1, bool blocking=true) + {return !!Put2(NULL, 0, propagation < 0 ? -1 : propagation+1, blocking);} + size_t PutMessageEnd(const byte *inString, size_t length, int propagation=-1, bool blocking=true) + {return Put2(inString, length, propagation < 0 ? -1 : propagation+1, blocking);} + + //! input multiple bytes for blocking or non-blocking processing + /*! \param messageEnd means how many filters to signal MessageEnd to, including this one */ + virtual size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking) =0; + //! input multiple bytes that may be modified by callee for blocking or non-blocking processing + /*! \param messageEnd means how many filters to signal MessageEnd to, including this one */ + virtual size_t PutModifiable2(byte *inString, size_t length, int messageEnd, bool blocking) + {return Put2(inString, length, messageEnd, blocking);} + + //! thrown by objects that have not implemented nonblocking input processing + struct BlockingInputOnly : public NotImplemented + {BlockingInputOnly(const std::string &s) : NotImplemented(s + ": Nonblocking input is not implemented by this object.") {}}; + //@} + + //! \name WAITING + //@{ + unsigned int GetMaxWaitObjectCount() const; + void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); + //@} + + //! \name SIGNALS + //@{ + virtual void IsolatedInitialize(const NameValuePairs ¶meters) {throw NotImplemented("BufferedTransformation: this object can't be reinitialized");} + virtual bool IsolatedFlush(bool hardFlush, bool blocking) =0; + virtual bool IsolatedMessageSeriesEnd(bool blocking) {return false;} + + //! initialize or reinitialize this object + virtual void Initialize(const NameValuePairs ¶meters=g_nullNameValuePairs, int propagation=-1); + //! flush buffered input and/or output + /*! \param hardFlush is used to indicate whether all data should be flushed + \note Hard flushes must be used with care. It means try to process and output everything, even if + there may not be enough data to complete the action. For example, hard flushing a HexDecoder would + cause an error if you do it after inputing an odd number of hex encoded characters. + For some types of filters, for example ZlibDecompressor, hard flushes can only + be done at "synchronization points". These synchronization points are positions in the data + stream that are created by hard flushes on the corresponding reverse filters, in this + example ZlibCompressor. This is useful when zlib compressed data is moved across a + network in packets and compression state is preserved across packets, as in the ssh2 protocol. + */ + virtual bool Flush(bool hardFlush, int propagation=-1, bool blocking=true); + //! mark end of a series of messages + /*! There should be a MessageEnd immediately before MessageSeriesEnd. */ + virtual bool MessageSeriesEnd(int propagation=-1, bool blocking=true); + + //! set propagation of automatically generated and transferred signals + /*! propagation == 0 means do not automaticly generate signals */ + virtual void SetAutoSignalPropagation(int propagation) {} + + //! + virtual int GetAutoSignalPropagation() const {return 0;} +public: + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + void Close() {MessageEnd();} +#endif + //@} + + //! \name RETRIEVAL OF ONE MESSAGE + //@{ + //! returns number of bytes that is currently ready for retrieval + /*! All retrieval functions return the actual number of bytes + retrieved, which is the lesser of the request number and + MaxRetrievable(). */ + virtual lword MaxRetrievable() const; + + //! returns whether any bytes are currently ready for retrieval + virtual bool AnyRetrievable() const; + + //! try to retrieve a single byte + virtual size_t Get(byte &outByte); + //! try to retrieve multiple bytes + virtual size_t Get(byte *outString, size_t getMax); + + //! peek at the next byte without removing it from the output buffer + virtual size_t Peek(byte &outByte) const; + //! peek at multiple bytes without removing them from the output buffer + virtual size_t Peek(byte *outString, size_t peekMax) const; + + //! try to retrieve a 16-bit word + size_t GetWord16(word16 &value, ByteOrder order=BIG_ENDIAN_ORDER); + //! try to retrieve a 32-bit word + size_t GetWord32(word32 &value, ByteOrder order=BIG_ENDIAN_ORDER); + + //! try to peek at a 16-bit word + size_t PeekWord16(word16 &value, ByteOrder order=BIG_ENDIAN_ORDER) const; + //! try to peek at a 32-bit word + size_t PeekWord32(word32 &value, ByteOrder order=BIG_ENDIAN_ORDER) const; + + //! move transferMax bytes of the buffered output to target as input + lword TransferTo(BufferedTransformation &target, lword transferMax=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL) + {TransferTo2(target, transferMax, channel); return transferMax;} + + //! discard skipMax bytes from the output buffer + virtual lword Skip(lword skipMax=LWORD_MAX); + + //! copy copyMax bytes of the buffered output to target as input + lword CopyTo(BufferedTransformation &target, lword copyMax=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL) const + {return CopyRangeTo(target, 0, copyMax, channel);} + + //! copy copyMax bytes of the buffered output, starting at position (relative to current position), to target as input + lword CopyRangeTo(BufferedTransformation &target, lword position, lword copyMax=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL) const + {lword i = position; CopyRangeTo2(target, i, i+copyMax, channel); return i-position;} + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + unsigned long MaxRetrieveable() const {return MaxRetrievable();} +#endif + //@} + + //! \name RETRIEVAL OF MULTIPLE MESSAGES + //@{ + //! + virtual lword TotalBytesRetrievable() const; + //! number of times MessageEnd() has been received minus messages retrieved or skipped + virtual unsigned int NumberOfMessages() const; + //! returns true if NumberOfMessages() > 0 + virtual bool AnyMessages() const; + //! start retrieving the next message + /*! + Returns false if no more messages exist or this message + is not completely retrieved. + */ + virtual bool GetNextMessage(); + //! skip count number of messages + virtual unsigned int SkipMessages(unsigned int count=UINT_MAX); + //! + unsigned int TransferMessagesTo(BufferedTransformation &target, unsigned int count=UINT_MAX, const std::string &channel=DEFAULT_CHANNEL) + {TransferMessagesTo2(target, count, channel); return count;} + //! + unsigned int CopyMessagesTo(BufferedTransformation &target, unsigned int count=UINT_MAX, const std::string &channel=DEFAULT_CHANNEL) const; + + //! + virtual void SkipAll(); + //! + void TransferAllTo(BufferedTransformation &target, const std::string &channel=DEFAULT_CHANNEL) + {TransferAllTo2(target, channel);} + //! + void CopyAllTo(BufferedTransformation &target, const std::string &channel=DEFAULT_CHANNEL) const; + + virtual bool GetNextMessageSeries() {return false;} + virtual unsigned int NumberOfMessagesInThisSeries() const {return NumberOfMessages();} + virtual unsigned int NumberOfMessageSeries() const {return 0;} + //@} + + //! \name NON-BLOCKING TRANSFER OF OUTPUT + //@{ + //! upon return, byteCount contains number of bytes that have finished being transfered, and returns the number of bytes left in the current transfer block + virtual size_t TransferTo2(BufferedTransformation &target, lword &byteCount, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) =0; + //! upon return, begin contains the start position of data yet to be finished copying, and returns the number of bytes left in the current transfer block + virtual size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const =0; + //! upon return, messageCount contains number of messages that have finished being transfered, and returns the number of bytes left in the current transfer block + size_t TransferMessagesTo2(BufferedTransformation &target, unsigned int &messageCount, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); + //! returns the number of bytes left in the current transfer block + size_t TransferAllTo2(BufferedTransformation &target, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); + //@} + + //! \name CHANNELS + //@{ + struct NoChannelSupport : public NotImplemented + {NoChannelSupport(const std::string &name) : NotImplemented(name + ": this object doesn't support multiple channels") {}}; + struct InvalidChannelName : public InvalidArgument + {InvalidChannelName(const std::string &name, const std::string &channel) : InvalidArgument(name + ": unexpected channel name \"" + channel + "\"") {}}; + + size_t ChannelPut(const std::string &channel, byte inByte, bool blocking=true) + {return ChannelPut(channel, &inByte, 1, blocking);} + size_t ChannelPut(const std::string &channel, const byte *inString, size_t length, bool blocking=true) + {return ChannelPut2(channel, inString, length, 0, blocking);} + + size_t ChannelPutModifiable(const std::string &channel, byte *inString, size_t length, bool blocking=true) + {return ChannelPutModifiable2(channel, inString, length, 0, blocking);} + + size_t ChannelPutWord16(const std::string &channel, word16 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); + size_t ChannelPutWord32(const std::string &channel, word32 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); + + bool ChannelMessageEnd(const std::string &channel, int propagation=-1, bool blocking=true) + {return !!ChannelPut2(channel, NULL, 0, propagation < 0 ? -1 : propagation+1, blocking);} + size_t ChannelPutMessageEnd(const std::string &channel, const byte *inString, size_t length, int propagation=-1, bool blocking=true) + {return ChannelPut2(channel, inString, length, propagation < 0 ? -1 : propagation+1, blocking);} + + virtual byte * ChannelCreatePutSpace(const std::string &channel, size_t &size); + + virtual size_t ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking); + virtual size_t ChannelPutModifiable2(const std::string &channel, byte *begin, size_t length, int messageEnd, bool blocking); + + virtual bool ChannelFlush(const std::string &channel, bool hardFlush, int propagation=-1, bool blocking=true); + virtual bool ChannelMessageSeriesEnd(const std::string &channel, int propagation=-1, bool blocking=true); + + virtual void SetRetrievalChannel(const std::string &channel); + //@} + + //! \name ATTACHMENT + /*! Some BufferedTransformation objects (e.g. Filter objects) + allow other BufferedTransformation objects to be attached. When + this is done, the first object instead of buffering its output, + sents that output to the attached object as input. The entire + attachment chain is deleted when the anchor object is destructed. + */ + //@{ + //! returns whether this object allows attachment + virtual bool Attachable() {return false;} + //! returns the object immediately attached to this object or NULL for no attachment + virtual BufferedTransformation *AttachedTransformation() {assert(!Attachable()); return 0;} + //! + virtual const BufferedTransformation *AttachedTransformation() const + {return const_cast(this)->AttachedTransformation();} + //! delete the current attachment chain and replace it with newAttachment + virtual void Detach(BufferedTransformation *newAttachment = 0) + {assert(!Attachable()); throw NotImplemented("BufferedTransformation: this object is not attachable");} + //! add newAttachment to the end of attachment chain + virtual void Attach(BufferedTransformation *newAttachment); + //@} + +protected: + static int DecrementPropagation(int propagation) + {return propagation != 0 ? propagation - 1 : 0;} + +private: + byte m_buf[4]; // for ChannelPutWord16 and ChannelPutWord32, to ensure buffer isn't deallocated before non-blocking operation completes +}; + +//! returns a reference to a BufferedTransformation object that discards all input +BufferedTransformation & TheBitBucket(); + +//! interface for crypto material, such as public and private keys, and crypto parameters + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CryptoMaterial : public NameValuePairs +{ +public: + //! exception thrown when invalid crypto material is detected + class CRYPTOPP_DLL InvalidMaterial : public InvalidDataFormat + { + public: + explicit InvalidMaterial(const std::string &s) : InvalidDataFormat(s) {} + }; + + //! assign values from source to this object + /*! \note This function can be used to create a public key from a private key. */ + virtual void AssignFrom(const NameValuePairs &source) =0; + + //! check this object for errors + /*! \param level denotes the level of thoroughness: + 0 - using this object won't cause a crash or exception (rng is ignored) + 1 - this object will probably function (encrypt, sign, etc.) correctly (but may not check for weak keys and such) + 2 - make sure this object will function correctly, and do reasonable security checks + 3 - do checks that may take a long time + \return true if the tests pass */ + virtual bool Validate(RandomNumberGenerator &rng, unsigned int level) const =0; + + //! throws InvalidMaterial if this object fails Validate() test + virtual void ThrowIfInvalid(RandomNumberGenerator &rng, unsigned int level) const + {if (!Validate(rng, level)) throw InvalidMaterial("CryptoMaterial: this object contains invalid values");} + +// virtual std::vector GetSupportedFormats(bool includeSaveOnly=false, bool includeLoadOnly=false); + + //! save key into a BufferedTransformation + virtual void Save(BufferedTransformation &bt) const + {throw NotImplemented("CryptoMaterial: this object does not support saving");} + + //! load key from a BufferedTransformation + /*! \throws KeyingErr if decode fails + \note Generally does not check that the key is valid. + Call ValidateKey() or ThrowIfInvalidKey() to check that. */ + virtual void Load(BufferedTransformation &bt) + {throw NotImplemented("CryptoMaterial: this object does not support loading");} + + //! \return whether this object supports precomputation + virtual bool SupportsPrecomputation() const {return false;} + //! do precomputation + /*! The exact semantics of Precompute() is varies, but + typically it means calculate a table of n objects + that can be used later to speed up computation. */ + virtual void Precompute(unsigned int n) + {assert(!SupportsPrecomputation()); throw NotImplemented("CryptoMaterial: this object does not support precomputation");} + //! retrieve previously saved precomputation + virtual void LoadPrecomputation(BufferedTransformation &storedPrecomputation) + {assert(!SupportsPrecomputation()); throw NotImplemented("CryptoMaterial: this object does not support precomputation");} + //! save precomputation for later use + virtual void SavePrecomputation(BufferedTransformation &storedPrecomputation) const + {assert(!SupportsPrecomputation()); throw NotImplemented("CryptoMaterial: this object does not support precomputation");} + + // for internal library use + void DoQuickSanityCheck() const {ThrowIfInvalid(NullRNG(), 0);} + +#if (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) + // Sun Studio 11/CC 5.8 workaround: it generates incorrect code when casting to an empty virtual base class + char m_sunCCworkaround; +#endif +}; + +//! interface for generatable crypto material, such as private keys and crypto parameters + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE GeneratableCryptoMaterial : virtual public CryptoMaterial +{ +public: + //! generate a random key or crypto parameters + /*! \throws KeyingErr if algorithm parameters are invalid, or if a key can't be generated + (e.g., if this is a public key object) */ + virtual void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs ¶ms = g_nullNameValuePairs) + {throw NotImplemented("GeneratableCryptoMaterial: this object does not support key/parameter generation");} + + //! calls the above function with a NameValuePairs object that just specifies "KeySize" + void GenerateRandomWithKeySize(RandomNumberGenerator &rng, unsigned int keySize); +}; + +//! interface for public keys + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PublicKey : virtual public CryptoMaterial +{ +}; + +//! interface for private keys + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PrivateKey : public GeneratableCryptoMaterial +{ +}; + +//! interface for crypto prameters + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CryptoParameters : public GeneratableCryptoMaterial +{ +}; + +//! interface for asymmetric algorithms + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AsymmetricAlgorithm : public Algorithm +{ +public: + //! returns a reference to the crypto material used by this object + virtual CryptoMaterial & AccessMaterial() =0; + //! returns a const reference to the crypto material used by this object + virtual const CryptoMaterial & GetMaterial() const =0; + + //! for backwards compatibility, calls AccessMaterial().Load(bt) + void BERDecode(BufferedTransformation &bt) + {AccessMaterial().Load(bt);} + //! for backwards compatibility, calls GetMaterial().Save(bt) + void DEREncode(BufferedTransformation &bt) const + {GetMaterial().Save(bt);} +}; + +//! interface for asymmetric algorithms using public keys + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PublicKeyAlgorithm : public AsymmetricAlgorithm +{ +public: + // VC60 workaround: no co-variant return type + CryptoMaterial & AccessMaterial() {return AccessPublicKey();} + const CryptoMaterial & GetMaterial() const {return GetPublicKey();} + + virtual PublicKey & AccessPublicKey() =0; + virtual const PublicKey & GetPublicKey() const {return const_cast(this)->AccessPublicKey();} +}; + +//! interface for asymmetric algorithms using private keys + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PrivateKeyAlgorithm : public AsymmetricAlgorithm +{ +public: + CryptoMaterial & AccessMaterial() {return AccessPrivateKey();} + const CryptoMaterial & GetMaterial() const {return GetPrivateKey();} + + virtual PrivateKey & AccessPrivateKey() =0; + virtual const PrivateKey & GetPrivateKey() const {return const_cast(this)->AccessPrivateKey();} +}; + +//! interface for key agreement algorithms + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE KeyAgreementAlgorithm : public AsymmetricAlgorithm +{ +public: + CryptoMaterial & AccessMaterial() {return AccessCryptoParameters();} + const CryptoMaterial & GetMaterial() const {return GetCryptoParameters();} + + virtual CryptoParameters & AccessCryptoParameters() =0; + virtual const CryptoParameters & GetCryptoParameters() const {return const_cast(this)->AccessCryptoParameters();} +}; + +//! interface for public-key encryptors and decryptors + +/*! This class provides an interface common to encryptors and decryptors + for querying their plaintext and ciphertext lengths. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_CryptoSystem +{ +public: + virtual ~PK_CryptoSystem() {} + + //! maximum length of plaintext for a given ciphertext length + /*! \note This function returns 0 if ciphertextLength is not valid (too long or too short). */ + virtual size_t MaxPlaintextLength(size_t ciphertextLength) const =0; + + //! calculate length of ciphertext given length of plaintext + /*! \note This function returns 0 if plaintextLength is not valid (too long). */ + virtual size_t CiphertextLength(size_t plaintextLength) const =0; + + //! this object supports the use of the parameter with the given name + /*! some possible parameter names: EncodingParameters, KeyDerivationParameters */ + virtual bool ParameterSupported(const char *name) const =0; + + //! return fixed ciphertext length, if one exists, otherwise return 0 + /*! \note "Fixed" here means length of ciphertext does not depend on length of plaintext. + It usually does depend on the key length. */ + virtual size_t FixedCiphertextLength() const {return 0;} + + //! return maximum plaintext length given the fixed ciphertext length, if one exists, otherwise return 0 + virtual size_t FixedMaxPlaintextLength() const {return 0;} + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + size_t MaxPlainTextLength(size_t cipherTextLength) const {return MaxPlaintextLength(cipherTextLength);} + size_t CipherTextLength(size_t plainTextLength) const {return CiphertextLength(plainTextLength);} +#endif +}; + +//! interface for public-key encryptors +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Encryptor : public PK_CryptoSystem, public PublicKeyAlgorithm +{ +public: + //! exception thrown when trying to encrypt plaintext of invalid length + class CRYPTOPP_DLL InvalidPlaintextLength : public Exception + { + public: + InvalidPlaintextLength() : Exception(OTHER_ERROR, "PK_Encryptor: invalid plaintext length") {} + }; + + //! encrypt a byte string + /*! \pre CiphertextLength(plaintextLength) != 0 (i.e., plaintext isn't too long) + \pre size of ciphertext == CiphertextLength(plaintextLength) + */ + virtual void Encrypt(RandomNumberGenerator &rng, + const byte *plaintext, size_t plaintextLength, + byte *ciphertext, const NameValuePairs ¶meters = g_nullNameValuePairs) const =0; + + //! create a new encryption filter + /*! \note The caller is responsible for deleting the returned pointer. + \note Encoding parameters should be passed in the "EP" channel. + */ + virtual BufferedTransformation * CreateEncryptionFilter(RandomNumberGenerator &rng, + BufferedTransformation *attachment=NULL, const NameValuePairs ¶meters = g_nullNameValuePairs) const; +}; + +//! interface for public-key decryptors + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Decryptor : public PK_CryptoSystem, public PrivateKeyAlgorithm +{ +public: + //! decrypt a byte string, and return the length of plaintext + /*! \pre size of plaintext == MaxPlaintextLength(ciphertextLength) bytes. + \return the actual length of the plaintext, indication that decryption failed. + */ + virtual DecodingResult Decrypt(RandomNumberGenerator &rng, + const byte *ciphertext, size_t ciphertextLength, + byte *plaintext, const NameValuePairs ¶meters = g_nullNameValuePairs) const =0; + + //! create a new decryption filter + /*! \note caller is responsible for deleting the returned pointer + */ + virtual BufferedTransformation * CreateDecryptionFilter(RandomNumberGenerator &rng, + BufferedTransformation *attachment=NULL, const NameValuePairs ¶meters = g_nullNameValuePairs) const; + + //! decrypt a fixed size ciphertext + DecodingResult FixedLengthDecrypt(RandomNumberGenerator &rng, const byte *ciphertext, byte *plaintext, const NameValuePairs ¶meters = g_nullNameValuePairs) const + {return Decrypt(rng, ciphertext, FixedCiphertextLength(), plaintext, parameters);} +}; + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY +typedef PK_CryptoSystem PK_FixedLengthCryptoSystem; +typedef PK_Encryptor PK_FixedLengthEncryptor; +typedef PK_Decryptor PK_FixedLengthDecryptor; +#endif + +//! interface for public-key signers and verifiers + +/*! This class provides an interface common to signers and verifiers + for querying scheme properties. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_SignatureScheme +{ +public: + //! invalid key exception, may be thrown by any function in this class if the private or public key has a length that can't be used + class CRYPTOPP_DLL InvalidKeyLength : public Exception + { + public: + InvalidKeyLength(const std::string &message) : Exception(OTHER_ERROR, message) {} + }; + + //! key too short exception, may be thrown by any function in this class if the private or public key is too short to sign or verify anything + class CRYPTOPP_DLL KeyTooShort : public InvalidKeyLength + { + public: + KeyTooShort() : InvalidKeyLength("PK_Signer: key too short for this signature scheme") {} + }; + + virtual ~PK_SignatureScheme() {} + + //! signature length if it only depends on the key, otherwise 0 + virtual size_t SignatureLength() const =0; + + //! maximum signature length produced for a given length of recoverable message part + virtual size_t MaxSignatureLength(size_t recoverablePartLength = 0) const {return SignatureLength();} + + //! length of longest message that can be recovered, or 0 if this signature scheme does not support message recovery + virtual size_t MaxRecoverableLength() const =0; + + //! length of longest message that can be recovered from a signature of given length, or 0 if this signature scheme does not support message recovery + virtual size_t MaxRecoverableLengthFromSignatureLength(size_t signatureLength) const =0; + + //! requires a random number generator to sign + /*! if this returns false, NullRNG() can be passed to functions that take RandomNumberGenerator & */ + virtual bool IsProbabilistic() const =0; + + //! whether or not a non-recoverable message part can be signed + virtual bool AllowNonrecoverablePart() const =0; + + //! if this function returns true, during verification you must input the signature before the message, otherwise you can input it at anytime */ + virtual bool SignatureUpfront() const {return false;} + + //! whether you must input the recoverable part before the non-recoverable part during signing + virtual bool RecoverablePartFirst() const =0; +}; + +//! interface for accumulating messages to be signed or verified +/*! Only Update() should be called + on this class. No other functions inherited from HashTransformation should be called. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_MessageAccumulator : public HashTransformation +{ +public: + //! should not be called on PK_MessageAccumulator + unsigned int DigestSize() const + {throw NotImplemented("PK_MessageAccumulator: DigestSize() should not be called");} + //! should not be called on PK_MessageAccumulator + void TruncatedFinal(byte *digest, size_t digestSize) + {throw NotImplemented("PK_MessageAccumulator: TruncatedFinal() should not be called");} +}; + +//! interface for public-key signers + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Signer : public PK_SignatureScheme, public PrivateKeyAlgorithm +{ +public: + //! create a new HashTransformation to accumulate the message to be signed + virtual PK_MessageAccumulator * NewSignatureAccumulator(RandomNumberGenerator &rng) const =0; + + virtual void InputRecoverableMessage(PK_MessageAccumulator &messageAccumulator, const byte *recoverableMessage, size_t recoverableMessageLength) const =0; + + //! sign and delete messageAccumulator (even in case of exception thrown) + /*! \pre size of signature == MaxSignatureLength() + \return actual signature length + */ + virtual size_t Sign(RandomNumberGenerator &rng, PK_MessageAccumulator *messageAccumulator, byte *signature) const; + + //! sign and restart messageAccumulator + /*! \pre size of signature == MaxSignatureLength() + \return actual signature length + */ + virtual size_t SignAndRestart(RandomNumberGenerator &rng, PK_MessageAccumulator &messageAccumulator, byte *signature, bool restart=true) const =0; + + //! sign a message + /*! \pre size of signature == MaxSignatureLength() + \return actual signature length + */ + virtual size_t SignMessage(RandomNumberGenerator &rng, const byte *message, size_t messageLen, byte *signature) const; + + //! sign a recoverable message + /*! \pre size of signature == MaxSignatureLength(recoverableMessageLength) + \return actual signature length + */ + virtual size_t SignMessageWithRecovery(RandomNumberGenerator &rng, const byte *recoverableMessage, size_t recoverableMessageLength, + const byte *nonrecoverableMessage, size_t nonrecoverableMessageLength, byte *signature) const; +}; + +//! interface for public-key signature verifiers +/*! The Recover* functions throw NotImplemented if the signature scheme does not support + message recovery. + The Verify* functions throw InvalidDataFormat if the scheme does support message + recovery and the signature contains a non-empty recoverable message part. The + Recovery* functions should be used in that case. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Verifier : public PK_SignatureScheme, public PublicKeyAlgorithm +{ +public: + //! create a new HashTransformation to accumulate the message to be verified + virtual PK_MessageAccumulator * NewVerificationAccumulator() const =0; + + //! input signature into a message accumulator + virtual void InputSignature(PK_MessageAccumulator &messageAccumulator, const byte *signature, size_t signatureLength) const =0; + + //! check whether messageAccumulator contains a valid signature and message, and delete messageAccumulator (even in case of exception thrown) + virtual bool Verify(PK_MessageAccumulator *messageAccumulator) const; + + //! check whether messageAccumulator contains a valid signature and message, and restart messageAccumulator + virtual bool VerifyAndRestart(PK_MessageAccumulator &messageAccumulator) const =0; + + //! check whether input signature is a valid signature for input message + virtual bool VerifyMessage(const byte *message, size_t messageLen, + const byte *signature, size_t signatureLength) const; + + //! recover a message from its signature + /*! \pre size of recoveredMessage == MaxRecoverableLengthFromSignatureLength(signatureLength) + */ + virtual DecodingResult Recover(byte *recoveredMessage, PK_MessageAccumulator *messageAccumulator) const; + + //! recover a message from its signature + /*! \pre size of recoveredMessage == MaxRecoverableLengthFromSignatureLength(signatureLength) + */ + virtual DecodingResult RecoverAndRestart(byte *recoveredMessage, PK_MessageAccumulator &messageAccumulator) const =0; + + //! recover a message from its signature + /*! \pre size of recoveredMessage == MaxRecoverableLengthFromSignatureLength(signatureLength) + */ + virtual DecodingResult RecoverMessage(byte *recoveredMessage, + const byte *nonrecoverableMessage, size_t nonrecoverableMessageLength, + const byte *signature, size_t signatureLength) const; +}; + +//! interface for domains of simple key agreement protocols + +/*! A key agreement domain is a set of parameters that must be shared + by two parties in a key agreement protocol, along with the algorithms + for generating key pairs and deriving agreed values. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE SimpleKeyAgreementDomain : public KeyAgreementAlgorithm +{ +public: + //! return length of agreed value produced + virtual unsigned int AgreedValueLength() const =0; + //! return length of private keys in this domain + virtual unsigned int PrivateKeyLength() const =0; + //! return length of public keys in this domain + virtual unsigned int PublicKeyLength() const =0; + //! generate private key + /*! \pre size of privateKey == PrivateKeyLength() */ + virtual void GeneratePrivateKey(RandomNumberGenerator &rng, byte *privateKey) const =0; + //! generate public key + /*! \pre size of publicKey == PublicKeyLength() */ + virtual void GeneratePublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const =0; + //! generate private/public key pair + /*! \note equivalent to calling GeneratePrivateKey() and then GeneratePublicKey() */ + virtual void GenerateKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const; + //! derive agreed value from your private key and couterparty's public key, return false in case of failure + /*! \note If you have previously validated the public key, use validateOtherPublicKey=false to save time. + \pre size of agreedValue == AgreedValueLength() + \pre length of privateKey == PrivateKeyLength() + \pre length of otherPublicKey == PublicKeyLength() + */ + virtual bool Agree(byte *agreedValue, const byte *privateKey, const byte *otherPublicKey, bool validateOtherPublicKey=true) const =0; + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + bool ValidateDomainParameters(RandomNumberGenerator &rng) const + {return GetCryptoParameters().Validate(rng, 2);} +#endif +}; + +//! interface for domains of authenticated key agreement protocols + +/*! In an authenticated key agreement protocol, each party has two + key pairs. The long-lived key pair is called the static key pair, + and the short-lived key pair is called the ephemeral key pair. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AuthenticatedKeyAgreementDomain : public KeyAgreementAlgorithm +{ +public: + //! return length of agreed value produced + virtual unsigned int AgreedValueLength() const =0; + + //! return length of static private keys in this domain + virtual unsigned int StaticPrivateKeyLength() const =0; + //! return length of static public keys in this domain + virtual unsigned int StaticPublicKeyLength() const =0; + //! generate static private key + /*! \pre size of privateKey == PrivateStaticKeyLength() */ + virtual void GenerateStaticPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const =0; + //! generate static public key + /*! \pre size of publicKey == PublicStaticKeyLength() */ + virtual void GenerateStaticPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const =0; + //! generate private/public key pair + /*! \note equivalent to calling GenerateStaticPrivateKey() and then GenerateStaticPublicKey() */ + virtual void GenerateStaticKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const; + + //! return length of ephemeral private keys in this domain + virtual unsigned int EphemeralPrivateKeyLength() const =0; + //! return length of ephemeral public keys in this domain + virtual unsigned int EphemeralPublicKeyLength() const =0; + //! generate ephemeral private key + /*! \pre size of privateKey == PrivateEphemeralKeyLength() */ + virtual void GenerateEphemeralPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const =0; + //! generate ephemeral public key + /*! \pre size of publicKey == PublicEphemeralKeyLength() */ + virtual void GenerateEphemeralPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const =0; + //! generate private/public key pair + /*! \note equivalent to calling GenerateEphemeralPrivateKey() and then GenerateEphemeralPublicKey() */ + virtual void GenerateEphemeralKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const; + + //! derive agreed value from your private keys and couterparty's public keys, return false in case of failure + /*! \note The ephemeral public key will always be validated. + If you have previously validated the static public key, use validateStaticOtherPublicKey=false to save time. + \pre size of agreedValue == AgreedValueLength() + \pre length of staticPrivateKey == StaticPrivateKeyLength() + \pre length of ephemeralPrivateKey == EphemeralPrivateKeyLength() + \pre length of staticOtherPublicKey == StaticPublicKeyLength() + \pre length of ephemeralOtherPublicKey == EphemeralPublicKeyLength() + */ + virtual bool Agree(byte *agreedValue, + const byte *staticPrivateKey, const byte *ephemeralPrivateKey, + const byte *staticOtherPublicKey, const byte *ephemeralOtherPublicKey, + bool validateStaticOtherPublicKey=true) const =0; + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + bool ValidateDomainParameters(RandomNumberGenerator &rng) const + {return GetCryptoParameters().Validate(rng, 2);} +#endif +}; + +// interface for password authenticated key agreement protocols, not implemented yet +#if 0 +//! interface for protocol sessions +/*! The methods should be called in the following order: + + InitializeSession(rng, parameters); // or call initialize method in derived class + while (true) + { + if (OutgoingMessageAvailable()) + { + length = GetOutgoingMessageLength(); + GetOutgoingMessage(message); + ; // send outgoing message + } + + if (LastMessageProcessed()) + break; + + ; // receive incoming message + ProcessIncomingMessage(message); + } + ; // call methods in derived class to obtain result of protocol session +*/ +class ProtocolSession +{ +public: + //! exception thrown when an invalid protocol message is processed + class ProtocolError : public Exception + { + public: + ProtocolError(ErrorType errorType, const std::string &s) : Exception(errorType, s) {} + }; + + //! exception thrown when a function is called unexpectedly + /*! for example calling ProcessIncomingMessage() when ProcessedLastMessage() == true */ + class UnexpectedMethodCall : public Exception + { + public: + UnexpectedMethodCall(const std::string &s) : Exception(OTHER_ERROR, s) {} + }; + + ProtocolSession() : m_rng(NULL), m_throwOnProtocolError(true), m_validState(false) {} + virtual ~ProtocolSession() {} + + virtual void InitializeSession(RandomNumberGenerator &rng, const NameValuePairs ¶meters) =0; + + bool GetThrowOnProtocolError() const {return m_throwOnProtocolError;} + void SetThrowOnProtocolError(bool throwOnProtocolError) {m_throwOnProtocolError = throwOnProtocolError;} + + bool HasValidState() const {return m_validState;} + + virtual bool OutgoingMessageAvailable() const =0; + virtual unsigned int GetOutgoingMessageLength() const =0; + virtual void GetOutgoingMessage(byte *message) =0; + + virtual bool LastMessageProcessed() const =0; + virtual void ProcessIncomingMessage(const byte *message, unsigned int messageLength) =0; + +protected: + void HandleProtocolError(Exception::ErrorType errorType, const std::string &s) const; + void CheckAndHandleInvalidState() const; + void SetValidState(bool valid) {m_validState = valid;} + + RandomNumberGenerator *m_rng; + +private: + bool m_throwOnProtocolError, m_validState; +}; + +class KeyAgreementSession : public ProtocolSession +{ +public: + virtual unsigned int GetAgreedValueLength() const =0; + virtual void GetAgreedValue(byte *agreedValue) const =0; +}; + +class PasswordAuthenticatedKeyAgreementSession : public KeyAgreementSession +{ +public: + void InitializePasswordAuthenticatedKeyAgreementSession(RandomNumberGenerator &rng, + const byte *myId, unsigned int myIdLength, + const byte *counterPartyId, unsigned int counterPartyIdLength, + const byte *passwordOrVerifier, unsigned int passwordOrVerifierLength); +}; + +class PasswordAuthenticatedKeyAgreementDomain : public KeyAgreementAlgorithm +{ +public: + //! return whether the domain parameters stored in this object are valid + virtual bool ValidateDomainParameters(RandomNumberGenerator &rng) const + {return GetCryptoParameters().Validate(rng, 2);} + + virtual unsigned int GetPasswordVerifierLength(const byte *password, unsigned int passwordLength) const =0; + virtual void GeneratePasswordVerifier(RandomNumberGenerator &rng, const byte *userId, unsigned int userIdLength, const byte *password, unsigned int passwordLength, byte *verifier) const =0; + + enum RoleFlags {CLIENT=1, SERVER=2, INITIATOR=4, RESPONDER=8}; + + virtual bool IsValidRole(unsigned int role) =0; + virtual PasswordAuthenticatedKeyAgreementSession * CreateProtocolSession(unsigned int role) const =0; +}; +#endif + +//! BER Decode Exception Class, may be thrown during an ASN1 BER decode operation +class CRYPTOPP_DLL BERDecodeErr : public InvalidArgument +{ +public: + BERDecodeErr() : InvalidArgument("BER decode error") {} + BERDecodeErr(const std::string &s) : InvalidArgument(s) {} +}; + +//! interface for encoding and decoding ASN1 objects +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE ASN1Object +{ +public: + virtual ~ASN1Object() {} + //! decode this object from a BufferedTransformation, using BER (Basic Encoding Rules) + virtual void BERDecode(BufferedTransformation &bt) =0; + //! encode this object into a BufferedTransformation, using DER (Distinguished Encoding Rules) + virtual void DEREncode(BufferedTransformation &bt) const =0; + //! encode this object into a BufferedTransformation, using BER + /*! this may be useful if DEREncode() would be too inefficient */ + virtual void BEREncode(BufferedTransformation &bt) const {DEREncode(bt);} +}; + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY +typedef PK_SignatureScheme PK_SignatureSystem; +typedef SimpleKeyAgreementDomain PK_SimpleKeyAgreementDomain; +typedef AuthenticatedKeyAgreementDomain PK_AuthenticatedKeyAgreementDomain; +#endif + +NAMESPACE_END + +#endif diff --git a/cryptopp/include/cryptopp/iterhash.h b/cryptopp/include/cryptopp/iterhash.h new file mode 100644 index 0000000000..0e2a147728 --- /dev/null +++ b/cryptopp/include/cryptopp/iterhash.h @@ -0,0 +1,29 @@ +#ifndef CRYPTOPP_ITERHASH_H +#define CRYPTOPP_ITERHASH_H + +#include "cryptopp/secblock.h" + +NAMESPACE_BEGIN(CryptoPP) + +// *** trimmed down dependency from iterhash.h *** +template +class CRYPTOPP_NO_VTABLE IteratedHashWithStaticTransform +{ +public: + CRYPTOPP_CONSTANT(DIGESTSIZE = T_DigestSize ? T_DigestSize : T_StateSize) + unsigned int DigestSize() const {return DIGESTSIZE;}; + typedef T_HashWordType HashWordType; + CRYPTOPP_CONSTANT(BLOCKSIZE = T_BlockSize) + +protected: + IteratedHashWithStaticTransform() {this->Init();} + void HashEndianCorrectedBlock(const T_HashWordType *data) {T_Transform::Transform(this->m_state, data);} + void Init() {T_Transform::InitState(this->m_state);} + + T_HashWordType* StateBuf() {return this->m_state;} + FixedSizeAlignedSecBlock m_state; +}; + +NAMESPACE_END + +#endif diff --git a/cryptopp/include/cryptopp/misc.h b/cryptopp/include/cryptopp/misc.h new file mode 100644 index 0000000000..5258e2abca --- /dev/null +++ b/cryptopp/include/cryptopp/misc.h @@ -0,0 +1,1134 @@ +#ifndef CRYPTOPP_MISC_H +#define CRYPTOPP_MISC_H + +#include "cryptopp/cryptlib.h" +#include "cryptopp/smartptr.h" +#include // for memcpy and memmove + +#ifdef _MSC_VER + #include + #if _MSC_VER >= 1400 + // VC2005 workaround: disable declarations that conflict with winnt.h + #define _interlockedbittestandset CRYPTOPP_DISABLED_INTRINSIC_1 + #define _interlockedbittestandreset CRYPTOPP_DISABLED_INTRINSIC_2 + #define _interlockedbittestandset64 CRYPTOPP_DISABLED_INTRINSIC_3 + #define _interlockedbittestandreset64 CRYPTOPP_DISABLED_INTRINSIC_4 + #include + #undef _interlockedbittestandset + #undef _interlockedbittestandreset + #undef _interlockedbittestandset64 + #undef _interlockedbittestandreset64 + #define CRYPTOPP_FAST_ROTATE(x) 1 + #elif _MSC_VER >= 1300 + #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32 | (x) == 64) + #else + #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32) + #endif +#elif (defined(__MWERKS__) && TARGET_CPU_PPC) || \ + (defined(__GNUC__) && (defined(_ARCH_PWR2) || defined(_ARCH_PWR) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || defined(_ARCH_COM))) + #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32) +#elif defined(__GNUC__) && (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X86) // depend on GCC's peephole optimization to generate rotate instructions + #define CRYPTOPP_FAST_ROTATE(x) 1 +#else + #define CRYPTOPP_FAST_ROTATE(x) 0 +#endif + +#ifdef __BORLANDC__ +#include +#endif + +#if defined(__GNUC__) && defined(__linux__) +#define CRYPTOPP_BYTESWAP_AVAILABLE +#include +#endif + +NAMESPACE_BEGIN(CryptoPP) + +// ************** compile-time assertion *************** + +template +struct CompileAssert +{ + static char dummy[2*b-1]; +}; + +#define CRYPTOPP_COMPILE_ASSERT(assertion) CRYPTOPP_COMPILE_ASSERT_INSTANCE(assertion, __LINE__) +#if defined(CRYPTOPP_EXPORTS) || defined(CRYPTOPP_IMPORTS) +#define CRYPTOPP_COMPILE_ASSERT_INSTANCE(assertion, instance) +#else +#define CRYPTOPP_COMPILE_ASSERT_INSTANCE(assertion, instance) static CompileAssert<(assertion)> CRYPTOPP_ASSERT_JOIN(cryptopp_assert_, instance) +#endif +#define CRYPTOPP_ASSERT_JOIN(X, Y) CRYPTOPP_DO_ASSERT_JOIN(X, Y) +#define CRYPTOPP_DO_ASSERT_JOIN(X, Y) X##Y + +// ************** misc classes *************** + +class CRYPTOPP_DLL Empty +{ +}; + +//! _ +template +class CRYPTOPP_NO_VTABLE TwoBases : public BASE1, public BASE2 +{ +}; + +//! _ +template +class CRYPTOPP_NO_VTABLE ThreeBases : public BASE1, public BASE2, public BASE3 +{ +}; + +template +class ObjectHolder +{ +protected: + T m_object; +}; + +class NotCopyable +{ +public: + NotCopyable() {} +private: + NotCopyable(const NotCopyable &); + void operator=(const NotCopyable &); +}; + +template +struct NewObject +{ + T* operator()() const {return new T;} +}; + +/*! This function safely initializes a static object in a multithreaded environment without using locks. + It may leak memory when two threads try to initialize the static object at the same time + but this should be acceptable since each static object is only initialized once per session. +*/ +template , int instance=0> +class Singleton +{ +public: + Singleton(F objectFactory = F()) : m_objectFactory(objectFactory) {} + + // prevent this function from being inlined + CRYPTOPP_NOINLINE const T & Ref(CRYPTOPP_NOINLINE_DOTDOTDOT) const; + +private: + F m_objectFactory; +}; + +template +const T & Singleton::Ref(CRYPTOPP_NOINLINE_DOTDOTDOT) const +{ + static simple_ptr s_pObject; + static char s_objectState = 0; + +retry: + switch (s_objectState) + { + case 0: + s_objectState = 1; + try + { + s_pObject.m_p = m_objectFactory(); + } + catch(...) + { + s_objectState = 0; + throw; + } + s_objectState = 2; + break; + case 1: + goto retry; + default: + break; + } + return *s_pObject.m_p; +} + +// ************** misc functions *************** + +#if (!__STDC_WANT_SECURE_LIB__) +inline void memcpy_s(void *dest, size_t sizeInBytes, const void *src, size_t count) +{ + if (count > sizeInBytes) + throw InvalidArgument("memcpy_s: buffer overflow"); + memcpy(dest, src, count); +} + +inline void memmove_s(void *dest, size_t sizeInBytes, const void *src, size_t count) +{ + if (count > sizeInBytes) + throw InvalidArgument("memmove_s: buffer overflow"); + memmove(dest, src, count); +} +#endif + +inline void * memset_z(void *ptr, int value, size_t num) +{ +// avoid extranous warning on GCC 4.3.2 Ubuntu 8.10 +#if CRYPTOPP_GCC_VERSION >= 30001 + if (__builtin_constant_p(num) && num==0) + return ptr; +#endif + return memset(ptr, value, num); +} + +// can't use std::min or std::max in MSVC60 or Cygwin 1.1.0 +template inline const T& STDMIN(const T& a, const T& b) +{ + return b < a ? b : a; +} + +template inline const T1 UnsignedMin(const T1& a, const T2& b) +{ + CRYPTOPP_COMPILE_ASSERT((sizeof(T1)<=sizeof(T2) && T2(-1)>0) || (sizeof(T1)>sizeof(T2) && T1(-1)>0)); + assert(a==0 || a>0); // GCC workaround: get rid of the warning "comparison is always true due to limited range of data type" + assert(b>=0); + + if (sizeof(T1)<=sizeof(T2)) + return b < (T2)a ? (T1)b : a; + else + return (T1)b < a ? (T1)b : a; +} + +template inline const T& STDMAX(const T& a, const T& b) +{ + return a < b ? b : a; +} + +#define RETURN_IF_NONZERO(x) size_t returnedValue = x; if (returnedValue) return returnedValue + +// this version of the macro is fastest on Pentium 3 and Pentium 4 with MSVC 6 SP5 w/ Processor Pack +#define GETBYTE(x, y) (unsigned int)byte((x)>>(8*(y))) +// these may be faster on other CPUs/compilers +// #define GETBYTE(x, y) (unsigned int)(((x)>>(8*(y)))&255) +// #define GETBYTE(x, y) (((byte *)&(x))[y]) + +#define CRYPTOPP_GET_BYTE_AS_BYTE(x, y) byte((x)>>(8*(y))) + +template +unsigned int Parity(T value) +{ + for (unsigned int i=8*sizeof(value)/2; i>0; i/=2) + value ^= value >> i; + return (unsigned int)value&1; +} + +template +unsigned int BytePrecision(const T &value) +{ + if (!value) + return 0; + + unsigned int l=0, h=8*sizeof(value); + + while (h-l > 8) + { + unsigned int t = (l+h)/2; + if (value >> t) + l = t; + else + h = t; + } + + return h/8; +} + +template +unsigned int BitPrecision(const T &value) +{ + if (!value) + return 0; + + unsigned int l=0, h=8*sizeof(value); + + while (h-l > 1) + { + unsigned int t = (l+h)/2; + if (value >> t) + l = t; + else + h = t; + } + + return h; +} + +template +inline T Crop(T value, size_t size) +{ + if (size < 8*sizeof(value)) + return T(value & ((T(1) << size) - 1)); + else + return value; +} + +template +inline bool SafeConvert(T1 from, T2 &to) +{ + to = (T2)from; + if (from != to || (from > 0) != (to > 0)) + return false; + return true; +} + +inline size_t BitsToBytes(size_t bitCount) +{ + return ((bitCount+7)/(8)); +} + +inline size_t BytesToWords(size_t byteCount) +{ + return ((byteCount+WORD_SIZE-1)/WORD_SIZE); +} + +inline size_t BitsToWords(size_t bitCount) +{ + return ((bitCount+WORD_BITS-1)/(WORD_BITS)); +} + +inline size_t BitsToDwords(size_t bitCount) +{ + return ((bitCount+2*WORD_BITS-1)/(2*WORD_BITS)); +} + +CRYPTOPP_DLL void CRYPTOPP_API xorbuf(byte *buf, const byte *mask, size_t count); +CRYPTOPP_DLL void CRYPTOPP_API xorbuf(byte *output, const byte *input, const byte *mask, size_t count); + +CRYPTOPP_DLL bool CRYPTOPP_API VerifyBufsEqual(const byte *buf1, const byte *buf2, size_t count); + +template +inline bool IsPowerOf2(const T &n) +{ + return n > 0 && (n & (n-1)) == 0; +} + +template +inline T2 ModPowerOf2(const T1 &a, const T2 &b) +{ + assert(IsPowerOf2(b)); + return T2(a) & (b-1); +} + +template +inline T1 RoundDownToMultipleOf(const T1 &n, const T2 &m) +{ + if (IsPowerOf2(m)) + return n - ModPowerOf2(n, m); + else + return n - n%m; +} + +template +inline T1 RoundUpToMultipleOf(const T1 &n, const T2 &m) +{ + if (n+m-1 < n) + throw InvalidArgument("RoundUpToMultipleOf: integer overflow"); + return RoundDownToMultipleOf(n+m-1, m); +} + +template +inline unsigned int GetAlignmentOf(T *dummy=NULL) // VC60 workaround +{ +#ifdef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS + if (sizeof(T) < 16) + return 1; +#endif + +#if (_MSC_VER >= 1300) + return __alignof(T); +#elif defined(__GNUC__) + return __alignof__(T); +#elif CRYPTOPP_BOOL_SLOW_WORD64 + return UnsignedMin(4U, sizeof(T)); +#else + return sizeof(T); +#endif +} + +inline bool IsAlignedOn(const void *p, unsigned int alignment) +{ + return alignment==1 || (IsPowerOf2(alignment) ? ModPowerOf2((size_t)p, alignment) == 0 : (size_t)p % alignment == 0); +} + +template +inline bool IsAligned(const void *p, T *dummy=NULL) // VC60 workaround +{ + return IsAlignedOn(p, GetAlignmentOf()); +} + +#ifdef IS_LITTLE_ENDIAN + typedef LittleEndian NativeByteOrder; +#else + typedef BigEndian NativeByteOrder; +#endif + +inline ByteOrder GetNativeByteOrder() +{ + return NativeByteOrder::ToEnum(); +} + +inline bool NativeByteOrderIs(ByteOrder order) +{ + return order == GetNativeByteOrder(); +} + +template +std::string IntToString(T a, unsigned int base = 10) +{ + if (a == 0) + return "0"; + bool negate = false; + if (a < 0) + { + negate = true; + a = 0-a; // VC .NET does not like -a + } + std::string result; + while (a > 0) + { + T digit = a % base; + result = char((digit < 10 ? '0' : ('a' - 10)) + digit) + result; + a /= base; + } + if (negate) + result = "-" + result; + return result; +} + +template +inline T1 SaturatingSubtract(const T1 &a, const T2 &b) +{ + return T1((a > b) ? (a - b) : 0); +} + +template +inline CipherDir GetCipherDir(const T &obj) +{ + return obj.IsForwardTransformation() ? ENCRYPTION : DECRYPTION; +} + +CRYPTOPP_DLL void CRYPTOPP_API CallNewHandler(); + +inline void IncrementCounterByOne(byte *inout, unsigned int s) +{ + for (int i=s-1, carry=1; i>=0 && carry; i--) + carry = !++inout[i]; +} + +inline void IncrementCounterByOne(byte *output, const byte *input, unsigned int s) +{ + int i, carry; + for (i=s-1, carry=1; i>=0 && carry; i--) + carry = ((output[i] = input[i]+1) == 0); + memcpy_s(output, s, input, i+1); +} + +// ************** rotate functions *************** + +template inline T rotlFixed(T x, unsigned int y) +{ + assert(y < sizeof(T)*8); + return T((x<>(sizeof(T)*8-y))); +} + +template inline T rotrFixed(T x, unsigned int y) +{ + assert(y < sizeof(T)*8); + return T((x>>y) | (x<<(sizeof(T)*8-y))); +} + +template inline T rotlVariable(T x, unsigned int y) +{ + assert(y < sizeof(T)*8); + return T((x<>(sizeof(T)*8-y))); +} + +template inline T rotrVariable(T x, unsigned int y) +{ + assert(y < sizeof(T)*8); + return T((x>>y) | (x<<(sizeof(T)*8-y))); +} + +template inline T rotlMod(T x, unsigned int y) +{ + y %= sizeof(T)*8; + return T((x<>(sizeof(T)*8-y))); +} + +template inline T rotrMod(T x, unsigned int y) +{ + y %= sizeof(T)*8; + return T((x>>y) | (x<<(sizeof(T)*8-y))); +} + +#ifdef _MSC_VER + +template<> inline word32 rotlFixed(word32 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _lrotl(x, y) : x; +} + +template<> inline word32 rotrFixed(word32 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _lrotr(x, y) : x; +} + +template<> inline word32 rotlVariable(word32 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _lrotl(x, y); +} + +template<> inline word32 rotrVariable(word32 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _lrotr(x, y); +} + +template<> inline word32 rotlMod(word32 x, unsigned int y) +{ + return _lrotl(x, y); +} + +template<> inline word32 rotrMod(word32 x, unsigned int y) +{ + return _lrotr(x, y); +} + +#endif // #ifdef _MSC_VER + +#if _MSC_VER >= 1300 && !defined(__INTEL_COMPILER) +// Intel C++ Compiler 10.0 calls a function instead of using the rotate instruction when using these instructions + +template<> inline word64 rotlFixed(word64 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _rotl64(x, y) : x; +} + +template<> inline word64 rotrFixed(word64 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _rotr64(x, y) : x; +} + +template<> inline word64 rotlVariable(word64 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _rotl64(x, y); +} + +template<> inline word64 rotrVariable(word64 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _rotr64(x, y); +} + +template<> inline word64 rotlMod(word64 x, unsigned int y) +{ + return _rotl64(x, y); +} + +template<> inline word64 rotrMod(word64 x, unsigned int y) +{ + return _rotr64(x, y); +} + +#endif // #if _MSC_VER >= 1310 + +#if _MSC_VER >= 1400 && !defined(__INTEL_COMPILER) +// Intel C++ Compiler 10.0 gives undefined externals with these + +template<> inline word16 rotlFixed(word16 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _rotl16(x, y) : x; +} + +template<> inline word16 rotrFixed(word16 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _rotr16(x, y) : x; +} + +template<> inline word16 rotlVariable(word16 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _rotl16(x, y); +} + +template<> inline word16 rotrVariable(word16 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _rotr16(x, y); +} + +template<> inline word16 rotlMod(word16 x, unsigned int y) +{ + return _rotl16(x, y); +} + +template<> inline word16 rotrMod(word16 x, unsigned int y) +{ + return _rotr16(x, y); +} + +template<> inline byte rotlFixed(byte x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _rotl8(x, y) : x; +} + +template<> inline byte rotrFixed(byte x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _rotr8(x, y) : x; +} + +template<> inline byte rotlVariable(byte x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _rotl8(x, y); +} + +template<> inline byte rotrVariable(byte x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _rotr8(x, y); +} + +template<> inline byte rotlMod(byte x, unsigned int y) +{ + return _rotl8(x, y); +} + +template<> inline byte rotrMod(byte x, unsigned int y) +{ + return _rotr8(x, y); +} + +#endif // #if _MSC_VER >= 1400 + +#if (defined(__MWERKS__) && TARGET_CPU_PPC) + +template<> inline word32 rotlFixed(word32 x, unsigned int y) +{ + assert(y < 32); + return y ? __rlwinm(x,y,0,31) : x; +} + +template<> inline word32 rotrFixed(word32 x, unsigned int y) +{ + assert(y < 32); + return y ? __rlwinm(x,32-y,0,31) : x; +} + +template<> inline word32 rotlVariable(word32 x, unsigned int y) +{ + assert(y < 32); + return (__rlwnm(x,y,0,31)); +} + +template<> inline word32 rotrVariable(word32 x, unsigned int y) +{ + assert(y < 32); + return (__rlwnm(x,32-y,0,31)); +} + +template<> inline word32 rotlMod(word32 x, unsigned int y) +{ + return (__rlwnm(x,y,0,31)); +} + +template<> inline word32 rotrMod(word32 x, unsigned int y) +{ + return (__rlwnm(x,32-y,0,31)); +} + +#endif // #if (defined(__MWERKS__) && TARGET_CPU_PPC) + +// ************** endian reversal *************** + +template +inline unsigned int GetByte(ByteOrder order, T value, unsigned int index) +{ + if (order == LITTLE_ENDIAN_ORDER) + return GETBYTE(value, index); + else + return GETBYTE(value, sizeof(T)-index-1); +} + +inline byte ByteReverse(byte value) +{ + return value; +} + +inline word16 ByteReverse(word16 value) +{ +#ifdef CRYPTOPP_BYTESWAP_AVAILABLE + return bswap_16(value); +#elif defined(_MSC_VER) && _MSC_VER >= 1300 + return _byteswap_ushort(value); +#else + return rotlFixed(value, 8U); +#endif +} + +inline word32 ByteReverse(word32 value) +{ +#if defined(__GNUC__) && defined(CRYPTOPP_X86_ASM_AVAILABLE) + __asm__ ("bswap %0" : "=r" (value) : "0" (value)); + return value; +#elif defined(CRYPTOPP_BYTESWAP_AVAILABLE) + return bswap_32(value); +#elif defined(__MWERKS__) && TARGET_CPU_PPC + return (word32)__lwbrx(&value,0); +#elif _MSC_VER >= 1400 || (_MSC_VER >= 1300 && !defined(_DLL)) + return _byteswap_ulong(value); +#elif CRYPTOPP_FAST_ROTATE(32) + // 5 instructions with rotate instruction, 9 without + return (rotrFixed(value, 8U) & 0xff00ff00) | (rotlFixed(value, 8U) & 0x00ff00ff); +#else + // 6 instructions with rotate instruction, 8 without + value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); + return rotlFixed(value, 16U); +#endif +} + +inline word64 ByteReverse(word64 value) +{ +#if defined(__GNUC__) && defined(CRYPTOPP_X86_ASM_AVAILABLE) && defined(__x86_64__) + __asm__ ("bswap %0" : "=r" (value) : "0" (value)); + return value; +#elif defined(CRYPTOPP_BYTESWAP_AVAILABLE) + return bswap_64(value); +#elif defined(_MSC_VER) && _MSC_VER >= 1300 + return _byteswap_uint64(value); +#elif CRYPTOPP_BOOL_SLOW_WORD64 + return (word64(ByteReverse(word32(value))) << 32) | ByteReverse(word32(value>>32)); +#else + value = ((value & W64LIT(0xFF00FF00FF00FF00)) >> 8) | ((value & W64LIT(0x00FF00FF00FF00FF)) << 8); + value = ((value & W64LIT(0xFFFF0000FFFF0000)) >> 16) | ((value & W64LIT(0x0000FFFF0000FFFF)) << 16); + return rotlFixed(value, 32U); +#endif +} + +inline byte BitReverse(byte value) +{ + value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); + value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); + return rotlFixed(value, 4U); +} + +inline word16 BitReverse(word16 value) +{ + value = ((value & 0xAAAA) >> 1) | ((value & 0x5555) << 1); + value = ((value & 0xCCCC) >> 2) | ((value & 0x3333) << 2); + value = ((value & 0xF0F0) >> 4) | ((value & 0x0F0F) << 4); + return ByteReverse(value); +} + +inline word32 BitReverse(word32 value) +{ + value = ((value & 0xAAAAAAAA) >> 1) | ((value & 0x55555555) << 1); + value = ((value & 0xCCCCCCCC) >> 2) | ((value & 0x33333333) << 2); + value = ((value & 0xF0F0F0F0) >> 4) | ((value & 0x0F0F0F0F) << 4); + return ByteReverse(value); +} + +inline word64 BitReverse(word64 value) +{ +#if CRYPTOPP_BOOL_SLOW_WORD64 + return (word64(BitReverse(word32(value))) << 32) | BitReverse(word32(value>>32)); +#else + value = ((value & W64LIT(0xAAAAAAAAAAAAAAAA)) >> 1) | ((value & W64LIT(0x5555555555555555)) << 1); + value = ((value & W64LIT(0xCCCCCCCCCCCCCCCC)) >> 2) | ((value & W64LIT(0x3333333333333333)) << 2); + value = ((value & W64LIT(0xF0F0F0F0F0F0F0F0)) >> 4) | ((value & W64LIT(0x0F0F0F0F0F0F0F0F)) << 4); + return ByteReverse(value); +#endif +} + +template +inline T BitReverse(T value) +{ + if (sizeof(T) == 1) + return (T)BitReverse((byte)value); + else if (sizeof(T) == 2) + return (T)BitReverse((word16)value); + else if (sizeof(T) == 4) + return (T)BitReverse((word32)value); + else + { + assert(sizeof(T) == 8); + return (T)BitReverse((word64)value); + } +} + +template +inline T ConditionalByteReverse(ByteOrder order, T value) +{ + return NativeByteOrderIs(order) ? value : ByteReverse(value); +} + +template +void ByteReverse(T *out, const T *in, size_t byteCount) +{ + assert(byteCount % sizeof(T) == 0); + size_t count = byteCount/sizeof(T); + for (size_t i=0; i +inline void ConditionalByteReverse(ByteOrder order, T *out, const T *in, size_t byteCount) +{ + if (!NativeByteOrderIs(order)) + ByteReverse(out, in, byteCount); + else if (in != out) + memcpy_s(out, byteCount, in, byteCount); +} + +template +inline void GetUserKey(ByteOrder order, T *out, size_t outlen, const byte *in, size_t inlen) +{ + const size_t U = sizeof(T); + assert(inlen <= outlen*U); + memcpy_s(out, outlen*U, in, inlen); + memset_z((byte *)out+inlen, 0, outlen*U-inlen); + ConditionalByteReverse(order, out, out, RoundUpToMultipleOf(inlen, U)); +} + +#ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS +inline byte UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const byte *) +{ + return block[0]; +} + +inline word16 UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const word16 *) +{ + return (order == BIG_ENDIAN_ORDER) + ? block[1] | (block[0] << 8) + : block[0] | (block[1] << 8); +} + +inline word32 UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const word32 *) +{ + return (order == BIG_ENDIAN_ORDER) + ? word32(block[3]) | (word32(block[2]) << 8) | (word32(block[1]) << 16) | (word32(block[0]) << 24) + : word32(block[0]) | (word32(block[1]) << 8) | (word32(block[2]) << 16) | (word32(block[3]) << 24); +} + +inline word64 UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const word64 *) +{ + return (order == BIG_ENDIAN_ORDER) + ? + (word64(block[7]) | + (word64(block[6]) << 8) | + (word64(block[5]) << 16) | + (word64(block[4]) << 24) | + (word64(block[3]) << 32) | + (word64(block[2]) << 40) | + (word64(block[1]) << 48) | + (word64(block[0]) << 56)) + : + (word64(block[0]) | + (word64(block[1]) << 8) | + (word64(block[2]) << 16) | + (word64(block[3]) << 24) | + (word64(block[4]) << 32) | + (word64(block[5]) << 40) | + (word64(block[6]) << 48) | + (word64(block[7]) << 56)); +} + +inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, byte value, const byte *xorBlock) +{ + block[0] = xorBlock ? (value ^ xorBlock[0]) : value; +} + +inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, word16 value, const byte *xorBlock) +{ + if (order == BIG_ENDIAN_ORDER) + { + if (xorBlock) + { + block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + } + else + { + block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + } + } + else + { + if (xorBlock) + { + block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + } + else + { + block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + } + } +} + +inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, word32 value, const byte *xorBlock) +{ + if (order == BIG_ENDIAN_ORDER) + { + if (xorBlock) + { + block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + } + else + { + block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + } + } + else + { + if (xorBlock) + { + block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + } + else + { + block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + } + } +} + +inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, word64 value, const byte *xorBlock) +{ + if (order == BIG_ENDIAN_ORDER) + { + if (xorBlock) + { + block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); + block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); + block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); + block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); + block[4] = xorBlock[4] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + block[5] = xorBlock[5] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[6] = xorBlock[6] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[7] = xorBlock[7] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + } + else + { + block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); + block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); + block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); + block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); + block[4] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + block[5] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[6] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[7] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + } + } + else + { + if (xorBlock) + { + block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + block[4] = xorBlock[4] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); + block[5] = xorBlock[5] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); + block[6] = xorBlock[6] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); + block[7] = xorBlock[7] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); + } + else + { + block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + block[4] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); + block[5] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); + block[6] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); + block[7] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); + } + } +} +#endif // #ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS + +template +inline T GetWord(bool assumeAligned, ByteOrder order, const byte *block) +{ +#ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS + if (!assumeAligned) + return UnalignedGetWordNonTemplate(order, block, (T*)NULL); + assert(IsAligned(block)); +#endif + return ConditionalByteReverse(order, *reinterpret_cast(block)); +} + +template +inline void GetWord(bool assumeAligned, ByteOrder order, T &result, const byte *block) +{ + result = GetWord(assumeAligned, order, block); +} + +template +inline void PutWord(bool assumeAligned, ByteOrder order, byte *block, T value, const byte *xorBlock = NULL) +{ +#ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS + if (!assumeAligned) + return UnalignedPutWordNonTemplate(order, block, value, xorBlock); + assert(IsAligned(block)); + assert(IsAligned(xorBlock)); +#endif + *reinterpret_cast(block) = ConditionalByteReverse(order, value) ^ (xorBlock ? *reinterpret_cast(xorBlock) : 0); +} + +template +class GetBlock +{ +public: + GetBlock(const void *block) + : m_block((const byte *)block) {} + + template + inline GetBlock & operator()(U &x) + { + CRYPTOPP_COMPILE_ASSERT(sizeof(U) >= sizeof(T)); + x = GetWord(A, B::ToEnum(), m_block); + m_block += sizeof(T); + return *this; + } + +private: + const byte *m_block; +}; + +template +class PutBlock +{ +public: + PutBlock(const void *xorBlock, void *block) + : m_xorBlock((const byte *)xorBlock), m_block((byte *)block) {} + + template + inline PutBlock & operator()(U x) + { + PutWord(A, B::ToEnum(), m_block, (T)x, m_xorBlock); + m_block += sizeof(T); + if (m_xorBlock) + m_xorBlock += sizeof(T); + return *this; + } + +private: + const byte *m_xorBlock; + byte *m_block; +}; + +template +struct BlockGetAndPut +{ + // function needed because of C++ grammatical ambiguity between expression-statements and declarations + static inline GetBlock Get(const void *block) {return GetBlock(block);} + typedef PutBlock Put; +}; + +template +std::string WordToString(T value, ByteOrder order = BIG_ENDIAN_ORDER) +{ + if (!NativeByteOrderIs(order)) + value = ByteReverse(value); + + return std::string((char *)&value, sizeof(value)); +} + +template +T StringToWord(const std::string &str, ByteOrder order = BIG_ENDIAN_ORDER) +{ + T value = 0; + memcpy_s(&value, sizeof(value), str.data(), UnsignedMin(str.size(), sizeof(value))); + return NativeByteOrderIs(order) ? value : ByteReverse(value); +} + +// ************** help remove warning on g++ *************** + +template struct SafeShifter; + +template<> struct SafeShifter +{ + template + static inline T RightShift(T value, unsigned int bits) + { + return 0; + } + + template + static inline T LeftShift(T value, unsigned int bits) + { + return 0; + } +}; + +template<> struct SafeShifter +{ + template + static inline T RightShift(T value, unsigned int bits) + { + return value >> bits; + } + + template + static inline T LeftShift(T value, unsigned int bits) + { + return value << bits; + } +}; + +template +inline T SafeRightShift(T value) +{ + return SafeShifter<(bits>=(8*sizeof(T)))>::RightShift(value, bits); +} + +template +inline T SafeLeftShift(T value) +{ + return SafeShifter<(bits>=(8*sizeof(T)))>::LeftShift(value, bits); +} + +// ************** use one buffer for multiple data members *************** + +#define CRYPTOPP_BLOCK_1(n, t, s) t* m_##n() {return (t *)(m_aggregate+0);} size_t SS1() {return sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_2(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS1());} size_t SS2() {return SS1()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_3(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS2());} size_t SS3() {return SS2()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_4(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS3());} size_t SS4() {return SS3()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_5(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS4());} size_t SS5() {return SS4()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_6(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS5());} size_t SS6() {return SS5()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_7(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS6());} size_t SS7() {return SS6()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_8(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS7());} size_t SS8() {return SS7()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCKS_END(i) size_t SST() {return SS##i();} void AllocateBlocks() {m_aggregate.New(SST());} AlignedSecByteBlock m_aggregate; + +NAMESPACE_END + +#endif diff --git a/cryptopp/include/cryptopp/pch.h b/cryptopp/include/cryptopp/pch.h new file mode 100644 index 0000000000..418c39076d --- /dev/null +++ b/cryptopp/include/cryptopp/pch.h @@ -0,0 +1,21 @@ +#ifndef CRYPTOPP_PCH_H +#define CRYPTOPP_PCH_H + +#ifdef CRYPTOPP_GENERATE_X64_MASM + + #include "cpu.h" + +#else + + #include "config.h" + + #ifdef USE_PRECOMPILED_HEADERS + #include "simple.h" + #include "secblock.h" + #include "misc.h" + #include "smartptr.h" + #endif + +#endif + +#endif diff --git a/cryptopp/include/cryptopp/secblock.h b/cryptopp/include/cryptopp/secblock.h new file mode 100644 index 0000000000..6433f97349 --- /dev/null +++ b/cryptopp/include/cryptopp/secblock.h @@ -0,0 +1,501 @@ +// secblock.h - written and placed in the public domain by Wei Dai + +#ifndef CRYPTOPP_SECBLOCK_H +#define CRYPTOPP_SECBLOCK_H + +#include "cryptopp/config.h" +#include "cryptopp/misc.h" +#include + +#if defined(CRYPTOPP_MEMALIGN_AVAILABLE) || defined(CRYPTOPP_MM_MALLOC_AVAILABLE) || defined(QNX) + #include +#else + #include +#endif + +NAMESPACE_BEGIN(CryptoPP) + +// ************** secure memory allocation *************** + +template +class AllocatorBase +{ +public: + typedef T value_type; + typedef size_t size_type; +#ifdef CRYPTOPP_MSVCRT6 + typedef ptrdiff_t difference_type; +#else + typedef std::ptrdiff_t difference_type; +#endif + typedef T * pointer; + typedef const T * const_pointer; + typedef T & reference; + typedef const T & const_reference; + + pointer address(reference r) const {return (&r);} + const_pointer address(const_reference r) const {return (&r); } + void construct(pointer p, const T& val) {new (p) T(val);} + void destroy(pointer p) {p->~T();} + size_type max_size() const {return ~size_type(0)/sizeof(T);} // switch to std::numeric_limits::max later + +protected: + static void CheckSize(size_t n) + { + if (n > ~size_t(0) / sizeof(T)) + throw InvalidArgument("AllocatorBase: requested size would cause integer overflow"); + } +}; + +#define CRYPTOPP_INHERIT_ALLOCATOR_TYPES \ +typedef typename AllocatorBase::value_type value_type;\ +typedef typename AllocatorBase::size_type size_type;\ +typedef typename AllocatorBase::difference_type difference_type;\ +typedef typename AllocatorBase::pointer pointer;\ +typedef typename AllocatorBase::const_pointer const_pointer;\ +typedef typename AllocatorBase::reference reference;\ +typedef typename AllocatorBase::const_reference const_reference; + +#if defined(_MSC_VER) && (_MSC_VER < 1300) +// this pragma causes an internal compiler error if placed immediately before std::swap(a, b) +#pragma warning(push) +#pragma warning(disable: 4700) // VC60 workaround: don't know how to get rid of this warning +#endif + +template +typename A::pointer StandardReallocate(A& a, T *p, typename A::size_type oldSize, typename A::size_type newSize, bool preserve) +{ + if (oldSize == newSize) + return p; + + if (preserve) + { + typename A::pointer newPointer = a.allocate(newSize, NULL); + memcpy_s(newPointer, sizeof(T)*newSize, p, sizeof(T)*STDMIN(oldSize, newSize)); + a.deallocate(p, oldSize); + return newPointer; + } + else + { + a.deallocate(p, oldSize); + return a.allocate(newSize, NULL); + } +} + +#if defined(_MSC_VER) && (_MSC_VER < 1300) +#pragma warning(pop) +#endif + +template +class AllocatorWithCleanup : public AllocatorBase +{ +public: + CRYPTOPP_INHERIT_ALLOCATOR_TYPES + + pointer allocate(size_type n, const void * = NULL) + { + CheckSize(n); + if (n == 0) + return NULL; + + if (CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16 && n*sizeof(T) >= 16) + { + byte *p; + #ifdef CRYPTOPP_MM_MALLOC_AVAILABLE + while (!(p = (byte *)_mm_malloc(sizeof(T)*n, 16))) + #elif defined(CRYPTOPP_MEMALIGN_AVAILABLE) + while (!(p = (byte *)memalign(16, sizeof(T)*n))) + #elif defined(CRYPTOPP_MALLOC_ALIGNMENT_IS_16) + while (!(p = (byte *)malloc(sizeof(T)*n))) + #else + while (!(p = (byte *)malloc(sizeof(T)*n + 16))) + #endif + CallNewHandler(); + + #ifdef CRYPTOPP_NO_ALIGNED_ALLOC + size_t adjustment = 16-((size_t)p%16); + p += adjustment; + p[-1] = (byte)adjustment; + #endif + + assert(IsAlignedOn(p, 16)); + return (pointer)p; + } + + pointer p; + while (!(p = (pointer)malloc(sizeof(T)*n))) + CallNewHandler(); + return p; + } + + void deallocate(void *p, size_type n) + { + memset_z(p, 0, n*sizeof(T)); + + if (CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16 && n*sizeof(T) >= 16) + { + #ifdef CRYPTOPP_MM_MALLOC_AVAILABLE + _mm_free(p); + #elif defined(CRYPTOPP_NO_ALIGNED_ALLOC) + p = (byte *)p - ((byte *)p)[-1]; + free(p); + #else + free(p); + #endif + return; + } + + free(p); + } + + pointer reallocate(T *p, size_type oldSize, size_type newSize, bool preserve) + { + return StandardReallocate(*this, p, oldSize, newSize, preserve); + } + + // VS.NET STL enforces the policy of "All STL-compliant allocators have to provide a + // template class member called rebind". + template struct rebind { typedef AllocatorWithCleanup other; }; +#if _MSC_VER >= 1500 + AllocatorWithCleanup() {} + template AllocatorWithCleanup(const AllocatorWithCleanup &) {} +#endif +}; + +CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; +CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; +CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; +CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; +#if CRYPTOPP_BOOL_X86 +CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; // for Integer +#endif + +template +class NullAllocator : public AllocatorBase +{ +public: + CRYPTOPP_INHERIT_ALLOCATOR_TYPES + + pointer allocate(size_type n, const void * = NULL) + { + assert(false); + return NULL; + } + + void deallocate(void *p, size_type n) + { + //// Bitcoin: don't know why this trips, probably a false alarm, depends on the compiler used. + //assert(false); + } + + size_type max_size() const {return 0;} +}; + +// This allocator can't be used with standard collections because +// they require that all objects of the same allocator type are equivalent. +// So this is for use with SecBlock only. +template , bool T_Align16 = false> +class FixedSizeAllocatorWithCleanup : public AllocatorBase +{ +public: + CRYPTOPP_INHERIT_ALLOCATOR_TYPES + + FixedSizeAllocatorWithCleanup() : m_allocated(false) {} + + pointer allocate(size_type n) + { + assert(IsAlignedOn(m_array, 8)); + + if (n <= S && !m_allocated) + { + m_allocated = true; + return GetAlignedArray(); + } + else + return m_fallbackAllocator.allocate(n); + } + + pointer allocate(size_type n, const void *hint) + { + if (n <= S && !m_allocated) + { + m_allocated = true; + return GetAlignedArray(); + } + else + return m_fallbackAllocator.allocate(n, hint); + } + + void deallocate(void *p, size_type n) + { + if (p == GetAlignedArray()) + { + assert(n <= S); + assert(m_allocated); + m_allocated = false; + memset(p, 0, n*sizeof(T)); + } + else + m_fallbackAllocator.deallocate(p, n); + } + + pointer reallocate(pointer p, size_type oldSize, size_type newSize, bool preserve) + { + if (p == GetAlignedArray() && newSize <= S) + { + assert(oldSize <= S); + if (oldSize > newSize) + memset(p + newSize, 0, (oldSize-newSize)*sizeof(T)); + return p; + } + + pointer newPointer = allocate(newSize, NULL); + if (preserve) + memcpy(newPointer, p, sizeof(T)*STDMIN(oldSize, newSize)); + deallocate(p, oldSize); + return newPointer; + } + + size_type max_size() const {return STDMAX(m_fallbackAllocator.max_size(), S);} + +private: +#ifdef __BORLANDC__ + T* GetAlignedArray() {return m_array;} + T m_array[S]; +#else + T* GetAlignedArray() {return (CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16) ? (T*)(((byte *)m_array) + (0-(size_t)m_array)%16) : m_array;} + CRYPTOPP_ALIGN_DATA(8) T m_array[(CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16) ? S+8/sizeof(T) : S]; +#endif + A m_fallbackAllocator; + bool m_allocated; +}; + +//! a block of memory allocated using A +template > +class SecBlock +{ +public: + typedef typename A::value_type value_type; + typedef typename A::pointer iterator; + typedef typename A::const_pointer const_iterator; + typedef typename A::size_type size_type; + + explicit SecBlock(size_type size=0) + : m_size(size) {m_ptr = m_alloc.allocate(size, NULL);} + SecBlock(const SecBlock &t) + : m_size(t.m_size) {m_ptr = m_alloc.allocate(m_size, NULL); memcpy_s(m_ptr, m_size*sizeof(T), t.m_ptr, m_size*sizeof(T));} + SecBlock(const T *t, size_type len) + : m_size(len) + { + m_ptr = m_alloc.allocate(len, NULL); + if (t == NULL) + memset_z(m_ptr, 0, len*sizeof(T)); + else + memcpy(m_ptr, t, len*sizeof(T)); + } + + ~SecBlock() + {m_alloc.deallocate(m_ptr, m_size);} + +#ifdef __BORLANDC__ + operator T *() const + {return (T*)m_ptr;} +#else + operator const void *() const + {return m_ptr;} + operator void *() + {return m_ptr;} + + operator const T *() const + {return m_ptr;} + operator T *() + {return m_ptr;} +#endif + +// T *operator +(size_type offset) +// {return m_ptr+offset;} + +// const T *operator +(size_type offset) const +// {return m_ptr+offset;} + +// T& operator[](size_type index) +// {assert(index >= 0 && index < m_size); return m_ptr[index];} + +// const T& operator[](size_type index) const +// {assert(index >= 0 && index < m_size); return m_ptr[index];} + + iterator begin() + {return m_ptr;} + const_iterator begin() const + {return m_ptr;} + iterator end() + {return m_ptr+m_size;} + const_iterator end() const + {return m_ptr+m_size;} + + typename A::pointer data() {return m_ptr;} + typename A::const_pointer data() const {return m_ptr;} + + size_type size() const {return m_size;} + bool empty() const {return m_size == 0;} + + byte * BytePtr() {return (byte *)m_ptr;} + const byte * BytePtr() const {return (const byte *)m_ptr;} + size_type SizeInBytes() const {return m_size*sizeof(T);} + + //! set contents and size + void Assign(const T *t, size_type len) + { + New(len); + memcpy_s(m_ptr, m_size*sizeof(T), t, len*sizeof(T)); + } + + //! copy contents and size from another SecBlock + void Assign(const SecBlock &t) + { + New(t.m_size); + memcpy_s(m_ptr, m_size*sizeof(T), t.m_ptr, m_size*sizeof(T)); + } + + SecBlock& operator=(const SecBlock &t) + { + Assign(t); + return *this; + } + + // append to this object + SecBlock& operator+=(const SecBlock &t) + { + size_type oldSize = m_size; + Grow(m_size+t.m_size); + memcpy_s(m_ptr+oldSize, m_size*sizeof(T), t.m_ptr, t.m_size*sizeof(T)); + return *this; + } + + // append operator + SecBlock operator+(const SecBlock &t) + { + SecBlock result(m_size+t.m_size); + memcpy_s(result.m_ptr, result.m_size*sizeof(T), m_ptr, m_size*sizeof(T)); + memcpy_s(result.m_ptr+m_size, t.m_size*sizeof(T), t.m_ptr, t.m_size*sizeof(T)); + return result; + } + + bool operator==(const SecBlock &t) const + { + return m_size == t.m_size && VerifyBufsEqual(m_ptr, t.m_ptr, m_size*sizeof(T)); + } + + bool operator!=(const SecBlock &t) const + { + return !operator==(t); + } + + //! change size, without preserving contents + void New(size_type newSize) + { + m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, false); + m_size = newSize; + } + + //! change size and set contents to 0 + void CleanNew(size_type newSize) + { + New(newSize); + memset_z(m_ptr, 0, m_size*sizeof(T)); + } + + //! change size only if newSize > current size. contents are preserved + void Grow(size_type newSize) + { + if (newSize > m_size) + { + m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); + m_size = newSize; + } + } + + //! change size only if newSize > current size. contents are preserved and additional area is set to 0 + void CleanGrow(size_type newSize) + { + if (newSize > m_size) + { + m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); + memset(m_ptr+m_size, 0, (newSize-m_size)*sizeof(T)); + m_size = newSize; + } + } + + //! change size and preserve contents + void resize(size_type newSize) + { + m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); + m_size = newSize; + } + + //! swap contents and size with another SecBlock + void swap(SecBlock &b) + { + std::swap(m_alloc, b.m_alloc); + std::swap(m_size, b.m_size); + std::swap(m_ptr, b.m_ptr); + } + +//private: + A m_alloc; + size_type m_size; + T *m_ptr; +}; + +typedef SecBlock SecByteBlock; +typedef SecBlock > AlignedSecByteBlock; +typedef SecBlock SecWordBlock; + +//! a SecBlock with fixed size, allocated statically +template > +class FixedSizeSecBlock : public SecBlock +{ +public: + explicit FixedSizeSecBlock() : SecBlock(S) {} +}; + +template +class FixedSizeAlignedSecBlock : public FixedSizeSecBlock, T_Align16> > +{ +}; + +//! a SecBlock that preallocates size S statically, and uses the heap when this size is exceeded +template > > +class SecBlockWithHint : public SecBlock +{ +public: + explicit SecBlockWithHint(size_t size) : SecBlock(size) {} +}; + +template +inline bool operator==(const CryptoPP::AllocatorWithCleanup&, const CryptoPP::AllocatorWithCleanup&) {return (true);} +template +inline bool operator!=(const CryptoPP::AllocatorWithCleanup&, const CryptoPP::AllocatorWithCleanup&) {return (false);} + +NAMESPACE_END + +NAMESPACE_BEGIN(std) +template +inline void swap(CryptoPP::SecBlock &a, CryptoPP::SecBlock &b) +{ + a.swap(b); +} + +#if defined(_STLP_DONT_SUPPORT_REBIND_MEMBER_TEMPLATE) || (defined(_STLPORT_VERSION) && !defined(_STLP_MEMBER_TEMPLATE_CLASSES)) +// working for STLport 5.1.3 and MSVC 6 SP5 +template +inline CryptoPP::AllocatorWithCleanup<_Tp2>& +__stl_alloc_rebind(CryptoPP::AllocatorWithCleanup<_Tp1>& __a, const _Tp2*) +{ + return (CryptoPP::AllocatorWithCleanup<_Tp2>&)(__a); +} +#endif + +NAMESPACE_END + +#endif diff --git a/cryptopp/include/cryptopp/sha.h b/cryptopp/include/cryptopp/sha.h new file mode 100644 index 0000000000..8d0dbfcac7 --- /dev/null +++ b/cryptopp/include/cryptopp/sha.h @@ -0,0 +1,63 @@ +#ifndef CRYPTOPP_SHA_H +#define CRYPTOPP_SHA_H + +#include "cryptopp/iterhash.h" + +NAMESPACE_BEGIN(CryptoPP) + +/// SHA-1 +class CRYPTOPP_DLL SHA1 : public IteratedHashWithStaticTransform +{ +public: + static void CRYPTOPP_API InitState(HashWordType *state); + static void CRYPTOPP_API Transform(word32 *digest, const word32 *data); + static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-1";} +}; + +typedef SHA1 SHA; // for backwards compatibility + +//! implements the SHA-256 standard +class CRYPTOPP_DLL SHA256 : public IteratedHashWithStaticTransform +{ +public: +#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) + size_t HashMultipleBlocks(const word32 *input, size_t length); +#endif + static void CRYPTOPP_API InitState(HashWordType *state); + static void CRYPTOPP_API Transform(word32 *digest, const word32 *data); + static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-256";} +}; + +//! implements the SHA-224 standard +class CRYPTOPP_DLL SHA224 : public IteratedHashWithStaticTransform +{ +public: +#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) + size_t HashMultipleBlocks(const word32 *input, size_t length); +#endif + static void CRYPTOPP_API InitState(HashWordType *state); + static void CRYPTOPP_API Transform(word32 *digest, const word32 *data) {SHA256::Transform(digest, data);} + static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-224";} +}; + +//! implements the SHA-512 standard +class CRYPTOPP_DLL SHA512 : public IteratedHashWithStaticTransform +{ +public: + static void CRYPTOPP_API InitState(HashWordType *state); + static void CRYPTOPP_API Transform(word64 *digest, const word64 *data); + static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-512";} +}; + +//! implements the SHA-384 standard +class CRYPTOPP_DLL SHA384 : public IteratedHashWithStaticTransform +{ +public: + static void CRYPTOPP_API InitState(HashWordType *state); + static void CRYPTOPP_API Transform(word64 *digest, const word64 *data) {SHA512::Transform(digest, data);} + static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-384";} +}; + +NAMESPACE_END + +#endif diff --git a/cryptopp/include/cryptopp/simple.h b/cryptopp/include/cryptopp/simple.h new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/cryptopp/include/cryptopp/simple.h @@ -0,0 +1 @@ + diff --git a/cryptopp/include/cryptopp/smartptr.h b/cryptopp/include/cryptopp/smartptr.h new file mode 100644 index 0000000000..fa0daec3df --- /dev/null +++ b/cryptopp/include/cryptopp/smartptr.h @@ -0,0 +1,223 @@ +#ifndef CRYPTOPP_SMARTPTR_H +#define CRYPTOPP_SMARTPTR_H + +#include "cryptopp/config.h" +#include + +NAMESPACE_BEGIN(CryptoPP) + +template class simple_ptr +{ +public: + simple_ptr() : m_p(NULL) {} + ~simple_ptr() {delete m_p;} + T *m_p; +}; + +template class member_ptr +{ +public: + explicit member_ptr(T *p = NULL) : m_p(p) {} + + ~member_ptr(); + + const T& operator*() const { return *m_p; } + T& operator*() { return *m_p; } + + const T* operator->() const { return m_p; } + T* operator->() { return m_p; } + + const T* get() const { return m_p; } + T* get() { return m_p; } + + T* release() + { + T *old_p = m_p; + m_p = 0; + return old_p; + } + + void reset(T *p = 0); + +protected: + member_ptr(const member_ptr& rhs); // copy not allowed + void operator=(const member_ptr& rhs); // assignment not allowed + + T *m_p; +}; + +template member_ptr::~member_ptr() {delete m_p;} +template void member_ptr::reset(T *p) {delete m_p; m_p = p;} + +// ******************************************************** + +template class value_ptr : public member_ptr +{ +public: + value_ptr(const T &obj) : member_ptr(new T(obj)) {} + value_ptr(T *p = NULL) : member_ptr(p) {} + value_ptr(const value_ptr& rhs) + : member_ptr(rhs.m_p ? new T(*rhs.m_p) : NULL) {} + + value_ptr& operator=(const value_ptr& rhs); + bool operator==(const value_ptr& rhs) + { + return (!this->m_p && !rhs.m_p) || (this->m_p && rhs.m_p && *this->m_p == *rhs.m_p); + } +}; + +template value_ptr& value_ptr::operator=(const value_ptr& rhs) +{ + T *old_p = this->m_p; + this->m_p = rhs.m_p ? new T(*rhs.m_p) : NULL; + delete old_p; + return *this; +} + +// ******************************************************** + +template class clonable_ptr : public member_ptr +{ +public: + clonable_ptr(const T &obj) : member_ptr(obj.Clone()) {} + clonable_ptr(T *p = NULL) : member_ptr(p) {} + clonable_ptr(const clonable_ptr& rhs) + : member_ptr(rhs.m_p ? rhs.m_p->Clone() : NULL) {} + + clonable_ptr& operator=(const clonable_ptr& rhs); +}; + +template clonable_ptr& clonable_ptr::operator=(const clonable_ptr& rhs) +{ + T *old_p = this->m_p; + this->m_p = rhs.m_p ? rhs.m_p->Clone() : NULL; + delete old_p; + return *this; +} + +// ******************************************************** + +template class counted_ptr +{ +public: + explicit counted_ptr(T *p = 0); + counted_ptr(const T &r) : m_p(0) {attach(r);} + counted_ptr(const counted_ptr& rhs); + + ~counted_ptr(); + + const T& operator*() const { return *m_p; } + T& operator*() { return *m_p; } + + const T* operator->() const { return m_p; } + T* operator->() { return get(); } + + const T* get() const { return m_p; } + T* get(); + + void attach(const T &p); + + counted_ptr & operator=(const counted_ptr& rhs); + +private: + T *m_p; +}; + +template counted_ptr::counted_ptr(T *p) + : m_p(p) +{ + if (m_p) + m_p->m_referenceCount = 1; +} + +template counted_ptr::counted_ptr(const counted_ptr& rhs) + : m_p(rhs.m_p) +{ + if (m_p) + m_p->m_referenceCount++; +} + +template counted_ptr::~counted_ptr() +{ + if (m_p && --m_p->m_referenceCount == 0) + delete m_p; +} + +template void counted_ptr::attach(const T &r) +{ + if (m_p && --m_p->m_referenceCount == 0) + delete m_p; + if (r.m_referenceCount == 0) + { + m_p = r.clone(); + m_p->m_referenceCount = 1; + } + else + { + m_p = const_cast(&r); + m_p->m_referenceCount++; + } +} + +template T* counted_ptr::get() +{ + if (m_p && m_p->m_referenceCount > 1) + { + T *temp = m_p->clone(); + m_p->m_referenceCount--; + m_p = temp; + m_p->m_referenceCount = 1; + } + return m_p; +} + +template counted_ptr & counted_ptr::operator=(const counted_ptr& rhs) +{ + if (m_p != rhs.m_p) + { + if (m_p && --m_p->m_referenceCount == 0) + delete m_p; + m_p = rhs.m_p; + if (m_p) + m_p->m_referenceCount++; + } + return *this; +} + +// ******************************************************** + +template class vector_member_ptrs +{ +public: + vector_member_ptrs(size_t size=0) + : m_size(size), m_ptr(new member_ptr[size]) {} + ~vector_member_ptrs() + {delete [] this->m_ptr;} + + member_ptr& operator[](size_t index) + {assert(indexm_size); return this->m_ptr[index];} + const member_ptr& operator[](size_t index) const + {assert(indexm_size); return this->m_ptr[index];} + + size_t size() const {return this->m_size;} + void resize(size_t newSize) + { + member_ptr *newPtr = new member_ptr[newSize]; + for (size_t i=0; im_size && im_ptr[i].release()); + delete [] this->m_ptr; + this->m_size = newSize; + this->m_ptr = newPtr; + } + +private: + vector_member_ptrs(const vector_member_ptrs &c); // copy not allowed + void operator=(const vector_member_ptrs &x); // assignment not allowed + + size_t m_size; + member_ptr *m_ptr; +}; + +NAMESPACE_END + +#endif diff --git a/cryptopp/include/cryptopp/stdcpp.h b/cryptopp/include/cryptopp/stdcpp.h new file mode 100644 index 0000000000..9a468ab61e --- /dev/null +++ b/cryptopp/include/cryptopp/stdcpp.h @@ -0,0 +1,27 @@ +#ifndef CRYPTOPP_STDCPP_H +#define CRYPTOPP_STDCPP_H + +#include +#include +#include +#include +#include +#include +#include + + +#ifdef _MSC_VER +#include // CodeWarrior doesn't have memory.h +#include +#include +#include + +// re-disable this +#pragma warning(disable: 4231) +#endif + +#if defined(_MSC_VER) && defined(_CRTAPI1) +#define CRYPTOPP_MSVCRT6 +#endif + +#endif diff --git a/cryptopp/src/cpu.cpp b/cryptopp/src/cpu.cpp new file mode 100644 index 0000000000..d36d3253ae --- /dev/null +++ b/cryptopp/src/cpu.cpp @@ -0,0 +1,199 @@ +// cpu.cpp - written and placed in the public domain by Wei Dai + +#include "cryptopp/pch.h" + +#ifndef CRYPTOPP_IMPORTS + +#include "cryptopp/cpu.h" +#include "cryptopp/misc.h" +#include + +#ifdef __GNUC__ +#include +#include +#endif + +#ifdef CRYPTOPP_MSVC6PP_OR_LATER +#include +#endif + +NAMESPACE_BEGIN(CryptoPP) + +#ifdef CRYPTOPP_X86_ASM_AVAILABLE + +#ifndef _MSC_VER +typedef void (*SigHandler)(int); + +static jmp_buf s_jmpNoCPUID; +static void SigIllHandlerCPUID(int) +{ + longjmp(s_jmpNoCPUID, 1); +} +#endif + +bool CpuId(word32 input, word32 *output) +{ +#ifdef _MSC_VER + __try + { + __asm + { + mov eax, input + cpuid + mov edi, output + mov [edi], eax + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], edx + } + } + __except (1) + { + return false; + } + return true; +#else + SigHandler oldHandler = signal(SIGILL, SigIllHandlerCPUID); + if (oldHandler == SIG_ERR) + return false; + + bool result = true; + if (setjmp(s_jmpNoCPUID)) + result = false; + else + { + __asm__ + ( + // save ebx in case -fPIC is being used +#if CRYPTOPP_BOOL_X86 + "push %%ebx; cpuid; mov %%ebx, %%edi; pop %%ebx" +#else + "pushq %%rbx; cpuid; mov %%ebx, %%edi; popq %%rbx" +#endif + : "=a" (output[0]), "=D" (output[1]), "=c" (output[2]), "=d" (output[3]) + : "a" (input) + ); + } + + signal(SIGILL, oldHandler); + return result; +#endif +} + +#ifndef _MSC_VER +static jmp_buf s_jmpNoSSE2; +static void SigIllHandlerSSE2(int) +{ + longjmp(s_jmpNoSSE2, 1); +} +#endif + +#elif _MSC_VER >= 1400 && CRYPTOPP_BOOL_X64 + +bool CpuId(word32 input, word32 *output) +{ + __cpuid((int *)output, input); + return true; +} + +#endif + +#ifdef CRYPTOPP_CPUID_AVAILABLE + +static bool TrySSE2() +{ +#if CRYPTOPP_BOOL_X64 + return true; +#elif defined(_MSC_VER) + __try + { +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + AS2(por xmm0, xmm0) // executing SSE2 instruction +#elif CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE + __mm128i x = _mm_setzero_si128(); + return _mm_cvtsi128_si32(x) == 0; +#endif + } + __except (1) + { + return false; + } + return true; +#elif defined(__GNUC__) + SigHandler oldHandler = signal(SIGILL, SigIllHandlerSSE2); + if (oldHandler == SIG_ERR) + return false; + + bool result = true; + if (setjmp(s_jmpNoSSE2)) + result = false; + else + { +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + __asm __volatile ("por %xmm0, %xmm0"); +#elif CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE + __mm128i x = _mm_setzero_si128(); + result = _mm_cvtsi128_si32(x) == 0; +#endif + } + + signal(SIGILL, oldHandler); + return result; +#else + return false; +#endif +} + +bool g_x86DetectionDone = false; +bool g_hasISSE = false, g_hasSSE2 = false, g_hasSSSE3 = false, g_hasMMX = false, g_isP4 = false; +word32 g_cacheLineSize = CRYPTOPP_L1_CACHE_LINE_SIZE; + +void DetectX86Features() +{ + word32 cpuid[4], cpuid1[4]; + if (!CpuId(0, cpuid)) + return; + if (!CpuId(1, cpuid1)) + return; + + g_hasMMX = (cpuid1[3] & (1 << 23)) != 0; + if ((cpuid1[3] & (1 << 26)) != 0) + g_hasSSE2 = TrySSE2(); + g_hasSSSE3 = g_hasSSE2 && (cpuid1[2] & (1<<9)); + + if ((cpuid1[3] & (1 << 25)) != 0) + g_hasISSE = true; + else + { + word32 cpuid2[4]; + CpuId(0x080000000, cpuid2); + if (cpuid2[0] >= 0x080000001) + { + CpuId(0x080000001, cpuid2); + g_hasISSE = (cpuid2[3] & (1 << 22)) != 0; + } + } + + std::swap(cpuid[2], cpuid[3]); + if (memcmp(cpuid+1, "GenuineIntel", 12) == 0) + { + g_isP4 = ((cpuid1[0] >> 8) & 0xf) == 0xf; + g_cacheLineSize = 8 * GETBYTE(cpuid1[1], 1); + } + else if (memcmp(cpuid+1, "AuthenticAMD", 12) == 0) + { + CpuId(0x80000005, cpuid); + g_cacheLineSize = GETBYTE(cpuid[2], 0); + } + + if (!g_cacheLineSize) + g_cacheLineSize = CRYPTOPP_L1_CACHE_LINE_SIZE; + + g_x86DetectionDone = true; +} + +#endif + +NAMESPACE_END + +#endif diff --git a/cryptopp/src/sha.cpp b/cryptopp/src/sha.cpp new file mode 100644 index 0000000000..1ff6c0b45c --- /dev/null +++ b/cryptopp/src/sha.cpp @@ -0,0 +1,899 @@ +// sha.cpp - modified by Wei Dai from Steve Reid's public domain sha1.c + +// Steve Reid implemented SHA-1. Wei Dai implemented SHA-2. +// Both are in the public domain. + +// use "cl /EP /P /DCRYPTOPP_GENERATE_X64_MASM sha.cpp" to generate MASM code + +#include "cryptopp/pch.h" + +#ifndef CRYPTOPP_IMPORTS +#ifndef CRYPTOPP_GENERATE_X64_MASM + +#include "cryptopp/sha.h" +#include "cryptopp/misc.h" +#include "cryptopp/cpu.h" + +NAMESPACE_BEGIN(CryptoPP) + +// start of Steve Reid's code + +#define blk0(i) (W[i] = data[i]) +#define blk1(i) (W[i&15] = rotlFixed(W[(i+13)&15]^W[(i+8)&15]^W[(i+2)&15]^W[i&15],1)) + +void SHA1::InitState(HashWordType *state) +{ + state[0] = 0x67452301L; + state[1] = 0xEFCDAB89L; + state[2] = 0x98BADCFEL; + state[3] = 0x10325476L; + state[4] = 0xC3D2E1F0L; +} + +#define f1(x,y,z) (z^(x&(y^z))) +#define f2(x,y,z) (x^y^z) +#define f3(x,y,z) ((x&y)|(z&(x|y))) +#define f4(x,y,z) (x^y^z) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=f1(w,x,y)+blk0(i)+0x5A827999+rotlFixed(v,5);w=rotlFixed(w,30); +#define R1(v,w,x,y,z,i) z+=f1(w,x,y)+blk1(i)+0x5A827999+rotlFixed(v,5);w=rotlFixed(w,30); +#define R2(v,w,x,y,z,i) z+=f2(w,x,y)+blk1(i)+0x6ED9EBA1+rotlFixed(v,5);w=rotlFixed(w,30); +#define R3(v,w,x,y,z,i) z+=f3(w,x,y)+blk1(i)+0x8F1BBCDC+rotlFixed(v,5);w=rotlFixed(w,30); +#define R4(v,w,x,y,z,i) z+=f4(w,x,y)+blk1(i)+0xCA62C1D6+rotlFixed(v,5);w=rotlFixed(w,30); + +void SHA1::Transform(word32 *state, const word32 *data) +{ + word32 W[16]; + /* Copy context->state[] to working vars */ + word32 a = state[0]; + word32 b = state[1]; + word32 c = state[2]; + word32 d = state[3]; + word32 e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; +} + +// end of Steve Reid's code + +// ************************************************************* + +void SHA224::InitState(HashWordType *state) +{ + static const word32 s[8] = {0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4}; + memcpy(state, s, sizeof(s)); +} + +void SHA256::InitState(HashWordType *state) +{ + static const word32 s[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + memcpy(state, s, sizeof(s)); +} + +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE +CRYPTOPP_ALIGN_DATA(16) extern const word32 SHA256_K[64] CRYPTOPP_SECTION_ALIGN16 = { +#else +extern const word32 SHA256_K[64] = { +#endif + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +#endif // #ifndef CRYPTOPP_GENERATE_X64_MASM + +#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_GENERATE_X64_MASM) + +#pragma warning(disable: 4731) // frame pointer register 'ebp' modified by inline assembly code + +static void CRYPTOPP_FASTCALL X86_SHA256_HashBlocks(word32 *state, const word32 *data, size_t len +#if defined(_MSC_VER) && (_MSC_VER == 1200) + , ... // VC60 workaround: prevent VC 6 from inlining this function +#endif + ) +{ +#if defined(_MSC_VER) && (_MSC_VER == 1200) + AS2(mov ecx, [state]) + AS2(mov edx, [data]) +#endif + + #define LOCALS_SIZE 8*4 + 16*4 + 4*WORD_SZ + #define H(i) [BASE+ASM_MOD(1024+7-(i),8)*4] + #define G(i) H(i+1) + #define F(i) H(i+2) + #define E(i) H(i+3) + #define D(i) H(i+4) + #define C(i) H(i+5) + #define B(i) H(i+6) + #define A(i) H(i+7) + #define Wt(i) BASE+8*4+ASM_MOD(1024+15-(i),16)*4 + #define Wt_2(i) Wt((i)-2) + #define Wt_15(i) Wt((i)-15) + #define Wt_7(i) Wt((i)-7) + #define K_END [BASE+8*4+16*4+0*WORD_SZ] + #define STATE_SAVE [BASE+8*4+16*4+1*WORD_SZ] + #define DATA_SAVE [BASE+8*4+16*4+2*WORD_SZ] + #define DATA_END [BASE+8*4+16*4+3*WORD_SZ] + #define Kt(i) WORD_REG(si)+(i)*4 +#if CRYPTOPP_BOOL_X86 + #define BASE esp+4 +#elif defined(__GNUC__) + #define BASE r8 +#else + #define BASE rsp +#endif + +#define RA0(i, edx, edi) \ + AS2( add edx, [Kt(i)] )\ + AS2( add edx, [Wt(i)] )\ + AS2( add edx, H(i) )\ + +#define RA1(i, edx, edi) + +#define RB0(i, edx, edi) + +#define RB1(i, edx, edi) \ + AS2( mov AS_REG_7d, [Wt_2(i)] )\ + AS2( mov edi, [Wt_15(i)])\ + AS2( mov ebx, AS_REG_7d )\ + AS2( shr AS_REG_7d, 10 )\ + AS2( ror ebx, 17 )\ + AS2( xor AS_REG_7d, ebx )\ + AS2( ror ebx, 2 )\ + AS2( xor ebx, AS_REG_7d )/* s1(W_t-2) */\ + AS2( add ebx, [Wt_7(i)])\ + AS2( mov AS_REG_7d, edi )\ + AS2( shr AS_REG_7d, 3 )\ + AS2( ror edi, 7 )\ + AS2( add ebx, [Wt(i)])/* s1(W_t-2) + W_t-7 + W_t-16 */\ + AS2( xor AS_REG_7d, edi )\ + AS2( add edx, [Kt(i)])\ + AS2( ror edi, 11 )\ + AS2( add edx, H(i) )\ + AS2( xor AS_REG_7d, edi )/* s0(W_t-15) */\ + AS2( add AS_REG_7d, ebx )/* W_t = s1(W_t-2) + W_t-7 + s0(W_t-15) W_t-16*/\ + AS2( mov [Wt(i)], AS_REG_7d)\ + AS2( add edx, AS_REG_7d )\ + +#define ROUND(i, r, eax, ecx, edi, edx)\ + /* in: edi = E */\ + /* unused: eax, ecx, temp: ebx, AS_REG_7d, out: edx = T1 */\ + AS2( mov edx, F(i) )\ + AS2( xor edx, G(i) )\ + AS2( and edx, edi )\ + AS2( xor edx, G(i) )/* Ch(E,F,G) = (G^(E&(F^G))) */\ + AS2( mov AS_REG_7d, edi )\ + AS2( ror edi, 6 )\ + AS2( ror AS_REG_7d, 25 )\ + RA##r(i, edx, edi )/* H + Wt + Kt + Ch(E,F,G) */\ + AS2( xor AS_REG_7d, edi )\ + AS2( ror edi, 5 )\ + AS2( xor AS_REG_7d, edi )/* S1(E) */\ + AS2( add edx, AS_REG_7d )/* T1 = S1(E) + Ch(E,F,G) + H + Wt + Kt */\ + RB##r(i, edx, edi )/* H + Wt + Kt + Ch(E,F,G) */\ + /* in: ecx = A, eax = B^C, edx = T1 */\ + /* unused: edx, temp: ebx, AS_REG_7d, out: eax = A, ecx = B^C, edx = E */\ + AS2( mov ebx, ecx )\ + AS2( xor ecx, B(i) )/* A^B */\ + AS2( and eax, ecx )\ + AS2( xor eax, B(i) )/* Maj(A,B,C) = B^((A^B)&(B^C) */\ + AS2( mov AS_REG_7d, ebx )\ + AS2( ror ebx, 2 )\ + AS2( add eax, edx )/* T1 + Maj(A,B,C) */\ + AS2( add edx, D(i) )\ + AS2( mov D(i), edx )\ + AS2( ror AS_REG_7d, 22 )\ + AS2( xor AS_REG_7d, ebx )\ + AS2( ror ebx, 11 )\ + AS2( xor AS_REG_7d, ebx )\ + AS2( add eax, AS_REG_7d )/* T1 + S0(A) + Maj(A,B,C) */\ + AS2( mov H(i), eax )\ + +#define SWAP_COPY(i) \ + AS2( mov WORD_REG(bx), [WORD_REG(dx)+i*WORD_SZ])\ + AS1( bswap WORD_REG(bx))\ + AS2( mov [Wt(i*(1+CRYPTOPP_BOOL_X64)+CRYPTOPP_BOOL_X64)], WORD_REG(bx)) + +#if defined(__GNUC__) + #if CRYPTOPP_BOOL_X64 + FixedSizeAlignedSecBlock workspace; + #endif + __asm__ __volatile__ + ( + #if CRYPTOPP_BOOL_X64 + "lea %4, %%r8;" + #endif + ".intel_syntax noprefix;" +#elif defined(CRYPTOPP_GENERATE_X64_MASM) + ALIGN 8 + X86_SHA256_HashBlocks PROC FRAME + rex_push_reg rsi + push_reg rdi + push_reg rbx + push_reg rbp + alloc_stack(LOCALS_SIZE+8) + .endprolog + mov rdi, r8 + lea rsi, [?SHA256_K@CryptoPP@@3QBIB + 48*4] +#endif + +#if CRYPTOPP_BOOL_X86 + #ifndef __GNUC__ + AS2( mov edi, [len]) + AS2( lea WORD_REG(si), [SHA256_K+48*4]) + #endif + #if !defined(_MSC_VER) || (_MSC_VER < 1400) + AS_PUSH_IF86(bx) + #endif + + AS_PUSH_IF86(bp) + AS2( mov ebx, esp) + AS2( and esp, -16) + AS2( sub WORD_REG(sp), LOCALS_SIZE) + AS_PUSH_IF86(bx) +#endif + AS2( mov STATE_SAVE, WORD_REG(cx)) + AS2( mov DATA_SAVE, WORD_REG(dx)) + AS2( add WORD_REG(di), WORD_REG(dx)) + AS2( mov DATA_END, WORD_REG(di)) + AS2( mov K_END, WORD_REG(si)) + +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE +#if CRYPTOPP_BOOL_X86 + AS2( test edi, 1) + ASJ( jnz, 2, f) +#endif + AS2( movdqa xmm0, XMMWORD_PTR [WORD_REG(cx)+0*16]) + AS2( movdqa xmm1, XMMWORD_PTR [WORD_REG(cx)+1*16]) +#endif + +#if CRYPTOPP_BOOL_X86 +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + ASJ( jmp, 0, f) +#endif + ASL(2) // non-SSE2 + AS2( mov esi, ecx) + AS2( lea edi, A(0)) + AS2( mov ecx, 8) + AS1( rep movsd) + AS2( mov esi, K_END) + ASJ( jmp, 3, f) +#endif + +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + ASL(0) + AS2( movdqa E(0), xmm1) + AS2( movdqa A(0), xmm0) +#endif +#if CRYPTOPP_BOOL_X86 + ASL(3) +#endif + AS2( sub WORD_REG(si), 48*4) + SWAP_COPY(0) SWAP_COPY(1) SWAP_COPY(2) SWAP_COPY(3) + SWAP_COPY(4) SWAP_COPY(5) SWAP_COPY(6) SWAP_COPY(7) +#if CRYPTOPP_BOOL_X86 + SWAP_COPY(8) SWAP_COPY(9) SWAP_COPY(10) SWAP_COPY(11) + SWAP_COPY(12) SWAP_COPY(13) SWAP_COPY(14) SWAP_COPY(15) +#endif + AS2( mov edi, E(0)) // E + AS2( mov eax, B(0)) // B + AS2( xor eax, C(0)) // B^C + AS2( mov ecx, A(0)) // A + + ROUND(0, 0, eax, ecx, edi, edx) + ROUND(1, 0, ecx, eax, edx, edi) + ROUND(2, 0, eax, ecx, edi, edx) + ROUND(3, 0, ecx, eax, edx, edi) + ROUND(4, 0, eax, ecx, edi, edx) + ROUND(5, 0, ecx, eax, edx, edi) + ROUND(6, 0, eax, ecx, edi, edx) + ROUND(7, 0, ecx, eax, edx, edi) + ROUND(8, 0, eax, ecx, edi, edx) + ROUND(9, 0, ecx, eax, edx, edi) + ROUND(10, 0, eax, ecx, edi, edx) + ROUND(11, 0, ecx, eax, edx, edi) + ROUND(12, 0, eax, ecx, edi, edx) + ROUND(13, 0, ecx, eax, edx, edi) + ROUND(14, 0, eax, ecx, edi, edx) + ROUND(15, 0, ecx, eax, edx, edi) + + ASL(1) + AS2(add WORD_REG(si), 4*16) + ROUND(0, 1, eax, ecx, edi, edx) + ROUND(1, 1, ecx, eax, edx, edi) + ROUND(2, 1, eax, ecx, edi, edx) + ROUND(3, 1, ecx, eax, edx, edi) + ROUND(4, 1, eax, ecx, edi, edx) + ROUND(5, 1, ecx, eax, edx, edi) + ROUND(6, 1, eax, ecx, edi, edx) + ROUND(7, 1, ecx, eax, edx, edi) + ROUND(8, 1, eax, ecx, edi, edx) + ROUND(9, 1, ecx, eax, edx, edi) + ROUND(10, 1, eax, ecx, edi, edx) + ROUND(11, 1, ecx, eax, edx, edi) + ROUND(12, 1, eax, ecx, edi, edx) + ROUND(13, 1, ecx, eax, edx, edi) + ROUND(14, 1, eax, ecx, edi, edx) + ROUND(15, 1, ecx, eax, edx, edi) + AS2( cmp WORD_REG(si), K_END) + ASJ( jne, 1, b) + + AS2( mov WORD_REG(dx), DATA_SAVE) + AS2( add WORD_REG(dx), 64) + AS2( mov AS_REG_7, STATE_SAVE) + AS2( mov DATA_SAVE, WORD_REG(dx)) + +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE +#if CRYPTOPP_BOOL_X86 + AS2( test DWORD PTR DATA_END, 1) + ASJ( jnz, 4, f) +#endif + AS2( movdqa xmm1, XMMWORD_PTR [AS_REG_7+1*16]) + AS2( movdqa xmm0, XMMWORD_PTR [AS_REG_7+0*16]) + AS2( paddd xmm1, E(0)) + AS2( paddd xmm0, A(0)) + AS2( movdqa [AS_REG_7+1*16], xmm1) + AS2( movdqa [AS_REG_7+0*16], xmm0) + AS2( cmp WORD_REG(dx), DATA_END) + ASJ( jl, 0, b) +#endif + +#if CRYPTOPP_BOOL_X86 +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + ASJ( jmp, 5, f) + ASL(4) // non-SSE2 +#endif + AS2( add [AS_REG_7+0*4], ecx) // A + AS2( add [AS_REG_7+4*4], edi) // E + AS2( mov eax, B(0)) + AS2( mov ebx, C(0)) + AS2( mov ecx, D(0)) + AS2( add [AS_REG_7+1*4], eax) + AS2( add [AS_REG_7+2*4], ebx) + AS2( add [AS_REG_7+3*4], ecx) + AS2( mov eax, F(0)) + AS2( mov ebx, G(0)) + AS2( mov ecx, H(0)) + AS2( add [AS_REG_7+5*4], eax) + AS2( add [AS_REG_7+6*4], ebx) + AS2( add [AS_REG_7+7*4], ecx) + AS2( mov ecx, AS_REG_7d) + AS2( cmp WORD_REG(dx), DATA_END) + ASJ( jl, 2, b) +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + ASL(5) +#endif +#endif + + AS_POP_IF86(sp) + AS_POP_IF86(bp) + #if !defined(_MSC_VER) || (_MSC_VER < 1400) + AS_POP_IF86(bx) + #endif + +#ifdef CRYPTOPP_GENERATE_X64_MASM + add rsp, LOCALS_SIZE+8 + pop rbp + pop rbx + pop rdi + pop rsi + ret + X86_SHA256_HashBlocks ENDP +#endif + +#ifdef __GNUC__ + ".att_syntax prefix;" + : + : "c" (state), "d" (data), "S" (SHA256_K+48), "D" (len) + #if CRYPTOPP_BOOL_X64 + , "m" (workspace[0]) + #endif + : "memory", "cc", "%eax" + #if CRYPTOPP_BOOL_X64 + , "%rbx", "%r8" + #endif + ); +#endif +} + +#endif // #if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_GENERATE_X64_MASM) + +#ifndef CRYPTOPP_GENERATE_X64_MASM + +#ifdef CRYPTOPP_X64_MASM_AVAILABLE +extern "C" { +void CRYPTOPP_FASTCALL X86_SHA256_HashBlocks(word32 *state, const word32 *data, size_t len); +} +#endif + +#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) + +size_t SHA256::HashMultipleBlocks(const word32 *input, size_t length) +{ + X86_SHA256_HashBlocks(m_state, input, (length&(size_t(0)-BLOCKSIZE)) - !HasSSE2()); + return length % BLOCKSIZE; +} + +size_t SHA224::HashMultipleBlocks(const word32 *input, size_t length) +{ + X86_SHA256_HashBlocks(m_state, input, (length&(size_t(0)-BLOCKSIZE)) - !HasSSE2()); + return length % BLOCKSIZE; +} + +#endif + +#define blk2(i) (W[i&15]+=s1(W[(i-2)&15])+W[(i-7)&15]+s0(W[(i-15)&15])) + +#define Ch(x,y,z) (z^(x&(y^z))) +#define Maj(x,y,z) (y^((x^y)&(y^z))) + +#define a(i) T[(0-i)&7] +#define b(i) T[(1-i)&7] +#define c(i) T[(2-i)&7] +#define d(i) T[(3-i)&7] +#define e(i) T[(4-i)&7] +#define f(i) T[(5-i)&7] +#define g(i) T[(6-i)&7] +#define h(i) T[(7-i)&7] + +#define R(i) h(i)+=S1(e(i))+Ch(e(i),f(i),g(i))+SHA256_K[i+j]+(j?blk2(i):blk0(i));\ + d(i)+=h(i);h(i)+=S0(a(i))+Maj(a(i),b(i),c(i)) + +// for SHA256 +#define S0(x) (rotrFixed(x,2)^rotrFixed(x,13)^rotrFixed(x,22)) +#define S1(x) (rotrFixed(x,6)^rotrFixed(x,11)^rotrFixed(x,25)) +#define s0(x) (rotrFixed(x,7)^rotrFixed(x,18)^(x>>3)) +#define s1(x) (rotrFixed(x,17)^rotrFixed(x,19)^(x>>10)) + +void SHA256::Transform(word32 *state, const word32 *data) +{ + word32 W[16]; +#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) + // this byte reverse is a waste of time, but this function is only called by MDC + ByteReverse(W, data, BLOCKSIZE); + X86_SHA256_HashBlocks(state, W, BLOCKSIZE - !HasSSE2()); +#else + word32 T[8]; + /* Copy context->state[] to working vars */ + memcpy(T, state, sizeof(T)); + /* 64 operations, partially loop unrolled */ + for (unsigned int j=0; j<64; j+=16) + { + R( 0); R( 1); R( 2); R( 3); + R( 4); R( 5); R( 6); R( 7); + R( 8); R( 9); R(10); R(11); + R(12); R(13); R(14); R(15); + } + /* Add the working vars back into context.state[] */ + state[0] += a(0); + state[1] += b(0); + state[2] += c(0); + state[3] += d(0); + state[4] += e(0); + state[5] += f(0); + state[6] += g(0); + state[7] += h(0); +#endif +} + +/* +// smaller but slower +void SHA256::Transform(word32 *state, const word32 *data) +{ + word32 T[20]; + word32 W[32]; + unsigned int i = 0, j = 0; + word32 *t = T+8; + + memcpy(t, state, 8*4); + word32 e = t[4], a = t[0]; + + do + { + word32 w = data[j]; + W[j] = w; + w += SHA256_K[j]; + w += t[7]; + w += S1(e); + w += Ch(e, t[5], t[6]); + e = t[3] + w; + t[3] = t[3+8] = e; + w += S0(t[0]); + a = w + Maj(a, t[1], t[2]); + t[-1] = t[7] = a; + --t; + ++j; + if (j%8 == 0) + t += 8; + } while (j<16); + + do + { + i = j&0xf; + word32 w = s1(W[i+16-2]) + s0(W[i+16-15]) + W[i] + W[i+16-7]; + W[i+16] = W[i] = w; + w += SHA256_K[j]; + w += t[7]; + w += S1(e); + w += Ch(e, t[5], t[6]); + e = t[3] + w; + t[3] = t[3+8] = e; + w += S0(t[0]); + a = w + Maj(a, t[1], t[2]); + t[-1] = t[7] = a; + + w = s1(W[(i+1)+16-2]) + s0(W[(i+1)+16-15]) + W[(i+1)] + W[(i+1)+16-7]; + W[(i+1)+16] = W[(i+1)] = w; + w += SHA256_K[j+1]; + w += (t-1)[7]; + w += S1(e); + w += Ch(e, (t-1)[5], (t-1)[6]); + e = (t-1)[3] + w; + (t-1)[3] = (t-1)[3+8] = e; + w += S0((t-1)[0]); + a = w + Maj(a, (t-1)[1], (t-1)[2]); + (t-1)[-1] = (t-1)[7] = a; + + t-=2; + j+=2; + if (j%8 == 0) + t += 8; + } while (j<64); + + state[0] += a; + state[1] += t[1]; + state[2] += t[2]; + state[3] += t[3]; + state[4] += e; + state[5] += t[5]; + state[6] += t[6]; + state[7] += t[7]; +} +*/ + +#undef S0 +#undef S1 +#undef s0 +#undef s1 +#undef R + +// ************************************************************* + +void SHA384::InitState(HashWordType *state) +{ + static const word64 s[8] = { + W64LIT(0xcbbb9d5dc1059ed8), W64LIT(0x629a292a367cd507), + W64LIT(0x9159015a3070dd17), W64LIT(0x152fecd8f70e5939), + W64LIT(0x67332667ffc00b31), W64LIT(0x8eb44a8768581511), + W64LIT(0xdb0c2e0d64f98fa7), W64LIT(0x47b5481dbefa4fa4)}; + memcpy(state, s, sizeof(s)); +} + +void SHA512::InitState(HashWordType *state) +{ + static const word64 s[8] = { + W64LIT(0x6a09e667f3bcc908), W64LIT(0xbb67ae8584caa73b), + W64LIT(0x3c6ef372fe94f82b), W64LIT(0xa54ff53a5f1d36f1), + W64LIT(0x510e527fade682d1), W64LIT(0x9b05688c2b3e6c1f), + W64LIT(0x1f83d9abfb41bd6b), W64LIT(0x5be0cd19137e2179)}; + memcpy(state, s, sizeof(s)); +} + +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 +CRYPTOPP_ALIGN_DATA(16) static const word64 SHA512_K[80] CRYPTOPP_SECTION_ALIGN16 = { +#else +static const word64 SHA512_K[80] = { +#endif + W64LIT(0x428a2f98d728ae22), W64LIT(0x7137449123ef65cd), + W64LIT(0xb5c0fbcfec4d3b2f), W64LIT(0xe9b5dba58189dbbc), + W64LIT(0x3956c25bf348b538), W64LIT(0x59f111f1b605d019), + W64LIT(0x923f82a4af194f9b), W64LIT(0xab1c5ed5da6d8118), + W64LIT(0xd807aa98a3030242), W64LIT(0x12835b0145706fbe), + W64LIT(0x243185be4ee4b28c), W64LIT(0x550c7dc3d5ffb4e2), + W64LIT(0x72be5d74f27b896f), W64LIT(0x80deb1fe3b1696b1), + W64LIT(0x9bdc06a725c71235), W64LIT(0xc19bf174cf692694), + W64LIT(0xe49b69c19ef14ad2), W64LIT(0xefbe4786384f25e3), + W64LIT(0x0fc19dc68b8cd5b5), W64LIT(0x240ca1cc77ac9c65), + W64LIT(0x2de92c6f592b0275), W64LIT(0x4a7484aa6ea6e483), + W64LIT(0x5cb0a9dcbd41fbd4), W64LIT(0x76f988da831153b5), + W64LIT(0x983e5152ee66dfab), W64LIT(0xa831c66d2db43210), + W64LIT(0xb00327c898fb213f), W64LIT(0xbf597fc7beef0ee4), + W64LIT(0xc6e00bf33da88fc2), W64LIT(0xd5a79147930aa725), + W64LIT(0x06ca6351e003826f), W64LIT(0x142929670a0e6e70), + W64LIT(0x27b70a8546d22ffc), W64LIT(0x2e1b21385c26c926), + W64LIT(0x4d2c6dfc5ac42aed), W64LIT(0x53380d139d95b3df), + W64LIT(0x650a73548baf63de), W64LIT(0x766a0abb3c77b2a8), + W64LIT(0x81c2c92e47edaee6), W64LIT(0x92722c851482353b), + W64LIT(0xa2bfe8a14cf10364), W64LIT(0xa81a664bbc423001), + W64LIT(0xc24b8b70d0f89791), W64LIT(0xc76c51a30654be30), + W64LIT(0xd192e819d6ef5218), W64LIT(0xd69906245565a910), + W64LIT(0xf40e35855771202a), W64LIT(0x106aa07032bbd1b8), + W64LIT(0x19a4c116b8d2d0c8), W64LIT(0x1e376c085141ab53), + W64LIT(0x2748774cdf8eeb99), W64LIT(0x34b0bcb5e19b48a8), + W64LIT(0x391c0cb3c5c95a63), W64LIT(0x4ed8aa4ae3418acb), + W64LIT(0x5b9cca4f7763e373), W64LIT(0x682e6ff3d6b2b8a3), + W64LIT(0x748f82ee5defb2fc), W64LIT(0x78a5636f43172f60), + W64LIT(0x84c87814a1f0ab72), W64LIT(0x8cc702081a6439ec), + W64LIT(0x90befffa23631e28), W64LIT(0xa4506cebde82bde9), + W64LIT(0xbef9a3f7b2c67915), W64LIT(0xc67178f2e372532b), + W64LIT(0xca273eceea26619c), W64LIT(0xd186b8c721c0c207), + W64LIT(0xeada7dd6cde0eb1e), W64LIT(0xf57d4f7fee6ed178), + W64LIT(0x06f067aa72176fba), W64LIT(0x0a637dc5a2c898a6), + W64LIT(0x113f9804bef90dae), W64LIT(0x1b710b35131c471b), + W64LIT(0x28db77f523047d84), W64LIT(0x32caab7b40c72493), + W64LIT(0x3c9ebe0a15c9bebc), W64LIT(0x431d67c49c100d4c), + W64LIT(0x4cc5d4becb3e42b6), W64LIT(0x597f299cfc657e2a), + W64LIT(0x5fcb6fab3ad6faec), W64LIT(0x6c44198c4a475817) +}; + +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 +// put assembly version in separate function, otherwise MSVC 2005 SP1 doesn't generate correct code for the non-assembly version +CRYPTOPP_NAKED static void CRYPTOPP_FASTCALL SHA512_SSE2_Transform(word64 *state, const word64 *data) +{ +#ifdef __GNUC__ + __asm__ __volatile__ + ( + ".intel_syntax noprefix;" + AS1( push ebx) + AS2( mov ebx, eax) +#else + AS1( push ebx) + AS1( push esi) + AS1( push edi) + AS2( lea ebx, SHA512_K) +#endif + + AS2( mov eax, esp) + AS2( and esp, 0xfffffff0) + AS2( sub esp, 27*16) // 17*16 for expanded data, 20*8 for state + AS1( push eax) + AS2( xor eax, eax) + AS2( lea edi, [esp+4+8*8]) // start at middle of state buffer. will decrement pointer each round to avoid copying + AS2( lea esi, [esp+4+20*8+8]) // 16-byte alignment, then add 8 + + AS2( movdqa xmm0, [ecx+0*16]) + AS2( movdq2q mm4, xmm0) + AS2( movdqa [edi+0*16], xmm0) + AS2( movdqa xmm0, [ecx+1*16]) + AS2( movdqa [edi+1*16], xmm0) + AS2( movdqa xmm0, [ecx+2*16]) + AS2( movdq2q mm5, xmm0) + AS2( movdqa [edi+2*16], xmm0) + AS2( movdqa xmm0, [ecx+3*16]) + AS2( movdqa [edi+3*16], xmm0) + ASJ( jmp, 0, f) + +#define SSE2_S0_S1(r, a, b, c) \ + AS2( movq mm6, r)\ + AS2( psrlq r, a)\ + AS2( movq mm7, r)\ + AS2( psllq mm6, 64-c)\ + AS2( pxor mm7, mm6)\ + AS2( psrlq r, b-a)\ + AS2( pxor mm7, r)\ + AS2( psllq mm6, c-b)\ + AS2( pxor mm7, mm6)\ + AS2( psrlq r, c-b)\ + AS2( pxor r, mm7)\ + AS2( psllq mm6, b-a)\ + AS2( pxor r, mm6) + +#define SSE2_s0(r, a, b, c) \ + AS2( movdqa xmm6, r)\ + AS2( psrlq r, a)\ + AS2( movdqa xmm7, r)\ + AS2( psllq xmm6, 64-c)\ + AS2( pxor xmm7, xmm6)\ + AS2( psrlq r, b-a)\ + AS2( pxor xmm7, r)\ + AS2( psrlq r, c-b)\ + AS2( pxor r, xmm7)\ + AS2( psllq xmm6, c-a)\ + AS2( pxor r, xmm6) + +#define SSE2_s1(r, a, b, c) \ + AS2( movdqa xmm6, r)\ + AS2( psrlq r, a)\ + AS2( movdqa xmm7, r)\ + AS2( psllq xmm6, 64-c)\ + AS2( pxor xmm7, xmm6)\ + AS2( psrlq r, b-a)\ + AS2( pxor xmm7, r)\ + AS2( psllq xmm6, c-b)\ + AS2( pxor xmm7, xmm6)\ + AS2( psrlq r, c-b)\ + AS2( pxor r, xmm7) + + ASL(SHA512_Round) + // k + w is in mm0, a is in mm4, e is in mm5 + AS2( paddq mm0, [edi+7*8]) // h + AS2( movq mm2, [edi+5*8]) // f + AS2( movq mm3, [edi+6*8]) // g + AS2( pxor mm2, mm3) + AS2( pand mm2, mm5) + SSE2_S0_S1(mm5,14,18,41) + AS2( pxor mm2, mm3) + AS2( paddq mm0, mm2) // h += Ch(e,f,g) + AS2( paddq mm5, mm0) // h += S1(e) + AS2( movq mm2, [edi+1*8]) // b + AS2( movq mm1, mm2) + AS2( por mm2, mm4) + AS2( pand mm2, [edi+2*8]) // c + AS2( pand mm1, mm4) + AS2( por mm1, mm2) + AS2( paddq mm1, mm5) // temp = h + Maj(a,b,c) + AS2( paddq mm5, [edi+3*8]) // e = d + h + AS2( movq [edi+3*8], mm5) + AS2( movq [edi+11*8], mm5) + SSE2_S0_S1(mm4,28,34,39) // S0(a) + AS2( paddq mm4, mm1) // a = temp + S0(a) + AS2( movq [edi-8], mm4) + AS2( movq [edi+7*8], mm4) + AS1( ret) + + // first 16 rounds + ASL(0) + AS2( movq mm0, [edx+eax*8]) + AS2( movq [esi+eax*8], mm0) + AS2( movq [esi+eax*8+16*8], mm0) + AS2( paddq mm0, [ebx+eax*8]) + ASC( call, SHA512_Round) + AS1( inc eax) + AS2( sub edi, 8) + AS2( test eax, 7) + ASJ( jnz, 0, b) + AS2( add edi, 8*8) + AS2( cmp eax, 16) + ASJ( jne, 0, b) + + // rest of the rounds + AS2( movdqu xmm0, [esi+(16-2)*8]) + ASL(1) + // data expansion, W[i-2] already in xmm0 + AS2( movdqu xmm3, [esi]) + AS2( paddq xmm3, [esi+(16-7)*8]) + AS2( movdqa xmm2, [esi+(16-15)*8]) + SSE2_s1(xmm0, 6, 19, 61) + AS2( paddq xmm0, xmm3) + SSE2_s0(xmm2, 1, 7, 8) + AS2( paddq xmm0, xmm2) + AS2( movdq2q mm0, xmm0) + AS2( movhlps xmm1, xmm0) + AS2( paddq mm0, [ebx+eax*8]) + AS2( movlps [esi], xmm0) + AS2( movlps [esi+8], xmm1) + AS2( movlps [esi+8*16], xmm0) + AS2( movlps [esi+8*17], xmm1) + // 2 rounds + ASC( call, SHA512_Round) + AS2( sub edi, 8) + AS2( movdq2q mm0, xmm1) + AS2( paddq mm0, [ebx+eax*8+8]) + ASC( call, SHA512_Round) + // update indices and loop + AS2( add esi, 16) + AS2( add eax, 2) + AS2( sub edi, 8) + AS2( test eax, 7) + ASJ( jnz, 1, b) + // do housekeeping every 8 rounds + AS2( mov esi, 0xf) + AS2( and esi, eax) + AS2( lea esi, [esp+4+20*8+8+esi*8]) + AS2( add edi, 8*8) + AS2( cmp eax, 80) + ASJ( jne, 1, b) + +#define SSE2_CombineState(i) \ + AS2( movdqa xmm0, [edi+i*16])\ + AS2( paddq xmm0, [ecx+i*16])\ + AS2( movdqa [ecx+i*16], xmm0) + + SSE2_CombineState(0) + SSE2_CombineState(1) + SSE2_CombineState(2) + SSE2_CombineState(3) + + AS1( pop esp) + AS1( emms) + +#if defined(__GNUC__) + AS1( pop ebx) + ".att_syntax prefix;" + : + : "a" (SHA512_K), "c" (state), "d" (data) + : "%esi", "%edi", "memory", "cc" + ); +#else + AS1( pop edi) + AS1( pop esi) + AS1( pop ebx) + AS1( ret) +#endif +} +#endif // #if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + +void SHA512::Transform(word64 *state, const word64 *data) +{ +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 + if (HasSSE2()) + { + SHA512_SSE2_Transform(state, data); + return; + } +#endif + +#define S0(x) (rotrFixed(x,28)^rotrFixed(x,34)^rotrFixed(x,39)) +#define S1(x) (rotrFixed(x,14)^rotrFixed(x,18)^rotrFixed(x,41)) +#define s0(x) (rotrFixed(x,1)^rotrFixed(x,8)^(x>>7)) +#define s1(x) (rotrFixed(x,19)^rotrFixed(x,61)^(x>>6)) + +#define R(i) h(i)+=S1(e(i))+Ch(e(i),f(i),g(i))+SHA512_K[i+j]+(j?blk2(i):blk0(i));\ + d(i)+=h(i);h(i)+=S0(a(i))+Maj(a(i),b(i),c(i)) + + word64 W[16]; + word64 T[8]; + /* Copy context->state[] to working vars */ + memcpy(T, state, sizeof(T)); + /* 80 operations, partially loop unrolled */ + for (unsigned int j=0; j<80; j+=16) + { + R( 0); R( 1); R( 2); R( 3); + R( 4); R( 5); R( 6); R( 7); + R( 8); R( 9); R(10); R(11); + R(12); R(13); R(14); R(15); + } + /* Add the working vars back into context.state[] */ + state[0] += a(0); + state[1] += b(0); + state[2] += c(0); + state[3] += d(0); + state[4] += e(0); + state[5] += f(0); + state[6] += g(0); + state[7] += h(0); +} + +NAMESPACE_END + +#endif // #ifndef CRYPTOPP_GENERATE_X64_MASM +#endif // #ifndef CRYPTOPP_IMPORTS diff --git a/gui/include/bitcoinaddressvalidator.h b/gui/include/bitcoinaddressvalidator.h index 8b57a24714..d1a06f7e57 100644 --- a/gui/include/bitcoinaddressvalidator.h +++ b/gui/include/bitcoinaddressvalidator.h @@ -11,7 +11,7 @@ class BitcoinAddressValidator : public QRegExpValidator public: explicit BitcoinAddressValidator(QObject *parent = 0); - static const QString valid_chars; + static const int MaxAddressLength = 34; signals: public slots: diff --git a/gui/src/bitcoinaddressvalidator.cpp b/gui/src/bitcoinaddressvalidator.cpp index 408027b4d5..8e71916391 100644 --- a/gui/src/bitcoinaddressvalidator.cpp +++ b/gui/src/bitcoinaddressvalidator.cpp @@ -1,8 +1,8 @@ #include "bitcoinaddressvalidator.h" -const QString BitcoinAddressValidator::valid_chars = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; +#include "base58.h" BitcoinAddressValidator::BitcoinAddressValidator(QObject *parent) : - QRegExpValidator(QRegExp("^["+valid_chars+"]+"), parent) + QRegExpValidator(QRegExp(QString("^[")+QString(pszBase58)+QString("]+")), parent) { } diff --git a/gui/src/sendcoinsdialog.cpp b/gui/src/sendcoinsdialog.cpp index 6f459fb669..6c216d397b 100644 --- a/gui/src/sendcoinsdialog.cpp +++ b/gui/src/sendcoinsdialog.cpp @@ -12,6 +12,7 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) : ui(new Ui::SendCoinsDialog) { ui->setupUi(this); + ui->payTo->setMaxLength(BitcoinAddressValidator::MaxAddressLength); ui->payTo->setValidator(new BitcoinAddressValidator(this)); ui->payAmount->setValidator(new QDoubleValidator(this)); } diff --git a/lib/include/bignum.h b/lib/include/bignum.h index e353532963..c207af1004 100644 --- a/lib/include/bignum.h +++ b/lib/include/bignum.h @@ -311,7 +311,7 @@ public: CAutoBN_CTX pctx; CBigNum bnBase = nBase; CBigNum bn0 = 0; - string str; + std::string str; CBigNum bn = *this; BN_set_negative(&bn, false); CBigNum dv; @@ -351,7 +351,7 @@ public: template void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) { - vector vch; + std::vector vch; ::Unserialize(s, vch, nType, nVersion); setvch(vch); } diff --git a/lib/include/uint256.h b/lib/include/uint256.h index 356b2dc8e9..14feb1683d 100644 --- a/lib/include/uint256.h +++ b/lib/include/uint256.h @@ -302,7 +302,7 @@ public: char psz[sizeof(pn)*2 + 1]; for (int i = 0; i < sizeof(pn); i++) sprintf(psz + i*2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]); - return string(psz, psz + sizeof(pn)*2); + return std::string(psz, psz + sizeof(pn)*2); } void SetHex(const char* psz) @@ -632,7 +632,7 @@ inline const uint256 operator-(const uint256& a, const uint256& b) { return -inline int Testuint256AdHoc(vector vArg) +inline int Testuint256AdHoc(std::vector vArg) { uint256 g(0); diff --git a/lib/include/util.h b/lib/include/util.h index af8cfcf659..e25004fa1c 100644 --- a/lib/include/util.h +++ b/lib/include/util.h @@ -5,8 +5,11 @@ #define BITCOIN_UTIL_H #include "uint256.h" +//#include "cryptopp/sha.h" #include +#include +#include #include #include #include @@ -16,6 +19,9 @@ #include #include +#include +#include + #if defined(_MSC_VER) || defined(__BORLANDC__) typedef __int64 int64; @@ -552,7 +558,7 @@ uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=VERSION) return Hash(ss.begin(), ss.end()); } -inline uint160 Hash160(const vector& vch) +inline uint160 Hash160(const std::vector& vch) { uint256 hash1; SHA256(&vch[0], vch.size(), (unsigned char*)&hash1); -- cgit v1.2.3 From c24d047b9c734b51d6f5d8018f0c9f5c5634dd91 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 14 May 2011 11:25:37 +0200 Subject: core is a more appropriate name for the bitcoin library --- bitcoin.pro | 12 +- core/include/base58.h | 208 ++++++++ core/include/bignum.h | 537 ++++++++++++++++++++ core/include/serialize.h | 1268 ++++++++++++++++++++++++++++++++++++++++++++++ core/include/uint256.h | 765 ++++++++++++++++++++++++++++ core/include/util.h | 679 +++++++++++++++++++++++++ lib/include/base58.h | 208 -------- lib/include/bignum.h | 537 -------------------- lib/include/serialize.h | 1268 ---------------------------------------------- lib/include/uint256.h | 765 ---------------------------- lib/include/util.h | 679 ------------------------- 11 files changed, 3463 insertions(+), 3463 deletions(-) create mode 100644 core/include/base58.h create mode 100644 core/include/bignum.h create mode 100644 core/include/serialize.h create mode 100644 core/include/uint256.h create mode 100644 core/include/util.h delete mode 100644 lib/include/base58.h delete mode 100644 lib/include/bignum.h delete mode 100644 lib/include/serialize.h delete mode 100644 lib/include/uint256.h delete mode 100644 lib/include/util.h diff --git a/bitcoin.pro b/bitcoin.pro index dd31f3ee18..84ecc9a0b2 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -1,7 +1,7 @@ TEMPLATE = app TARGET = DEPENDPATH += . -INCLUDEPATH += gui/include lib/include cryptopp/include +INCLUDEPATH += gui/include core/include cryptopp/include # Input HEADERS += gui/include/bitcoingui.h \ @@ -14,11 +14,11 @@ HEADERS += gui/include/bitcoingui.h \ gui/include/aboutdialog.h \ gui/include/editaddressdialog.h \ gui/include/bitcoinaddressvalidator.h \ - lib/include/base58.h \ - lib/include/bignum.h \ - lib/include/util.h \ - lib/include/uint256.h \ - lib/include/serialize.h \ + core/include/base58.h \ + core/include/bignum.h \ + core/include/util.h \ + core/include/uint256.h \ + core/include/serialize.h \ cryptopp/include/cryptopp/stdcpp.h \ cryptopp/include/cryptopp/smartptr.h \ cryptopp/include/cryptopp/simple.h \ diff --git a/core/include/base58.h b/core/include/base58.h new file mode 100644 index 0000000000..7acdf63ae3 --- /dev/null +++ b/core/include/base58.h @@ -0,0 +1,208 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + + +// +// Why base-58 instead of standard base-64 encoding? +// - Don't want 0OIl characters that look the same in some fonts and +// could be used to create visually identical looking account numbers. +// - A std::string with non-alphanumeric characters is not as easily accepted as an account number. +// - E-mail usually won't line-break if there's no punctuation to break at. +// - Doubleclicking selects the whole number as one word if it's all alphanumeric. +// +#ifndef BITCOIN_BASE58_H +#define BITCOIN_BASE58_H + +#include +#include +#include "bignum.h" + +static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + + +inline std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) +{ + CAutoBN_CTX pctx; + CBigNum bn58 = 58; + CBigNum bn0 = 0; + + // Convert big endian data to little endian + // Extra zero at the end make sure bignum will interpret as a positive number + std::vector vchTmp(pend-pbegin+1, 0); + reverse_copy(pbegin, pend, vchTmp.begin()); + + // Convert little endian data to bignum + CBigNum bn; + bn.setvch(vchTmp); + + // Convert bignum to std::string + std::string str; + str.reserve((pend - pbegin) * 138 / 100 + 1); + CBigNum dv; + CBigNum rem; + while (bn > bn0) + { + if (!BN_div(&dv, &rem, &bn, &bn58, pctx)) + throw bignum_error("EncodeBase58 : BN_div failed"); + bn = dv; + unsigned int c = rem.getulong(); + str += pszBase58[c]; + } + + // Leading zeroes encoded as base58 zeros + for (const unsigned char* p = pbegin; p < pend && *p == 0; p++) + str += pszBase58[0]; + + // Convert little endian std::string to big endian + reverse(str.begin(), str.end()); + return str; +} + +inline std::string EncodeBase58(const std::vector& vch) +{ + return EncodeBase58(&vch[0], &vch[0] + vch.size()); +} + +inline bool DecodeBase58(const char* psz, std::vector& vchRet) +{ + CAutoBN_CTX pctx; + vchRet.clear(); + CBigNum bn58 = 58; + CBigNum bn = 0; + CBigNum bnChar; + while (isspace(*psz)) + psz++; + + // Convert big endian std::string to bignum + for (const char* p = psz; *p; p++) + { + const char* p1 = strchr(pszBase58, *p); + if (p1 == NULL) + { + while (isspace(*p)) + p++; + if (*p != '\0') + return false; + break; + } + bnChar.setulong(p1 - pszBase58); + if (!BN_mul(&bn, &bn, &bn58, pctx)) + throw bignum_error("DecodeBase58 : BN_mul failed"); + bn += bnChar; + } + + // Get bignum as little endian data + std::vector vchTmp = bn.getvch(); + + // Trim off sign byte if present + if (vchTmp.size() >= 2 && vchTmp.end()[-1] == 0 && vchTmp.end()[-2] >= 0x80) + vchTmp.erase(vchTmp.end()-1); + + // Restore leading zeros + int nLeadingZeros = 0; + for (const char* p = psz; *p == pszBase58[0]; p++) + nLeadingZeros++; + vchRet.assign(nLeadingZeros + vchTmp.size(), 0); + + // Convert little endian data to big endian + reverse_copy(vchTmp.begin(), vchTmp.end(), vchRet.end() - vchTmp.size()); + return true; +} + +inline bool DecodeBase58(const std::string& str, std::vector& vchRet) +{ + return DecodeBase58(str.c_str(), vchRet); +} + + + + + +inline std::string EncodeBase58Check(const std::vector& vchIn) +{ + // add 4-byte hash check to the end + std::vector vch(vchIn); + uint256 hash = Hash(vch.begin(), vch.end()); + vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4); + return EncodeBase58(vch); +} + +inline bool DecodeBase58Check(const char* psz, std::vector& vchRet) +{ + if (!DecodeBase58(psz, vchRet)) + return false; + if (vchRet.size() < 4) + { + vchRet.clear(); + return false; + } + uint256 hash = Hash(vchRet.begin(), vchRet.end()-4); + if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) + { + vchRet.clear(); + return false; + } + vchRet.resize(vchRet.size()-4); + return true; +} + +inline bool DecodeBase58Check(const std::string& str, std::vector& vchRet) +{ + return DecodeBase58Check(str.c_str(), vchRet); +} + + + + + + +#define ADDRESSVERSION ((unsigned char)(fTestNet ? 111 : 0)) + +inline std::string Hash160ToAddress(uint160 hash160) +{ + // add 1-byte version number to the front + std::vector vch(1, ADDRESSVERSION); + vch.insert(vch.end(), UBEGIN(hash160), UEND(hash160)); + return EncodeBase58Check(vch); +} + +inline bool AddressToHash160(const char* psz, uint160& hash160Ret) +{ + std::vector vch; + if (!DecodeBase58Check(psz, vch)) + return false; + if (vch.empty()) + return false; + unsigned char nVersion = vch[0]; + if (vch.size() != sizeof(hash160Ret) + 1) + return false; + memcpy(&hash160Ret, &vch[1], sizeof(hash160Ret)); + return (nVersion <= ADDRESSVERSION); +} + +inline bool AddressToHash160(const std::string& str, uint160& hash160Ret) +{ + return AddressToHash160(str.c_str(), hash160Ret); +} + +inline bool IsValidBitcoinAddress(const char* psz) +{ + uint160 hash160; + return AddressToHash160(psz, hash160); +} + +inline bool IsValidBitcoinAddress(const std::string& str) +{ + return IsValidBitcoinAddress(str.c_str()); +} + + + + +inline std::string PubKeyToAddress(const std::vector& vchPubKey) +{ + return Hash160ToAddress(Hash160(vchPubKey)); +} + +#endif diff --git a/core/include/bignum.h b/core/include/bignum.h new file mode 100644 index 0000000000..c207af1004 --- /dev/null +++ b/core/include/bignum.h @@ -0,0 +1,537 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_BIGNUM_H +#define BITCOIN_BIGNUM_H + +#include +#include +#include +#include + + + + + +class bignum_error : public std::runtime_error +{ +public: + explicit bignum_error(const std::string& str) : std::runtime_error(str) {} +}; + + + +class CAutoBN_CTX +{ +protected: + BN_CTX* pctx; + BN_CTX* operator=(BN_CTX* pnew) { return pctx = pnew; } + +public: + CAutoBN_CTX() + { + pctx = BN_CTX_new(); + if (pctx == NULL) + throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned NULL"); + } + + ~CAutoBN_CTX() + { + if (pctx != NULL) + BN_CTX_free(pctx); + } + + operator BN_CTX*() { return pctx; } + BN_CTX& operator*() { return *pctx; } + BN_CTX** operator&() { return &pctx; } + bool operator!() { return (pctx == NULL); } +}; + + + +class CBigNum : public BIGNUM +{ +public: + CBigNum() + { + BN_init(this); + } + + CBigNum(const CBigNum& b) + { + BN_init(this); + if (!BN_copy(this, &b)) + { + BN_clear_free(this); + throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_copy failed"); + } + } + + CBigNum& operator=(const CBigNum& b) + { + if (!BN_copy(this, &b)) + throw bignum_error("CBigNum::operator= : BN_copy failed"); + return (*this); + } + + ~CBigNum() + { + BN_clear_free(this); + } + + CBigNum(char n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(short n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(long n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int64 n) { BN_init(this); setint64(n); } + CBigNum(unsigned char n) { BN_init(this); setulong(n); } + CBigNum(unsigned short n) { BN_init(this); setulong(n); } + CBigNum(unsigned int n) { BN_init(this); setulong(n); } + CBigNum(unsigned long n) { BN_init(this); setulong(n); } + CBigNum(uint64 n) { BN_init(this); setuint64(n); } + explicit CBigNum(uint256 n) { BN_init(this); setuint256(n); } + + explicit CBigNum(const std::vector& vch) + { + BN_init(this); + setvch(vch); + } + + void setulong(unsigned long n) + { + if (!BN_set_word(this, n)) + throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed"); + } + + unsigned long getulong() const + { + return BN_get_word(this); + } + + unsigned int getuint() const + { + return BN_get_word(this); + } + + int getint() const + { + unsigned long n = BN_get_word(this); + if (!BN_is_negative(this)) + return (n > INT_MAX ? INT_MAX : n); + else + return (n > INT_MAX ? INT_MIN : -(int)n); + } + + void setint64(int64 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fNegative = false; + if (n < (int64)0) + { + n = -n; + fNegative = true; + } + bool fLeadingZeroes = true; + for (int i = 0; i < 8; i++) + { + unsigned char c = (n >> 56) & 0xff; + n <<= 8; + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = (fNegative ? 0x80 : 0); + else if (fNegative) + c |= 0x80; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + void setuint64(uint64 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fLeadingZeroes = true; + for (int i = 0; i < 8; i++) + { + unsigned char c = (n >> 56) & 0xff; + n <<= 8; + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + void setuint256(uint256 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fLeadingZeroes = true; + unsigned char* pbegin = (unsigned char*)&n; + unsigned char* psrc = pbegin + sizeof(n); + while (psrc != pbegin) + { + unsigned char c = *(--psrc); + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize >> 0) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + uint256 getuint256() + { + unsigned int nSize = BN_bn2mpi(this, NULL); + if (nSize < 4) + return 0; + std::vector vch(nSize); + BN_bn2mpi(this, &vch[0]); + if (vch.size() > 4) + vch[4] &= 0x7f; + uint256 n = 0; + for (int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) + ((unsigned char*)&n)[i] = vch[j]; + return n; + } + + void setvch(const std::vector& vch) + { + std::vector vch2(vch.size() + 4); + unsigned int nSize = vch.size(); + vch2[0] = (nSize >> 24) & 0xff; + vch2[1] = (nSize >> 16) & 0xff; + vch2[2] = (nSize >> 8) & 0xff; + vch2[3] = (nSize >> 0) & 0xff; + reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4); + BN_mpi2bn(&vch2[0], vch2.size(), this); + } + + std::vector getvch() const + { + unsigned int nSize = BN_bn2mpi(this, NULL); + if (nSize < 4) + return std::vector(); + std::vector vch(nSize); + BN_bn2mpi(this, &vch[0]); + vch.erase(vch.begin(), vch.begin() + 4); + reverse(vch.begin(), vch.end()); + return vch; + } + + CBigNum& SetCompact(unsigned int nCompact) + { + unsigned int nSize = nCompact >> 24; + std::vector vch(4 + nSize); + vch[3] = nSize; + if (nSize >= 1) vch[4] = (nCompact >> 16) & 0xff; + if (nSize >= 2) vch[5] = (nCompact >> 8) & 0xff; + if (nSize >= 3) vch[6] = (nCompact >> 0) & 0xff; + BN_mpi2bn(&vch[0], vch.size(), this); + return *this; + } + + unsigned int GetCompact() const + { + unsigned int nSize = BN_bn2mpi(this, NULL); + std::vector vch(nSize); + nSize -= 4; + BN_bn2mpi(this, &vch[0]); + unsigned int nCompact = nSize << 24; + if (nSize >= 1) nCompact |= (vch[4] << 16); + if (nSize >= 2) nCompact |= (vch[5] << 8); + if (nSize >= 3) nCompact |= (vch[6] << 0); + return nCompact; + } + + void SetHex(const std::string& str) + { + // skip 0x + const char* psz = str.c_str(); + while (isspace(*psz)) + psz++; + bool fNegative = false; + if (*psz == '-') + { + fNegative = true; + psz++; + } + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + while (isspace(*psz)) + psz++; + + // hex string to bignum + static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; + *this = 0; + while (isxdigit(*psz)) + { + *this <<= 4; + int n = phexdigit[*psz++]; + *this += n; + } + if (fNegative) + *this = 0 - *this; + } + + std::string ToString(int nBase=10) const + { + CAutoBN_CTX pctx; + CBigNum bnBase = nBase; + CBigNum bn0 = 0; + std::string str; + CBigNum bn = *this; + BN_set_negative(&bn, false); + CBigNum dv; + CBigNum rem; + if (BN_cmp(&bn, &bn0) == 0) + return "0"; + while (BN_cmp(&bn, &bn0) > 0) + { + if (!BN_div(&dv, &rem, &bn, &bnBase, pctx)) + throw bignum_error("CBigNum::ToString() : BN_div failed"); + bn = dv; + unsigned int c = rem.getulong(); + str += "0123456789abcdef"[c]; + } + if (BN_is_negative(this)) + str += "-"; + reverse(str.begin(), str.end()); + return str; + } + + std::string GetHex() const + { + return ToString(16); + } + + unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const + { + return ::GetSerializeSize(getvch(), nType, nVersion); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const + { + ::Serialize(s, getvch(), nType, nVersion); + } + + template + void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) + { + std::vector vch; + ::Unserialize(s, vch, nType, nVersion); + setvch(vch); + } + + + bool operator!() const + { + return BN_is_zero(this); + } + + CBigNum& operator+=(const CBigNum& b) + { + if (!BN_add(this, this, &b)) + throw bignum_error("CBigNum::operator+= : BN_add failed"); + return *this; + } + + CBigNum& operator-=(const CBigNum& b) + { + *this = *this - b; + return *this; + } + + CBigNum& operator*=(const CBigNum& b) + { + CAutoBN_CTX pctx; + if (!BN_mul(this, this, &b, pctx)) + throw bignum_error("CBigNum::operator*= : BN_mul failed"); + return *this; + } + + CBigNum& operator/=(const CBigNum& b) + { + *this = *this / b; + return *this; + } + + CBigNum& operator%=(const CBigNum& b) + { + *this = *this % b; + return *this; + } + + CBigNum& operator<<=(unsigned int shift) + { + if (!BN_lshift(this, this, shift)) + throw bignum_error("CBigNum:operator<<= : BN_lshift failed"); + return *this; + } + + CBigNum& operator>>=(unsigned int shift) + { + // Note: BN_rshift segfaults on 64-bit if 2^shift is greater than the number + // if built on ubuntu 9.04 or 9.10, probably depends on version of openssl + CBigNum a = 1; + a <<= shift; + if (BN_cmp(&a, this) > 0) + { + *this = 0; + return *this; + } + + if (!BN_rshift(this, this, shift)) + throw bignum_error("CBigNum:operator>>= : BN_rshift failed"); + return *this; + } + + + CBigNum& operator++() + { + // prefix operator + if (!BN_add(this, this, BN_value_one())) + throw bignum_error("CBigNum::operator++ : BN_add failed"); + return *this; + } + + const CBigNum operator++(int) + { + // postfix operator + const CBigNum ret = *this; + ++(*this); + return ret; + } + + CBigNum& operator--() + { + // prefix operator + CBigNum r; + if (!BN_sub(&r, this, BN_value_one())) + throw bignum_error("CBigNum::operator-- : BN_sub failed"); + *this = r; + return *this; + } + + const CBigNum operator--(int) + { + // postfix operator + const CBigNum ret = *this; + --(*this); + return ret; + } + + + friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator/(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator%(const CBigNum& a, const CBigNum& b); +}; + + + +inline const CBigNum operator+(const CBigNum& a, const CBigNum& b) +{ + CBigNum r; + if (!BN_add(&r, &a, &b)) + throw bignum_error("CBigNum::operator+ : BN_add failed"); + return r; +} + +inline const CBigNum operator-(const CBigNum& a, const CBigNum& b) +{ + CBigNum r; + if (!BN_sub(&r, &a, &b)) + throw bignum_error("CBigNum::operator- : BN_sub failed"); + return r; +} + +inline const CBigNum operator-(const CBigNum& a) +{ + CBigNum r(a); + BN_set_negative(&r, !BN_is_negative(&r)); + return r; +} + +inline const CBigNum operator*(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_mul(&r, &a, &b, pctx)) + throw bignum_error("CBigNum::operator* : BN_mul failed"); + return r; +} + +inline const CBigNum operator/(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_div(&r, NULL, &a, &b, pctx)) + throw bignum_error("CBigNum::operator/ : BN_div failed"); + return r; +} + +inline const CBigNum operator%(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_mod(&r, &a, &b, pctx)) + throw bignum_error("CBigNum::operator% : BN_div failed"); + return r; +} + +inline const CBigNum operator<<(const CBigNum& a, unsigned int shift) +{ + CBigNum r; + if (!BN_lshift(&r, &a, shift)) + throw bignum_error("CBigNum:operator<< : BN_lshift failed"); + return r; +} + +inline const CBigNum operator>>(const CBigNum& a, unsigned int shift) +{ + CBigNum r = a; + r >>= shift; + return r; +} + +inline bool operator==(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) == 0); } +inline bool operator!=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) != 0); } +inline bool operator<=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) <= 0); } +inline bool operator>=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) >= 0); } +inline bool operator<(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) < 0); } +inline bool operator>(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) > 0); } + +#endif diff --git a/core/include/serialize.h b/core/include/serialize.h new file mode 100644 index 0000000000..f810b339c2 --- /dev/null +++ b/core/include/serialize.h @@ -0,0 +1,1268 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_SERIALIZE_H +#define BITCOIN_SERIALIZE_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false) ; else for +#endif +class CScript; +class CDataStream; +class CAutoFile; +static const unsigned int MAX_SIZE = 0x02000000; + +static const int VERSION = 32200; +static const char* pszSubVer = ""; +static const bool VERSION_IS_BETA = true; + + + + + + +///////////////////////////////////////////////////////////////// +// +// Templates for serializing to anything that looks like a stream, +// i.e. anything that supports .read(char*, int) and .write(char*, int) +// + +enum +{ + // primary actions + SER_NETWORK = (1 << 0), + SER_DISK = (1 << 1), + SER_GETHASH = (1 << 2), + + // modifiers + SER_SKIPSIG = (1 << 16), + SER_BLOCKHEADERONLY = (1 << 17), +}; + +#define IMPLEMENT_SERIALIZE(statements) \ + unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const \ + { \ + CSerActionGetSerializeSize ser_action; \ + const bool fGetSize = true; \ + const bool fWrite = false; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + ser_streamplaceholder s; \ + s.nType = nType; \ + s.nVersion = nVersion; \ + {statements} \ + return nSerSize; \ + } \ + template \ + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const \ + { \ + CSerActionSerialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = true; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + {statements} \ + } \ + template \ + void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) \ + { \ + CSerActionUnserialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = false; \ + const bool fRead = true; \ + unsigned int nSerSize = 0; \ + {statements} \ + } + +#define READWRITE(obj) (nSerSize += ::SerReadWrite(s, (obj), nType, nVersion, ser_action)) + + + + + + +// +// Basic types +// +#define WRITEDATA(s, obj) s.write((char*)&(obj), sizeof(obj)) +#define READDATA(s, obj) s.read((char*)&(obj), sizeof(obj)) + +inline unsigned int GetSerializeSize(char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed short a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned short a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed int a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned int a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(int64 a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(uint64 a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(float a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(double a, int, int=0) { return sizeof(a); } + +template inline void Serialize(Stream& s, char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed short a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned short a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed int a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned int a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed long a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned long a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, int64 a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, uint64 a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, float a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, double a, int, int=0) { WRITEDATA(s, a); } + +template inline void Unserialize(Stream& s, char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed short& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned short& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed int& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned int& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed long& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned long& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, int64& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, uint64& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, float& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, double& a, int, int=0) { READDATA(s, a); } + +inline unsigned int GetSerializeSize(bool a, int, int=0) { return sizeof(char); } +template inline void Serialize(Stream& s, bool a, int, int=0) { char f=a; WRITEDATA(s, f); } +template inline void Unserialize(Stream& s, bool& a, int, int=0) { char f; READDATA(s, f); a=f; } + + + + + + +// +// Compact size +// size < 253 -- 1 byte +// size <= USHRT_MAX -- 3 bytes (253 + 2 bytes) +// size <= UINT_MAX -- 5 bytes (254 + 4 bytes) +// size > UINT_MAX -- 9 bytes (255 + 8 bytes) +// +inline unsigned int GetSizeOfCompactSize(uint64 nSize) +{ + if (nSize < 253) return sizeof(unsigned char); + else if (nSize <= USHRT_MAX) return sizeof(unsigned char) + sizeof(unsigned short); + else if (nSize <= UINT_MAX) return sizeof(unsigned char) + sizeof(unsigned int); + else return sizeof(unsigned char) + sizeof(uint64); +} + +template +void WriteCompactSize(Stream& os, uint64 nSize) +{ + if (nSize < 253) + { + unsigned char chSize = nSize; + WRITEDATA(os, chSize); + } + else if (nSize <= USHRT_MAX) + { + unsigned char chSize = 253; + unsigned short xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + else if (nSize <= UINT_MAX) + { + unsigned char chSize = 254; + unsigned int xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + else + { + unsigned char chSize = 255; + uint64 xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + return; +} + +template +uint64 ReadCompactSize(Stream& is) +{ + unsigned char chSize; + READDATA(is, chSize); + uint64 nSizeRet = 0; + if (chSize < 253) + { + nSizeRet = chSize; + } + else if (chSize == 253) + { + unsigned short xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + else if (chSize == 254) + { + unsigned int xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + else + { + uint64 xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + if (nSizeRet > (uint64)MAX_SIZE) + throw std::ios_base::failure("ReadCompactSize() : size too large"); + return nSizeRet; +} + + + +// +// Wrapper for serializing arrays and POD +// There's a clever template way to make arrays serialize normally, but MSVC6 doesn't support it +// +#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) +class CFlatData +{ +protected: + char* pbegin; + char* pend; +public: + CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } + char* begin() { return pbegin; } + const char* begin() const { return pbegin; } + char* end() { return pend; } + const char* end() const { return pend; } + + unsigned int GetSerializeSize(int, int=0) const + { + return pend - pbegin; + } + + template + void Serialize(Stream& s, int, int=0) const + { + s.write(pbegin, pend - pbegin); + } + + template + void Unserialize(Stream& s, int, int=0) + { + s.read(pbegin, pend - pbegin); + } +}; + + + +// +// string stored as a fixed length field +// +template +class CFixedFieldString +{ +protected: + const std::string* pcstr; + std::string* pstr; +public: + explicit CFixedFieldString(const std::string& str) : pcstr(&str), pstr(NULL) { } + explicit CFixedFieldString(std::string& str) : pcstr(&str), pstr(&str) { } + + unsigned int GetSerializeSize(int, int=0) const + { + return LEN; + } + + template + void Serialize(Stream& s, int, int=0) const + { + char pszBuf[LEN]; + strncpy(pszBuf, pcstr->c_str(), LEN); + s.write(pszBuf, LEN); + } + + template + void Unserialize(Stream& s, int, int=0) + { + if (pstr == NULL) + throw std::ios_base::failure("CFixedFieldString::Unserialize : trying to unserialize to const string"); + char pszBuf[LEN+1]; + s.read(pszBuf, LEN); + pszBuf[LEN] = '\0'; + *pstr = pszBuf; + } +}; + + + + + +// +// Forward declarations +// + +// string +template unsigned int GetSerializeSize(const std::basic_string& str, int, int=0); +template void Serialize(Stream& os, const std::basic_string& str, int, int=0); +template void Unserialize(Stream& is, std::basic_string& str, int, int=0); + +// vector +template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&); +template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion=VERSION); +template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&); +template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion=VERSION); +template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&); +template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion=VERSION); + +// others derived from vector +extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const CScript& v, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, CScript& v, int nType, int nVersion=VERSION); + +// pair +template unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const std::pair& item, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, std::pair& item, int nType, int nVersion=VERSION); + +// 3 tuple +template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion=VERSION); + +// 4 tuple +template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion=VERSION); + +// map +template unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const std::map& m, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, std::map& m, int nType, int nVersion=VERSION); + +// set +template unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const std::set& m, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, std::set& m, int nType, int nVersion=VERSION); + + + + + +// +// If none of the specialized versions above matched, default to calling member function. +// "int nType" is changed to "long nType" to keep from getting an ambiguous overload error. +// The compiler will only cast int to long if none of the other templates matched. +// Thanks to Boost serialization for this idea. +// +template +inline unsigned int GetSerializeSize(const T& a, long nType, int nVersion=VERSION) +{ + return a.GetSerializeSize((int)nType, nVersion); +} + +template +inline void Serialize(Stream& os, const T& a, long nType, int nVersion=VERSION) +{ + a.Serialize(os, (int)nType, nVersion); +} + +template +inline void Unserialize(Stream& is, T& a, long nType, int nVersion=VERSION) +{ + a.Unserialize(is, (int)nType, nVersion); +} + + + + + +// +// string +// +template +unsigned int GetSerializeSize(const std::basic_string& str, int, int) +{ + return GetSizeOfCompactSize(str.size()) + str.size() * sizeof(str[0]); +} + +template +void Serialize(Stream& os, const std::basic_string& str, int, int) +{ + WriteCompactSize(os, str.size()); + if (!str.empty()) + os.write((char*)&str[0], str.size() * sizeof(str[0])); +} + +template +void Unserialize(Stream& is, std::basic_string& str, int, int) +{ + unsigned int nSize = ReadCompactSize(is); + str.resize(nSize); + if (nSize != 0) + is.read((char*)&str[0], nSize * sizeof(str[0])); +} + + + +// +// vector +// +template +unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); +} + +template +unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + unsigned int nSize = GetSizeOfCompactSize(v.size()); + for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + nSize += GetSerializeSize((*vi), nType, nVersion); + return nSize; +} + +template +inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion) +{ + return GetSerializeSize_impl(v, nType, nVersion, boost::is_fundamental()); +} + + +template +void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + WriteCompactSize(os, v.size()); + if (!v.empty()) + os.write((char*)&v[0], v.size() * sizeof(T)); +} + +template +void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + WriteCompactSize(os, v.size()); + for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + ::Serialize(os, (*vi), nType, nVersion); +} + +template +inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion) +{ + Serialize_impl(os, v, nType, nVersion, boost::is_fundamental()); +} + + +template +void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + //unsigned int nSize = ReadCompactSize(is); + //v.resize(nSize); + //is.read((char*)&v[0], nSize * sizeof(T)); + + // Limit size per read so bogus size value won't cause out of memory + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + while (i < nSize) + { + unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); + v.resize(i + blk); + is.read((char*)&v[i], blk * sizeof(T)); + i += blk; + } +} + +template +void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + //unsigned int nSize = ReadCompactSize(is); + //v.resize(nSize); + //for (std::vector::iterator vi = v.begin(); vi != v.end(); ++vi) + // Unserialize(is, (*vi), nType, nVersion); + + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + unsigned int nMid = 0; + while (nMid < nSize) + { + nMid += 5000000 / sizeof(T); + if (nMid > nSize) + nMid = nSize; + v.resize(nMid); + for (; i < nMid; i++) + Unserialize(is, v[i], nType, nVersion); + } +} + +template +inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion) +{ + Unserialize_impl(is, v, nType, nVersion, boost::is_fundamental()); +} + + + +// +// others derived from vector +// +inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion) +{ + return GetSerializeSize((const std::vector&)v, nType, nVersion); +} + +template +void Serialize(Stream& os, const CScript& v, int nType, int nVersion) +{ + Serialize(os, (const std::vector&)v, nType, nVersion); +} + +template +void Unserialize(Stream& is, CScript& v, int nType, int nVersion) +{ + Unserialize(is, (std::vector&)v, nType, nVersion); +} + + + +// +// pair +// +template +unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion) +{ + return GetSerializeSize(item.first, nType, nVersion) + GetSerializeSize(item.second, nType, nVersion); +} + +template +void Serialize(Stream& os, const std::pair& item, int nType, int nVersion) +{ + Serialize(os, item.first, nType, nVersion); + Serialize(os, item.second, nType, nVersion); +} + +template +void Unserialize(Stream& is, std::pair& item, int nType, int nVersion) +{ + Unserialize(is, item.first, nType, nVersion); + Unserialize(is, item.second, nType, nVersion); +} + + + +// +// 3 tuple +// +template +unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) +{ + unsigned int nSize = 0; + nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) +{ + Serialize(os, boost::get<0>(item), nType, nVersion); + Serialize(os, boost::get<1>(item), nType, nVersion); + Serialize(os, boost::get<2>(item), nType, nVersion); +} + +template +void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) +{ + Unserialize(is, boost::get<0>(item), nType, nVersion); + Unserialize(is, boost::get<1>(item), nType, nVersion); + Unserialize(is, boost::get<2>(item), nType, nVersion); +} + + + +// +// 4 tuple +// +template +unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) +{ + unsigned int nSize = 0; + nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<3>(item), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) +{ + Serialize(os, boost::get<0>(item), nType, nVersion); + Serialize(os, boost::get<1>(item), nType, nVersion); + Serialize(os, boost::get<2>(item), nType, nVersion); + Serialize(os, boost::get<3>(item), nType, nVersion); +} + +template +void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) +{ + Unserialize(is, boost::get<0>(item), nType, nVersion); + Unserialize(is, boost::get<1>(item), nType, nVersion); + Unserialize(is, boost::get<2>(item), nType, nVersion); + Unserialize(is, boost::get<3>(item), nType, nVersion); +} + + + +// +// map +// +template +unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion) +{ + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) + nSize += GetSerializeSize((*mi), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const std::map& m, int nType, int nVersion) +{ + WriteCompactSize(os, m.size()); + for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) + Serialize(os, (*mi), nType, nVersion); +} + +template +void Unserialize(Stream& is, std::map& m, int nType, int nVersion) +{ + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::map::iterator mi = m.begin(); + for (unsigned int i = 0; i < nSize; i++) + { + std::pair item; + Unserialize(is, item, nType, nVersion); + mi = m.insert(mi, item); + } +} + + + +// +// set +// +template +unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion) +{ + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) + nSize += GetSerializeSize((*it), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const std::set& m, int nType, int nVersion) +{ + WriteCompactSize(os, m.size()); + for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) + Serialize(os, (*it), nType, nVersion); +} + +template +void Unserialize(Stream& is, std::set& m, int nType, int nVersion) +{ + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::set::iterator it = m.begin(); + for (unsigned int i = 0; i < nSize; i++) + { + K key; + Unserialize(is, key, nType, nVersion); + it = m.insert(it, key); + } +} + + + +// +// Support for IMPLEMENT_SERIALIZE and READWRITE macro +// +class CSerActionGetSerializeSize { }; +class CSerActionSerialize { }; +class CSerActionUnserialize { }; + +template +inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionGetSerializeSize ser_action) +{ + return ::GetSerializeSize(obj, nType, nVersion); +} + +template +inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionSerialize ser_action) +{ + ::Serialize(s, obj, nType, nVersion); + return 0; +} + +template +inline unsigned int SerReadWrite(Stream& s, T& obj, int nType, int nVersion, CSerActionUnserialize ser_action) +{ + ::Unserialize(s, obj, nType, nVersion); + return 0; +} + +struct ser_streamplaceholder +{ + int nType; + int nVersion; +}; + + + + + + + + + +// +// Allocator that clears its contents before deletion +// +template +struct secure_allocator : public std::allocator +{ + // MSVC8 default copy constructor is broken + typedef std::allocator base; + typedef typename base::size_type size_type; + typedef typename base::difference_type difference_type; + typedef typename base::pointer pointer; + typedef typename base::const_pointer const_pointer; + typedef typename base::reference reference; + typedef typename base::const_reference const_reference; + typedef typename base::value_type value_type; + secure_allocator() throw() {} + secure_allocator(const secure_allocator& a) throw() : base(a) {} + template + secure_allocator(const secure_allocator& a) throw() : base(a) {} + ~secure_allocator() throw() {} + template struct rebind + { typedef secure_allocator<_Other> other; }; + + void deallocate(T* p, std::size_t n) + { + if (p != NULL) + memset(p, 0, sizeof(T) * n); + std::allocator::deallocate(p, n); + } +}; + + + +// +// Double ended buffer combining vector and stream-like interfaces. +// >> and << read and write unformatted data using the above serialization templates. +// Fills with data in linear time; some stringstream implementations take N^2 time. +// +class CDataStream +{ +protected: + typedef std::vector > vector_type; + vector_type vch; + unsigned int nReadPos; + short state; + short exceptmask; +public: + int nType; + int nVersion; + + typedef vector_type::allocator_type allocator_type; + typedef vector_type::size_type size_type; + typedef vector_type::difference_type difference_type; + typedef vector_type::reference reference; + typedef vector_type::const_reference const_reference; + typedef vector_type::value_type value_type; + typedef vector_type::iterator iterator; + typedef vector_type::const_iterator const_iterator; + typedef vector_type::reverse_iterator reverse_iterator; + + explicit CDataStream(int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(pbegin, pend) + { + Init(nTypeIn, nVersionIn); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + CDataStream(const char* pbegin, const char* pend, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(pbegin, pend) + { + Init(nTypeIn, nVersionIn); + } +#endif + + CDataStream(const vector_type& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end()) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end()) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch((char*)&vchIn.begin()[0], (char*)&vchIn.end()[0]) + { + Init(nTypeIn, nVersionIn); + } + + void Init(int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) + { + nReadPos = 0; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = std::ios::badbit | std::ios::failbit; + } + + CDataStream& operator+=(const CDataStream& b) + { + vch.insert(vch.end(), b.begin(), b.end()); + return *this; + } + + friend CDataStream operator+(const CDataStream& a, const CDataStream& b) + { + CDataStream ret = a; + ret += b; + return (ret); + } + + std::string str() const + { + return (std::string(begin(), end())); + } + + + // + // Vector subset + // + const_iterator begin() const { return vch.begin() + nReadPos; } + iterator begin() { return vch.begin() + nReadPos; } + const_iterator end() const { return vch.end(); } + iterator end() { return vch.end(); } + size_type size() const { return vch.size() - nReadPos; } + bool empty() const { return vch.size() == nReadPos; } + void resize(size_type n, value_type c=0) { vch.resize(n + nReadPos, c); } + void reserve(size_type n) { vch.reserve(n + nReadPos); } + const_reference operator[](size_type pos) const { return vch[pos + nReadPos]; } + reference operator[](size_type pos) { return vch[pos + nReadPos]; } + void clear() { vch.clear(); nReadPos = 0; } + iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); } + void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); } + + void insert(iterator it, const_iterator first, const_iterator last) + { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } + else + vch.insert(it, first, last); + } + + void insert(iterator it, std::vector::const_iterator first, std::vector::const_iterator last) + { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } + else + vch.insert(it, first, last); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + void insert(iterator it, const char* first, const char* last) + { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } + else + vch.insert(it, first, last); + } +#endif + + iterator erase(iterator it) + { + if (it == vch.begin() + nReadPos) + { + // special case for erasing from the front + if (++nReadPos >= vch.size()) + { + // whenever we reach the end, we take the opportunity to clear the buffer + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } + return vch.begin() + nReadPos; + } + else + return vch.erase(it); + } + + iterator erase(iterator first, iterator last) + { + if (first == vch.begin() + nReadPos) + { + // special case for erasing from the front + if (last == vch.end()) + { + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } + else + { + nReadPos = (last - vch.begin()); + return last; + } + } + else + return vch.erase(first, last); + } + + inline void Compact() + { + vch.erase(vch.begin(), vch.begin() + nReadPos); + nReadPos = 0; + } + + bool Rewind(size_type n) + { + // Rewind by n characters if the buffer hasn't been compacted yet + if (n > nReadPos) + return false; + nReadPos -= n; + return true; + } + + + // + // Stream subset + // + void setstate(short bits, const char* psz) + { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + bool eof() const { return size() == 0; } + bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } + bool good() const { return !eof() && (state == 0); } + void clear(short n) { state = n; } // name conflict with vector clear() + short exceptions() { return exceptmask; } + short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CDataStream"); return prev; } + CDataStream* rdbuf() { return this; } + int in_avail() { return size(); } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CDataStream& read(char* pch, int nSize) + { + // Read from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) + { + if (nReadPosNext > vch.size()) + { + setstate(std::ios::failbit, "CDataStream::read() : end of data"); + memset(pch, 0, nSize); + nSize = vch.size() - nReadPos; + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = 0; + vch.clear(); + return (*this); + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream& ignore(int nSize) + { + // Ignore from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) + { + if (nReadPosNext > vch.size()) + { + setstate(std::ios::failbit, "CDataStream::ignore() : end of data"); + nSize = vch.size() - nReadPos; + } + nReadPos = 0; + vch.clear(); + return (*this); + } + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream& write(const char* pch, int nSize) + { + // Write to the end of the buffer + assert(nSize >= 0); + vch.insert(vch.end(), pch, pch + nSize); + return (*this); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const + { + // Special case: stream << stream concatenates like stream += stream + if (!vch.empty()) + s.write((char*)&vch[0], vch.size() * sizeof(vch[0])); + } + + template + unsigned int GetSerializeSize(const T& obj) + { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template + CDataStream& operator<<(const T& obj) + { + // Serialize to this stream + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template + CDataStream& operator>>(T& obj) + { + // Unserialize from this stream + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } +}; + +#ifdef TESTCDATASTREAM +// VC6sp6 +// CDataStream: +// n=1000 0 seconds +// n=2000 0 seconds +// n=4000 0 seconds +// n=8000 0 seconds +// n=16000 0 seconds +// n=32000 0 seconds +// n=64000 1 seconds +// n=128000 1 seconds +// n=256000 2 seconds +// n=512000 4 seconds +// n=1024000 8 seconds +// n=2048000 16 seconds +// n=4096000 32 seconds +// stringstream: +// n=1000 1 seconds +// n=2000 1 seconds +// n=4000 13 seconds +// n=8000 87 seconds +// n=16000 400 seconds +// n=32000 1660 seconds +// n=64000 6749 seconds +// n=128000 27241 seconds +// n=256000 109804 seconds +#include +int main(int argc, char *argv[]) +{ + vector vch(0xcc, 250); + printf("CDataStream:\n"); + for (int n = 1000; n <= 4500000; n *= 2) + { + CDataStream ss; + time_t nStart = time(NULL); + for (int i = 0; i < n; i++) + ss.write((char*)&vch[0], vch.size()); + printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); + } + printf("stringstream:\n"); + for (int n = 1000; n <= 4500000; n *= 2) + { + stringstream ss; + time_t nStart = time(NULL); + for (int i = 0; i < n; i++) + ss.write((char*)&vch[0], vch.size()); + printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); + } +} +#endif + + + + + + + + + + +// +// Automatic closing wrapper for FILE* +// - Will automatically close the file when it goes out of scope if not null. +// - If you're returning the file pointer, return file.release(). +// - If you need to close the file early, use file.fclose() instead of fclose(file). +// +class CAutoFile +{ +protected: + FILE* file; + short state; + short exceptmask; +public: + int nType; + int nVersion; + + typedef FILE element_type; + + CAutoFile(FILE* filenew=NULL, int nTypeIn=SER_DISK, int nVersionIn=VERSION) + { + file = filenew; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = std::ios::badbit | std::ios::failbit; + } + + ~CAutoFile() + { + fclose(); + } + + void fclose() + { + if (file != NULL && file != stdin && file != stdout && file != stderr) + ::fclose(file); + file = NULL; + } + + FILE* release() { FILE* ret = file; file = NULL; return ret; } + operator FILE*() { return file; } + FILE* operator->() { return file; } + FILE& operator*() { return *file; } + FILE** operator&() { return &file; } + FILE* operator=(FILE* pnew) { return file = pnew; } + bool operator!() { return (file == NULL); } + + + // + // Stream subset + // + void setstate(short bits, const char* psz) + { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } + bool good() const { return state == 0; } + void clear(short n = 0) { state = n; } + short exceptions() { return exceptmask; } + short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CAutoFile"); return prev; } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CAutoFile& read(char* pch, int nSize) + { + if (!file) + throw std::ios_base::failure("CAutoFile::read : file handle is NULL"); + if (fread(pch, 1, nSize, file) != nSize) + setstate(std::ios::failbit, feof(file) ? "CAutoFile::read : end of file" : "CAutoFile::read : fread failed"); + return (*this); + } + + CAutoFile& write(const char* pch, int nSize) + { + if (!file) + throw std::ios_base::failure("CAutoFile::write : file handle is NULL"); + if (fwrite(pch, 1, nSize, file) != nSize) + setstate(std::ios::failbit, "CAutoFile::write : write failed"); + return (*this); + } + + template + unsigned int GetSerializeSize(const T& obj) + { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template + CAutoFile& operator<<(const T& obj) + { + // Serialize to this stream + if (!file) + throw std::ios_base::failure("CAutoFile::operator<< : file handle is NULL"); + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template + CAutoFile& operator>>(T& obj) + { + // Unserialize from this stream + if (!file) + throw std::ios_base::failure("CAutoFile::operator>> : file handle is NULL"); + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } +}; + +#endif diff --git a/core/include/uint256.h b/core/include/uint256.h new file mode 100644 index 0000000000..14feb1683d --- /dev/null +++ b/core/include/uint256.h @@ -0,0 +1,765 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UINT256_H +#define BITCOIN_UINT256_H + +#include "serialize.h" + +#include +#include +#include + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false) ; else for +#endif + + +inline int Testuint256AdHoc(std::vector vArg); + + + +// We have to keep a separate base class without constructors +// so the compiler will let us use it in a union +template +class base_uint +{ +protected: + enum { WIDTH=BITS/32 }; + unsigned int pn[WIDTH]; +public: + + bool operator!() const + { + for (int i = 0; i < WIDTH; i++) + if (pn[i] != 0) + return false; + return true; + } + + const base_uint operator~() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + return ret; + } + + const base_uint operator-() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + ret++; + return ret; + } + + + base_uint& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + base_uint& operator^=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] ^= b.pn[i]; + return *this; + } + + base_uint& operator&=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] &= b.pn[i]; + return *this; + } + + base_uint& operator|=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] |= b.pn[i]; + return *this; + } + + base_uint& operator^=(uint64 b) + { + pn[0] ^= (unsigned int)b; + pn[1] ^= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator&=(uint64 b) + { + pn[0] &= (unsigned int)b; + pn[1] &= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator|=(uint64 b) + { + pn[0] |= (unsigned int)b; + pn[1] |= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator<<=(unsigned int shift) + { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i+k+1 < WIDTH && shift != 0) + pn[i+k+1] |= (a.pn[i] >> (32-shift)); + if (i+k < WIDTH) + pn[i+k] |= (a.pn[i] << shift); + } + return *this; + } + + base_uint& operator>>=(unsigned int shift) + { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i-k-1 >= 0 && shift != 0) + pn[i-k-1] |= (a.pn[i] << (32-shift)); + if (i-k >= 0) + pn[i-k] |= (a.pn[i] >> shift); + } + return *this; + } + + base_uint& operator+=(const base_uint& b) + { + uint64 carry = 0; + for (int i = 0; i < WIDTH; i++) + { + uint64 n = carry + pn[i] + b.pn[i]; + pn[i] = n & 0xffffffff; + carry = n >> 32; + } + return *this; + } + + base_uint& operator-=(const base_uint& b) + { + *this += -b; + return *this; + } + + base_uint& operator+=(uint64 b64) + { + base_uint b; + b = b64; + *this += b; + return *this; + } + + base_uint& operator-=(uint64 b64) + { + base_uint b; + b = b64; + *this += -b; + return *this; + } + + + base_uint& operator++() + { + // prefix operator + int i = 0; + while (++pn[i] == 0 && i < WIDTH-1) + i++; + return *this; + } + + const base_uint operator++(int) + { + // postfix operator + const base_uint ret = *this; + ++(*this); + return ret; + } + + base_uint& operator--() + { + // prefix operator + int i = 0; + while (--pn[i] == -1 && i < WIDTH-1) + i++; + return *this; + } + + const base_uint operator--(int) + { + // postfix operator + const base_uint ret = *this; + --(*this); + return ret; + } + + + friend inline bool operator<(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator<=(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator>(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator>=(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator==(const base_uint& a, const base_uint& b) + { + for (int i = 0; i < base_uint::WIDTH; i++) + if (a.pn[i] != b.pn[i]) + return false; + return true; + } + + friend inline bool operator==(const base_uint& a, uint64 b) + { + if (a.pn[0] != (unsigned int)b) + return false; + if (a.pn[1] != (unsigned int)(b >> 32)) + return false; + for (int i = 2; i < base_uint::WIDTH; i++) + if (a.pn[i] != 0) + return false; + return true; + } + + friend inline bool operator!=(const base_uint& a, const base_uint& b) + { + return (!(a == b)); + } + + friend inline bool operator!=(const base_uint& a, uint64 b) + { + return (!(a == b)); + } + + + + std::string GetHex() const + { + char psz[sizeof(pn)*2 + 1]; + for (int i = 0; i < sizeof(pn); i++) + sprintf(psz + i*2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]); + return std::string(psz, psz + sizeof(pn)*2); + } + + void SetHex(const char* psz) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + + // skip leading spaces + while (isspace(*psz)) + psz++; + + // skip 0x + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + + // hex string to uint + static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; + const char* pbegin = psz; + while (phexdigit[*psz] || *psz == '0') + psz++; + psz--; + unsigned char* p1 = (unsigned char*)pn; + unsigned char* pend = p1 + WIDTH * 4; + while (psz >= pbegin && p1 < pend) + { + *p1 = phexdigit[(unsigned char)*psz--]; + if (psz >= pbegin) + { + *p1 |= (phexdigit[(unsigned char)*psz--] << 4); + p1++; + } + } + } + + void SetHex(const std::string& str) + { + SetHex(str.c_str()); + } + + std::string ToString() const + { + return (GetHex()); + } + + unsigned char* begin() + { + return (unsigned char*)&pn[0]; + } + + unsigned char* end() + { + return (unsigned char*)&pn[WIDTH]; + } + + unsigned int size() + { + return sizeof(pn); + } + + + unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const + { + return sizeof(pn); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const + { + s.write((char*)pn, sizeof(pn)); + } + + template + void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) + { + s.read((char*)pn, sizeof(pn)); + } + + + friend class uint160; + friend class uint256; + friend inline int Testuint256AdHoc(std::vector vArg); +}; + +typedef base_uint<160> base_uint160; +typedef base_uint<256> base_uint256; + + + +// +// uint160 and uint256 could be implemented as templates, but to keep +// compile errors and debugging cleaner, they're copy and pasted. +// + + + +////////////////////////////////////////////////////////////////////////////// +// +// uint160 +// + +class uint160 : public base_uint160 +{ +public: + typedef base_uint160 basetype; + + uint160() + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint160(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint160& operator=(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint160(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint160& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint160(const std::string& str) + { + SetHex(str); + } + + explicit uint160(const std::vector& vch) + { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint160& a, uint64 b) { return (base_uint160)a == b; } +inline bool operator!=(const uint160& a, uint64 b) { return (base_uint160)a != b; } +inline const uint160 operator<<(const base_uint160& a, unsigned int shift) { return uint160(a) <<= shift; } +inline const uint160 operator>>(const base_uint160& a, unsigned int shift) { return uint160(a) >>= shift; } +inline const uint160 operator<<(const uint160& a, unsigned int shift) { return uint160(a) <<= shift; } +inline const uint160 operator>>(const uint160& a, unsigned int shift) { return uint160(a) >>= shift; } + +inline const uint160 operator^(const base_uint160& a, const base_uint160& b) { return uint160(a) ^= b; } +inline const uint160 operator&(const base_uint160& a, const base_uint160& b) { return uint160(a) &= b; } +inline const uint160 operator|(const base_uint160& a, const base_uint160& b) { return uint160(a) |= b; } +inline const uint160 operator+(const base_uint160& a, const base_uint160& b) { return uint160(a) += b; } +inline const uint160 operator-(const base_uint160& a, const base_uint160& b) { return uint160(a) -= b; } + +inline bool operator<(const base_uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const base_uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const base_uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const base_uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const base_uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const base_uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const base_uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const base_uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const base_uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const base_uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const base_uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160& a, const base_uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160& a, const base_uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160& a, const base_uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160& a, const base_uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160& a, const base_uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160& a, const base_uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160& a, const base_uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160& a, const base_uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160& a, const base_uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160& a, const base_uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160& a, const base_uint160& b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// uint256 +// + +class uint256 : public base_uint256 +{ +public: + typedef base_uint256 basetype; + + uint256() + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint256(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint256& operator=(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint256(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint256& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint256(const std::string& str) + { + SetHex(str); + } + + explicit uint256(const std::vector& vch) + { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint256& a, uint64 b) { return (base_uint256)a == b; } +inline bool operator!=(const uint256& a, uint64 b) { return (base_uint256)a != b; } +inline const uint256 operator<<(const base_uint256& a, unsigned int shift) { return uint256(a) <<= shift; } +inline const uint256 operator>>(const base_uint256& a, unsigned int shift) { return uint256(a) >>= shift; } +inline const uint256 operator<<(const uint256& a, unsigned int shift) { return uint256(a) <<= shift; } +inline const uint256 operator>>(const uint256& a, unsigned int shift) { return uint256(a) >>= shift; } + +inline const uint256 operator^(const base_uint256& a, const base_uint256& b) { return uint256(a) ^= b; } +inline const uint256 operator&(const base_uint256& a, const base_uint256& b) { return uint256(a) &= b; } +inline const uint256 operator|(const base_uint256& a, const base_uint256& b) { return uint256(a) |= b; } +inline const uint256 operator+(const base_uint256& a, const base_uint256& b) { return uint256(a) += b; } +inline const uint256 operator-(const base_uint256& a, const base_uint256& b) { return uint256(a) -= b; } + +inline bool operator<(const base_uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const base_uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const base_uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const base_uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const base_uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const base_uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const base_uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const base_uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const base_uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const base_uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const base_uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } + +inline bool operator<(const uint256& a, const base_uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const uint256& a, const base_uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const uint256& a, const base_uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const uint256& a, const base_uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const uint256& a, const base_uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const uint256& a, const base_uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const uint256& a, const base_uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const uint256& a, const base_uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const uint256& a, const base_uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const uint256& a, const base_uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const uint256& a, const base_uint256& b) { return (base_uint256)a - (base_uint256)b; } + +inline bool operator<(const uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } + + + + + + + + + + + + +inline int Testuint256AdHoc(std::vector vArg) +{ + uint256 g(0); + + + printf("%s\n", g.ToString().c_str()); + g--; printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g--; printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + + + + uint256 a(7); + printf("a=7\n"); + printf("%s\n", a.ToString().c_str()); + + uint256 b; + printf("b undefined\n"); + printf("%s\n", b.ToString().c_str()); + int c = 3; + + a = c; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + uint256 k(c); + + a = 5; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + b = 1; + b <<= 52; + + a |= b; + + a ^= 0x500; + + printf("a %s\n", a.ToString().c_str()); + + a = a | b | (uint256)0x1000; + + + printf("a %s\n", a.ToString().c_str()); + printf("b %s\n", b.ToString().c_str()); + + a = 0xfffffffe; + a.pn[4] = 9; + + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + uint256 d = a--; + printf("%s\n", d.ToString().c_str()); + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + + d = a; + + printf("%s\n", d.ToString().c_str()); + for (int i = uint256::WIDTH-1; i >= 0; i--) printf("%08x", d.pn[i]); printf("\n"); + + uint256 neg = d; + neg = ~neg; + printf("%s\n", neg.ToString().c_str()); + + + uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + printf("\n"); + printf("%s\n", e.ToString().c_str()); + + + printf("\n"); + uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + uint256 x2; + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) + { + x2 = x1 << i; + printf("%s\n", x2.ToString().c_str()); + } + + printf("\n"); + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) + { + x2 = x1; + x2 >>= i; + printf("%s\n", x2.ToString().c_str()); + } + + + for (int i = 0; i < 100; i++) + { + uint256 k = (~uint256(0) >> i); + printf("%s\n", k.ToString().c_str()); + } + + for (int i = 0; i < 100; i++) + { + uint256 k = (~uint256(0) << i); + printf("%s\n", k.ToString().c_str()); + } + + return (0); +} + +#endif diff --git a/core/include/util.h b/core/include/util.h new file mode 100644 index 0000000000..e25004fa1c --- /dev/null +++ b/core/include/util.h @@ -0,0 +1,679 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UTIL_H +#define BITCOIN_UTIL_H + +#include "uint256.h" +//#include "cryptopp/sha.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false) ; else for +#endif +#ifndef _MSC_VER +#define __forceinline inline +#endif + +//#define foreach BOOST_FOREACH +#define loop for (;;) +#define BEGIN(a) ((char*)&(a)) +#define END(a) ((char*)&((&(a))[1])) +#define UBEGIN(a) ((unsigned char*)&(a)) +#define UEND(a) ((unsigned char*)&((&(a))[1])) +#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) +#define printf OutputDebugStringF + +#ifdef snprintf +#undef snprintf +#endif +#define snprintf my_snprintf + +#ifndef PRI64d +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MSVCRT__) +#define PRI64d "I64d" +#define PRI64u "I64u" +#define PRI64x "I64x" +#else +#define PRI64d "lld" +#define PRI64u "llu" +#define PRI64x "llx" +#endif +#endif + +// This is needed because the foreach macro can't get over the comma in pair +#define PAIRTYPE(t1, t2) pair + +// Used to bypass the rule against non-const reference to temporary +// where it makes sense with wrappers such as CFlatData or CTxDB +template +inline T& REF(const T& val) +{ + return (T&)val; +} + +// Align by increasing pointer, must have extra space at end of buffer +template +T* alignup(T* p) +{ + union + { + T* ptr; + size_t n; + } u; + u.ptr = p; + u.n = (u.n + (nBytes-1)) & ~(nBytes-1); + return u.ptr; +} + +#ifdef __WXMSW__ +#define MSG_NOSIGNAL 0 +#define MSG_DONTWAIT 0 +#ifndef UINT64_MAX +#define UINT64_MAX _UI64_MAX +#define INT64_MAX _I64_MAX +#define INT64_MIN _I64_MIN +#endif +#ifndef S_IRUSR +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#endif +#define unlink _unlink +typedef int socklen_t; +#else +#define WSAGetLastError() errno +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEMSGSIZE EMSGSIZE +#define WSAEINTR EINTR +#define WSAEINPROGRESS EINPROGRESS +#define WSAEADDRINUSE EADDRINUSE +#define WSAENOTSOCK EBADF +#define INVALID_SOCKET (SOCKET)(~0) +#define SOCKET_ERROR -1 +typedef u_int SOCKET; +#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) +#define strlwr(psz) to_lower(psz) +#define _strlwr(psz) to_lower(psz) +#define MAX_PATH 1024 +#define Beep(n1,n2) (0) +inline void Sleep(int64 n) +{ + boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(n)); +} +#endif + +inline int myclosesocket(SOCKET& hSocket) +{ + if (hSocket == INVALID_SOCKET) + return WSAENOTSOCK; +#ifdef __WXMSW__ + int ret = closesocket(hSocket); +#else + int ret = close(hSocket); +#endif + hSocket = INVALID_SOCKET; + return ret; +} +#define closesocket(s) myclosesocket(s) + +#ifndef GUI +inline const char* _(const char* psz) +{ + return psz; +} +#endif + + + + + + + + + + +extern std::map mapArgs; +extern std::map > mapMultiArgs; +extern bool fDebug; +extern bool fPrintToConsole; +extern bool fPrintToDebugger; +extern char pszSetDataDir[MAX_PATH]; +extern bool fRequestShutdown; +extern bool fShutdown; +extern bool fDaemon; +extern bool fServer; +extern bool fCommandLine; +extern std::string strMiscWarning; +extern bool fTestNet; +extern bool fNoListen; +extern bool fLogTimestamps; + +void RandAddSeed(); +void RandAddSeedPerfmon(); +int OutputDebugStringF(const char* pszFormat, ...); +int my_snprintf(char* buffer, size_t limit, const char* format, ...); +std::string strprintf(const char* format, ...); +bool error(const char* format, ...); +void LogException(std::exception* pex, const char* pszThread); +void PrintException(std::exception* pex, const char* pszThread); +void PrintExceptionContinue(std::exception* pex, const char* pszThread); +void ParseString(const std::string& str, char c, std::vector& v); +std::string FormatMoney(int64 n, bool fPlus=false); +bool ParseMoney(const std::string& str, int64& nRet); +bool ParseMoney(const char* pszIn, int64& nRet); +std::vector ParseHex(const char* psz); +std::vector ParseHex(const std::string& str); +void ParseParameters(int argc, char* argv[]); +const char* wxGetTranslation(const char* psz); +bool WildcardMatch(const char* psz, const char* mask); +bool WildcardMatch(const std::string& str, const std::string& mask); +int GetFilesize(FILE* file); +void GetDataDir(char* pszDirRet); +std::string GetConfigFile(); +std::string GetPidFile(); +void CreatePidFile(std::string pidFile, pid_t pid); +void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); +#ifdef __WXMSW__ +string MyGetSpecialFolderPath(int nFolder, bool fCreate); +#endif +std::string GetDefaultDataDir(); +std::string GetDataDir(); +void ShrinkDebugFile(); +int GetRandInt(int nMax); +uint64 GetRand(uint64 nMax); +int64 GetTime(); +int64 GetAdjustedTime(); +void AddTimeData(unsigned int ip, int64 nTime); +std::string FormatFullVersion(); + + + + + + + + + + + + + +// Wrapper to automatically initialize critical sections +class CCriticalSection +{ +#ifdef __WXMSW__ +protected: + CRITICAL_SECTION cs; +public: + explicit CCriticalSection() { InitializeCriticalSection(&cs); } + ~CCriticalSection() { DeleteCriticalSection(&cs); } + void Enter() { EnterCriticalSection(&cs); } + void Leave() { LeaveCriticalSection(&cs); } + bool TryEnter() { return TryEnterCriticalSection(&cs); } +#else +protected: + boost::interprocess::interprocess_recursive_mutex mutex; +public: + explicit CCriticalSection() { } + ~CCriticalSection() { } + void Enter() { mutex.lock(); } + void Leave() { mutex.unlock(); } + bool TryEnter() { return mutex.try_lock(); } +#endif +public: + const char* pszFile; + int nLine; +}; + +// Automatically leave critical section when leaving block, needed for exception safety +class CCriticalBlock +{ +protected: + CCriticalSection* pcs; +public: + CCriticalBlock(CCriticalSection& csIn) { pcs = &csIn; pcs->Enter(); } + ~CCriticalBlock() { pcs->Leave(); } +}; + +// WARNING: This will catch continue and break! +// break is caught with an assertion, but there's no way to detect continue. +// I'd rather be careful than suffer the other more error prone syntax. +// The compiler will optimise away all this loop junk. +#define CRITICAL_BLOCK(cs) \ + for (bool fcriticalblockonce=true; fcriticalblockonce; assert(("break caught by CRITICAL_BLOCK!", !fcriticalblockonce)), fcriticalblockonce=false) \ + for (CCriticalBlock criticalblock(cs); fcriticalblockonce && (cs.pszFile=__FILE__, cs.nLine=__LINE__, true); fcriticalblockonce=false, cs.pszFile=NULL, cs.nLine=0) + +class CTryCriticalBlock +{ +protected: + CCriticalSection* pcs; +public: + CTryCriticalBlock(CCriticalSection& csIn) { pcs = (csIn.TryEnter() ? &csIn : NULL); } + ~CTryCriticalBlock() { if (pcs) pcs->Leave(); } + bool Entered() { return pcs != NULL; } +}; + +#define TRY_CRITICAL_BLOCK(cs) \ + for (bool fcriticalblockonce=true; fcriticalblockonce; assert(("break caught by TRY_CRITICAL_BLOCK!", !fcriticalblockonce)), fcriticalblockonce=false) \ + for (CTryCriticalBlock criticalblock(cs); fcriticalblockonce && (fcriticalblockonce = criticalblock.Entered()) && (cs.pszFile=__FILE__, cs.nLine=__LINE__, true); fcriticalblockonce=false, cs.pszFile=NULL, cs.nLine=0) + + + + + + + + + + +inline std::string i64tostr(int64 n) +{ + return strprintf("%"PRI64d, n); +} + +inline std::string itostr(int n) +{ + return strprintf("%d", n); +} + +inline int64 atoi64(const char* psz) +{ +#ifdef _MSC_VER + return _atoi64(psz); +#else + return strtoll(psz, NULL, 10); +#endif +} + +inline int64 atoi64(const std::string& str) +{ +#ifdef _MSC_VER + return _atoi64(str.c_str()); +#else + return strtoll(str.c_str(), NULL, 10); +#endif +} + +inline int atoi(const std::string& str) +{ + return atoi(str.c_str()); +} + +inline int roundint(double d) +{ + return (int)(d > 0 ? d + 0.5 : d - 0.5); +} + +inline int64 roundint64(double d) +{ + return (int64)(d > 0 ? d + 0.5 : d - 0.5); +} + +inline int64 abs64(int64 n) +{ + return (n >= 0 ? n : -n); +} + +template +std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) +{ + if (itbegin == itend) + return ""; + const unsigned char* pbegin = (const unsigned char*)&itbegin[0]; + const unsigned char* pend = pbegin + (itend - itbegin) * sizeof(itbegin[0]); + std::string str; + str.reserve((pend-pbegin) * (fSpaces ? 3 : 2)); + for (const unsigned char* p = pbegin; p != pend; p++) + str += strprintf((fSpaces && p != pend-1 ? "%02x " : "%02x"), *p); + return str; +} + +inline std::string HexStr(const std::vector& vch, bool fSpaces=false) +{ + return HexStr(vch.begin(), vch.end(), fSpaces); +} + +template +std::string HexNumStr(const T itbegin, const T itend, bool f0x=true) +{ + if (itbegin == itend) + return ""; + const unsigned char* pbegin = (const unsigned char*)&itbegin[0]; + const unsigned char* pend = pbegin + (itend - itbegin) * sizeof(itbegin[0]); + std::string str = (f0x ? "0x" : ""); + str.reserve(str.size() + (pend-pbegin) * 2); + for (const unsigned char* p = pend-1; p >= pbegin; p--) + str += strprintf("%02x", *p); + return str; +} + +inline std::string HexNumStr(const std::vector& vch, bool f0x=true) +{ + return HexNumStr(vch.begin(), vch.end(), f0x); +} + +template +void PrintHex(const T pbegin, const T pend, const char* pszFormat="%s", bool fSpaces=true) +{ + printf(pszFormat, HexStr(pbegin, pend, fSpaces).c_str()); +} + +inline void PrintHex(const std::vector& vch, const char* pszFormat="%s", bool fSpaces=true) +{ + printf(pszFormat, HexStr(vch, fSpaces).c_str()); +} + +inline int64 GetPerformanceCounter() +{ + int64 nCounter = 0; +#ifdef __WXMSW__ + QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); +#else + timeval t; + gettimeofday(&t, NULL); + nCounter = t.tv_sec * 1000000 + t.tv_usec; +#endif + return nCounter; +} + +inline int64 GetTimeMillis() +{ + return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - + boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); +} + +inline std::string DateTimeStrFormat(const char* pszFormat, int64 nTime) +{ + time_t n = nTime; + struct tm* ptmTime = gmtime(&n); + char pszTime[200]; + strftime(pszTime, sizeof(pszTime), pszFormat, ptmTime); + return pszTime; +} + +template +void skipspaces(T& it) +{ + while (isspace(*it)) + ++it; +} + +inline bool IsSwitchChar(char c) +{ +#ifdef __WXMSW__ + return c == '-' || c == '/'; +#else + return c == '-'; +#endif +} + +inline std::string GetArg(const std::string& strArg, const std::string& strDefault) +{ + if (mapArgs.count(strArg)) + return mapArgs[strArg]; + return strDefault; +} + +inline int64 GetArg(const std::string& strArg, int64 nDefault) +{ + if (mapArgs.count(strArg)) + return atoi64(mapArgs[strArg]); + return nDefault; +} + +inline bool GetBoolArg(const std::string& strArg) +{ + if (mapArgs.count(strArg)) + { + if (mapArgs[strArg].empty()) + return true; + return (atoi(mapArgs[strArg]) != 0); + } + return false; +} + + + + + + + + + + +inline void heapchk() +{ +#ifdef __WXMSW__ + /// for debugging + //if (_heapchk() != _HEAPOK) + // DebugBreak(); +#endif +} + +// Randomize the stack to help protect against buffer overrun exploits +#define IMPLEMENT_RANDOMIZE_STACK(ThreadFn) \ + { \ + static char nLoops; \ + if (nLoops <= 0) \ + nLoops = GetRand(20) + 1; \ + if (nLoops-- > 1) \ + { \ + ThreadFn; \ + return; \ + } \ + } + +#define CATCH_PRINT_EXCEPTION(pszFn) \ + catch (std::exception& e) { \ + PrintException(&e, (pszFn)); \ + } catch (...) { \ + PrintException(NULL, (pszFn)); \ + } + + + + + + + + + + +template +inline uint256 Hash(const T1 pbegin, const T1 pend) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +inline uint256 Hash(const T1 p1begin, const T1 p1end, + const T2 p2begin, const T2 p2end) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); + SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); + SHA256_Final((unsigned char*)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +inline uint256 Hash(const T1 p1begin, const T1 p1end, + const T2 p2begin, const T2 p2end, + const T3 p3begin, const T3 p3end) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); + SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); + SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0])); + SHA256_Final((unsigned char*)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=VERSION) +{ + // Most of the time is spent allocating and deallocating CDataStream's + // buffer. If this ever needs to be optimized further, make a CStaticStream + // class with its buffer on the stack. + CDataStream ss(nType, nVersion); + ss.reserve(10000); + ss << obj; + return Hash(ss.begin(), ss.end()); +} + +inline uint160 Hash160(const std::vector& vch) +{ + uint256 hash1; + SHA256(&vch[0], vch.size(), (unsigned char*)&hash1); + uint160 hash2; + RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + + + + + + + + + + + +// Note: It turns out we might have been able to use boost::thread +// by using TerminateThread(boost::thread.native_handle(), 0); +#ifdef __WXMSW__ +typedef HANDLE pthread_t; + +inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) +{ + DWORD nUnused = 0; + HANDLE hthread = + CreateThread( + NULL, // default security + 0, // inherit stack size from parent + (LPTHREAD_START_ROUTINE)pfn, // function pointer + parg, // argument + 0, // creation option, start immediately + &nUnused); // thread identifier + if (hthread == NULL) + { + printf("Error: CreateThread() returned %d\n", GetLastError()); + return (pthread_t)0; + } + if (!fWantHandle) + { + CloseHandle(hthread); + return (pthread_t)-1; + } + return hthread; +} + +inline void SetThreadPriority(int nPriority) +{ + SetThreadPriority(GetCurrentThread(), nPriority); +} +#else +inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) +{ + pthread_t hthread = 0; + int ret = pthread_create(&hthread, NULL, (void*(*)(void*))pfn, parg); + if (ret != 0) + { + printf("Error: pthread_create() returned %d\n", ret); + return (pthread_t)0; + } + if (!fWantHandle) + return (pthread_t)-1; + return hthread; +} + +#define THREAD_PRIORITY_LOWEST PRIO_MAX +#define THREAD_PRIORITY_BELOW_NORMAL 2 +#define THREAD_PRIORITY_NORMAL 0 +#define THREAD_PRIORITY_ABOVE_NORMAL 0 + +inline void SetThreadPriority(int nPriority) +{ + // It's unclear if it's even possible to change thread priorities on Linux, + // but we really and truly need it for the generation threads. +#ifdef PRIO_THREAD + setpriority(PRIO_THREAD, 0, nPriority); +#else + setpriority(PRIO_PROCESS, 0, nPriority); +#endif +} + +inline bool TerminateThread(pthread_t hthread, unsigned int nExitCode) +{ + return (pthread_cancel(hthread) == 0); +} + +inline void ExitThread(unsigned int nExitCode) +{ + pthread_exit((void*)nExitCode); +} +#endif + + + + + +inline bool AffinityBugWorkaround(void(*pfn)(void*)) +{ +#ifdef __WXMSW__ + // Sometimes after a few hours affinity gets stuck on one processor + DWORD dwProcessAffinityMask = -1; + DWORD dwSystemAffinityMask = -1; + GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask); + DWORD dwPrev1 = SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinityMask); + DWORD dwPrev2 = SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinityMask); + if (dwPrev2 != dwProcessAffinityMask) + { + printf("AffinityBugWorkaround() : SetThreadAffinityMask=%d, ProcessAffinityMask=%d, restarting thread\n", dwPrev2, dwProcessAffinityMask); + if (!CreateThread(pfn, NULL)) + printf("Error: CreateThread() failed\n"); + return true; + } +#endif + return false; +} + +#endif diff --git a/lib/include/base58.h b/lib/include/base58.h deleted file mode 100644 index 7acdf63ae3..0000000000 --- a/lib/include/base58.h +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. - - -// -// Why base-58 instead of standard base-64 encoding? -// - Don't want 0OIl characters that look the same in some fonts and -// could be used to create visually identical looking account numbers. -// - A std::string with non-alphanumeric characters is not as easily accepted as an account number. -// - E-mail usually won't line-break if there's no punctuation to break at. -// - Doubleclicking selects the whole number as one word if it's all alphanumeric. -// -#ifndef BITCOIN_BASE58_H -#define BITCOIN_BASE58_H - -#include -#include -#include "bignum.h" - -static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - - -inline std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) -{ - CAutoBN_CTX pctx; - CBigNum bn58 = 58; - CBigNum bn0 = 0; - - // Convert big endian data to little endian - // Extra zero at the end make sure bignum will interpret as a positive number - std::vector vchTmp(pend-pbegin+1, 0); - reverse_copy(pbegin, pend, vchTmp.begin()); - - // Convert little endian data to bignum - CBigNum bn; - bn.setvch(vchTmp); - - // Convert bignum to std::string - std::string str; - str.reserve((pend - pbegin) * 138 / 100 + 1); - CBigNum dv; - CBigNum rem; - while (bn > bn0) - { - if (!BN_div(&dv, &rem, &bn, &bn58, pctx)) - throw bignum_error("EncodeBase58 : BN_div failed"); - bn = dv; - unsigned int c = rem.getulong(); - str += pszBase58[c]; - } - - // Leading zeroes encoded as base58 zeros - for (const unsigned char* p = pbegin; p < pend && *p == 0; p++) - str += pszBase58[0]; - - // Convert little endian std::string to big endian - reverse(str.begin(), str.end()); - return str; -} - -inline std::string EncodeBase58(const std::vector& vch) -{ - return EncodeBase58(&vch[0], &vch[0] + vch.size()); -} - -inline bool DecodeBase58(const char* psz, std::vector& vchRet) -{ - CAutoBN_CTX pctx; - vchRet.clear(); - CBigNum bn58 = 58; - CBigNum bn = 0; - CBigNum bnChar; - while (isspace(*psz)) - psz++; - - // Convert big endian std::string to bignum - for (const char* p = psz; *p; p++) - { - const char* p1 = strchr(pszBase58, *p); - if (p1 == NULL) - { - while (isspace(*p)) - p++; - if (*p != '\0') - return false; - break; - } - bnChar.setulong(p1 - pszBase58); - if (!BN_mul(&bn, &bn, &bn58, pctx)) - throw bignum_error("DecodeBase58 : BN_mul failed"); - bn += bnChar; - } - - // Get bignum as little endian data - std::vector vchTmp = bn.getvch(); - - // Trim off sign byte if present - if (vchTmp.size() >= 2 && vchTmp.end()[-1] == 0 && vchTmp.end()[-2] >= 0x80) - vchTmp.erase(vchTmp.end()-1); - - // Restore leading zeros - int nLeadingZeros = 0; - for (const char* p = psz; *p == pszBase58[0]; p++) - nLeadingZeros++; - vchRet.assign(nLeadingZeros + vchTmp.size(), 0); - - // Convert little endian data to big endian - reverse_copy(vchTmp.begin(), vchTmp.end(), vchRet.end() - vchTmp.size()); - return true; -} - -inline bool DecodeBase58(const std::string& str, std::vector& vchRet) -{ - return DecodeBase58(str.c_str(), vchRet); -} - - - - - -inline std::string EncodeBase58Check(const std::vector& vchIn) -{ - // add 4-byte hash check to the end - std::vector vch(vchIn); - uint256 hash = Hash(vch.begin(), vch.end()); - vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4); - return EncodeBase58(vch); -} - -inline bool DecodeBase58Check(const char* psz, std::vector& vchRet) -{ - if (!DecodeBase58(psz, vchRet)) - return false; - if (vchRet.size() < 4) - { - vchRet.clear(); - return false; - } - uint256 hash = Hash(vchRet.begin(), vchRet.end()-4); - if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) - { - vchRet.clear(); - return false; - } - vchRet.resize(vchRet.size()-4); - return true; -} - -inline bool DecodeBase58Check(const std::string& str, std::vector& vchRet) -{ - return DecodeBase58Check(str.c_str(), vchRet); -} - - - - - - -#define ADDRESSVERSION ((unsigned char)(fTestNet ? 111 : 0)) - -inline std::string Hash160ToAddress(uint160 hash160) -{ - // add 1-byte version number to the front - std::vector vch(1, ADDRESSVERSION); - vch.insert(vch.end(), UBEGIN(hash160), UEND(hash160)); - return EncodeBase58Check(vch); -} - -inline bool AddressToHash160(const char* psz, uint160& hash160Ret) -{ - std::vector vch; - if (!DecodeBase58Check(psz, vch)) - return false; - if (vch.empty()) - return false; - unsigned char nVersion = vch[0]; - if (vch.size() != sizeof(hash160Ret) + 1) - return false; - memcpy(&hash160Ret, &vch[1], sizeof(hash160Ret)); - return (nVersion <= ADDRESSVERSION); -} - -inline bool AddressToHash160(const std::string& str, uint160& hash160Ret) -{ - return AddressToHash160(str.c_str(), hash160Ret); -} - -inline bool IsValidBitcoinAddress(const char* psz) -{ - uint160 hash160; - return AddressToHash160(psz, hash160); -} - -inline bool IsValidBitcoinAddress(const std::string& str) -{ - return IsValidBitcoinAddress(str.c_str()); -} - - - - -inline std::string PubKeyToAddress(const std::vector& vchPubKey) -{ - return Hash160ToAddress(Hash160(vchPubKey)); -} - -#endif diff --git a/lib/include/bignum.h b/lib/include/bignum.h deleted file mode 100644 index c207af1004..0000000000 --- a/lib/include/bignum.h +++ /dev/null @@ -1,537 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_BIGNUM_H -#define BITCOIN_BIGNUM_H - -#include -#include -#include -#include - - - - - -class bignum_error : public std::runtime_error -{ -public: - explicit bignum_error(const std::string& str) : std::runtime_error(str) {} -}; - - - -class CAutoBN_CTX -{ -protected: - BN_CTX* pctx; - BN_CTX* operator=(BN_CTX* pnew) { return pctx = pnew; } - -public: - CAutoBN_CTX() - { - pctx = BN_CTX_new(); - if (pctx == NULL) - throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned NULL"); - } - - ~CAutoBN_CTX() - { - if (pctx != NULL) - BN_CTX_free(pctx); - } - - operator BN_CTX*() { return pctx; } - BN_CTX& operator*() { return *pctx; } - BN_CTX** operator&() { return &pctx; } - bool operator!() { return (pctx == NULL); } -}; - - - -class CBigNum : public BIGNUM -{ -public: - CBigNum() - { - BN_init(this); - } - - CBigNum(const CBigNum& b) - { - BN_init(this); - if (!BN_copy(this, &b)) - { - BN_clear_free(this); - throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_copy failed"); - } - } - - CBigNum& operator=(const CBigNum& b) - { - if (!BN_copy(this, &b)) - throw bignum_error("CBigNum::operator= : BN_copy failed"); - return (*this); - } - - ~CBigNum() - { - BN_clear_free(this); - } - - CBigNum(char n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(short n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(int n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(long n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(int64 n) { BN_init(this); setint64(n); } - CBigNum(unsigned char n) { BN_init(this); setulong(n); } - CBigNum(unsigned short n) { BN_init(this); setulong(n); } - CBigNum(unsigned int n) { BN_init(this); setulong(n); } - CBigNum(unsigned long n) { BN_init(this); setulong(n); } - CBigNum(uint64 n) { BN_init(this); setuint64(n); } - explicit CBigNum(uint256 n) { BN_init(this); setuint256(n); } - - explicit CBigNum(const std::vector& vch) - { - BN_init(this); - setvch(vch); - } - - void setulong(unsigned long n) - { - if (!BN_set_word(this, n)) - throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed"); - } - - unsigned long getulong() const - { - return BN_get_word(this); - } - - unsigned int getuint() const - { - return BN_get_word(this); - } - - int getint() const - { - unsigned long n = BN_get_word(this); - if (!BN_is_negative(this)) - return (n > INT_MAX ? INT_MAX : n); - else - return (n > INT_MAX ? INT_MIN : -(int)n); - } - - void setint64(int64 n) - { - unsigned char pch[sizeof(n) + 6]; - unsigned char* p = pch + 4; - bool fNegative = false; - if (n < (int64)0) - { - n = -n; - fNegative = true; - } - bool fLeadingZeroes = true; - for (int i = 0; i < 8; i++) - { - unsigned char c = (n >> 56) & 0xff; - n <<= 8; - if (fLeadingZeroes) - { - if (c == 0) - continue; - if (c & 0x80) - *p++ = (fNegative ? 0x80 : 0); - else if (fNegative) - c |= 0x80; - fLeadingZeroes = false; - } - *p++ = c; - } - unsigned int nSize = p - (pch + 4); - pch[0] = (nSize >> 24) & 0xff; - pch[1] = (nSize >> 16) & 0xff; - pch[2] = (nSize >> 8) & 0xff; - pch[3] = (nSize) & 0xff; - BN_mpi2bn(pch, p - pch, this); - } - - void setuint64(uint64 n) - { - unsigned char pch[sizeof(n) + 6]; - unsigned char* p = pch + 4; - bool fLeadingZeroes = true; - for (int i = 0; i < 8; i++) - { - unsigned char c = (n >> 56) & 0xff; - n <<= 8; - if (fLeadingZeroes) - { - if (c == 0) - continue; - if (c & 0x80) - *p++ = 0; - fLeadingZeroes = false; - } - *p++ = c; - } - unsigned int nSize = p - (pch + 4); - pch[0] = (nSize >> 24) & 0xff; - pch[1] = (nSize >> 16) & 0xff; - pch[2] = (nSize >> 8) & 0xff; - pch[3] = (nSize) & 0xff; - BN_mpi2bn(pch, p - pch, this); - } - - void setuint256(uint256 n) - { - unsigned char pch[sizeof(n) + 6]; - unsigned char* p = pch + 4; - bool fLeadingZeroes = true; - unsigned char* pbegin = (unsigned char*)&n; - unsigned char* psrc = pbegin + sizeof(n); - while (psrc != pbegin) - { - unsigned char c = *(--psrc); - if (fLeadingZeroes) - { - if (c == 0) - continue; - if (c & 0x80) - *p++ = 0; - fLeadingZeroes = false; - } - *p++ = c; - } - unsigned int nSize = p - (pch + 4); - pch[0] = (nSize >> 24) & 0xff; - pch[1] = (nSize >> 16) & 0xff; - pch[2] = (nSize >> 8) & 0xff; - pch[3] = (nSize >> 0) & 0xff; - BN_mpi2bn(pch, p - pch, this); - } - - uint256 getuint256() - { - unsigned int nSize = BN_bn2mpi(this, NULL); - if (nSize < 4) - return 0; - std::vector vch(nSize); - BN_bn2mpi(this, &vch[0]); - if (vch.size() > 4) - vch[4] &= 0x7f; - uint256 n = 0; - for (int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) - ((unsigned char*)&n)[i] = vch[j]; - return n; - } - - void setvch(const std::vector& vch) - { - std::vector vch2(vch.size() + 4); - unsigned int nSize = vch.size(); - vch2[0] = (nSize >> 24) & 0xff; - vch2[1] = (nSize >> 16) & 0xff; - vch2[2] = (nSize >> 8) & 0xff; - vch2[3] = (nSize >> 0) & 0xff; - reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4); - BN_mpi2bn(&vch2[0], vch2.size(), this); - } - - std::vector getvch() const - { - unsigned int nSize = BN_bn2mpi(this, NULL); - if (nSize < 4) - return std::vector(); - std::vector vch(nSize); - BN_bn2mpi(this, &vch[0]); - vch.erase(vch.begin(), vch.begin() + 4); - reverse(vch.begin(), vch.end()); - return vch; - } - - CBigNum& SetCompact(unsigned int nCompact) - { - unsigned int nSize = nCompact >> 24; - std::vector vch(4 + nSize); - vch[3] = nSize; - if (nSize >= 1) vch[4] = (nCompact >> 16) & 0xff; - if (nSize >= 2) vch[5] = (nCompact >> 8) & 0xff; - if (nSize >= 3) vch[6] = (nCompact >> 0) & 0xff; - BN_mpi2bn(&vch[0], vch.size(), this); - return *this; - } - - unsigned int GetCompact() const - { - unsigned int nSize = BN_bn2mpi(this, NULL); - std::vector vch(nSize); - nSize -= 4; - BN_bn2mpi(this, &vch[0]); - unsigned int nCompact = nSize << 24; - if (nSize >= 1) nCompact |= (vch[4] << 16); - if (nSize >= 2) nCompact |= (vch[5] << 8); - if (nSize >= 3) nCompact |= (vch[6] << 0); - return nCompact; - } - - void SetHex(const std::string& str) - { - // skip 0x - const char* psz = str.c_str(); - while (isspace(*psz)) - psz++; - bool fNegative = false; - if (*psz == '-') - { - fNegative = true; - psz++; - } - if (psz[0] == '0' && tolower(psz[1]) == 'x') - psz += 2; - while (isspace(*psz)) - psz++; - - // hex string to bignum - static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; - *this = 0; - while (isxdigit(*psz)) - { - *this <<= 4; - int n = phexdigit[*psz++]; - *this += n; - } - if (fNegative) - *this = 0 - *this; - } - - std::string ToString(int nBase=10) const - { - CAutoBN_CTX pctx; - CBigNum bnBase = nBase; - CBigNum bn0 = 0; - std::string str; - CBigNum bn = *this; - BN_set_negative(&bn, false); - CBigNum dv; - CBigNum rem; - if (BN_cmp(&bn, &bn0) == 0) - return "0"; - while (BN_cmp(&bn, &bn0) > 0) - { - if (!BN_div(&dv, &rem, &bn, &bnBase, pctx)) - throw bignum_error("CBigNum::ToString() : BN_div failed"); - bn = dv; - unsigned int c = rem.getulong(); - str += "0123456789abcdef"[c]; - } - if (BN_is_negative(this)) - str += "-"; - reverse(str.begin(), str.end()); - return str; - } - - std::string GetHex() const - { - return ToString(16); - } - - unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const - { - return ::GetSerializeSize(getvch(), nType, nVersion); - } - - template - void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const - { - ::Serialize(s, getvch(), nType, nVersion); - } - - template - void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) - { - std::vector vch; - ::Unserialize(s, vch, nType, nVersion); - setvch(vch); - } - - - bool operator!() const - { - return BN_is_zero(this); - } - - CBigNum& operator+=(const CBigNum& b) - { - if (!BN_add(this, this, &b)) - throw bignum_error("CBigNum::operator+= : BN_add failed"); - return *this; - } - - CBigNum& operator-=(const CBigNum& b) - { - *this = *this - b; - return *this; - } - - CBigNum& operator*=(const CBigNum& b) - { - CAutoBN_CTX pctx; - if (!BN_mul(this, this, &b, pctx)) - throw bignum_error("CBigNum::operator*= : BN_mul failed"); - return *this; - } - - CBigNum& operator/=(const CBigNum& b) - { - *this = *this / b; - return *this; - } - - CBigNum& operator%=(const CBigNum& b) - { - *this = *this % b; - return *this; - } - - CBigNum& operator<<=(unsigned int shift) - { - if (!BN_lshift(this, this, shift)) - throw bignum_error("CBigNum:operator<<= : BN_lshift failed"); - return *this; - } - - CBigNum& operator>>=(unsigned int shift) - { - // Note: BN_rshift segfaults on 64-bit if 2^shift is greater than the number - // if built on ubuntu 9.04 or 9.10, probably depends on version of openssl - CBigNum a = 1; - a <<= shift; - if (BN_cmp(&a, this) > 0) - { - *this = 0; - return *this; - } - - if (!BN_rshift(this, this, shift)) - throw bignum_error("CBigNum:operator>>= : BN_rshift failed"); - return *this; - } - - - CBigNum& operator++() - { - // prefix operator - if (!BN_add(this, this, BN_value_one())) - throw bignum_error("CBigNum::operator++ : BN_add failed"); - return *this; - } - - const CBigNum operator++(int) - { - // postfix operator - const CBigNum ret = *this; - ++(*this); - return ret; - } - - CBigNum& operator--() - { - // prefix operator - CBigNum r; - if (!BN_sub(&r, this, BN_value_one())) - throw bignum_error("CBigNum::operator-- : BN_sub failed"); - *this = r; - return *this; - } - - const CBigNum operator--(int) - { - // postfix operator - const CBigNum ret = *this; - --(*this); - return ret; - } - - - friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b); - friend inline const CBigNum operator/(const CBigNum& a, const CBigNum& b); - friend inline const CBigNum operator%(const CBigNum& a, const CBigNum& b); -}; - - - -inline const CBigNum operator+(const CBigNum& a, const CBigNum& b) -{ - CBigNum r; - if (!BN_add(&r, &a, &b)) - throw bignum_error("CBigNum::operator+ : BN_add failed"); - return r; -} - -inline const CBigNum operator-(const CBigNum& a, const CBigNum& b) -{ - CBigNum r; - if (!BN_sub(&r, &a, &b)) - throw bignum_error("CBigNum::operator- : BN_sub failed"); - return r; -} - -inline const CBigNum operator-(const CBigNum& a) -{ - CBigNum r(a); - BN_set_negative(&r, !BN_is_negative(&r)); - return r; -} - -inline const CBigNum operator*(const CBigNum& a, const CBigNum& b) -{ - CAutoBN_CTX pctx; - CBigNum r; - if (!BN_mul(&r, &a, &b, pctx)) - throw bignum_error("CBigNum::operator* : BN_mul failed"); - return r; -} - -inline const CBigNum operator/(const CBigNum& a, const CBigNum& b) -{ - CAutoBN_CTX pctx; - CBigNum r; - if (!BN_div(&r, NULL, &a, &b, pctx)) - throw bignum_error("CBigNum::operator/ : BN_div failed"); - return r; -} - -inline const CBigNum operator%(const CBigNum& a, const CBigNum& b) -{ - CAutoBN_CTX pctx; - CBigNum r; - if (!BN_mod(&r, &a, &b, pctx)) - throw bignum_error("CBigNum::operator% : BN_div failed"); - return r; -} - -inline const CBigNum operator<<(const CBigNum& a, unsigned int shift) -{ - CBigNum r; - if (!BN_lshift(&r, &a, shift)) - throw bignum_error("CBigNum:operator<< : BN_lshift failed"); - return r; -} - -inline const CBigNum operator>>(const CBigNum& a, unsigned int shift) -{ - CBigNum r = a; - r >>= shift; - return r; -} - -inline bool operator==(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) == 0); } -inline bool operator!=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) != 0); } -inline bool operator<=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) <= 0); } -inline bool operator>=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) >= 0); } -inline bool operator<(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) < 0); } -inline bool operator>(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) > 0); } - -#endif diff --git a/lib/include/serialize.h b/lib/include/serialize.h deleted file mode 100644 index f810b339c2..0000000000 --- a/lib/include/serialize.h +++ /dev/null @@ -1,1268 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_SERIALIZE_H -#define BITCOIN_SERIALIZE_H - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if defined(_MSC_VER) || defined(__BORLANDC__) -typedef __int64 int64; -typedef unsigned __int64 uint64; -#else -typedef long long int64; -typedef unsigned long long uint64; -#endif -#if defined(_MSC_VER) && _MSC_VER < 1300 -#define for if (false) ; else for -#endif -class CScript; -class CDataStream; -class CAutoFile; -static const unsigned int MAX_SIZE = 0x02000000; - -static const int VERSION = 32200; -static const char* pszSubVer = ""; -static const bool VERSION_IS_BETA = true; - - - - - - -///////////////////////////////////////////////////////////////// -// -// Templates for serializing to anything that looks like a stream, -// i.e. anything that supports .read(char*, int) and .write(char*, int) -// - -enum -{ - // primary actions - SER_NETWORK = (1 << 0), - SER_DISK = (1 << 1), - SER_GETHASH = (1 << 2), - - // modifiers - SER_SKIPSIG = (1 << 16), - SER_BLOCKHEADERONLY = (1 << 17), -}; - -#define IMPLEMENT_SERIALIZE(statements) \ - unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const \ - { \ - CSerActionGetSerializeSize ser_action; \ - const bool fGetSize = true; \ - const bool fWrite = false; \ - const bool fRead = false; \ - unsigned int nSerSize = 0; \ - ser_streamplaceholder s; \ - s.nType = nType; \ - s.nVersion = nVersion; \ - {statements} \ - return nSerSize; \ - } \ - template \ - void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const \ - { \ - CSerActionSerialize ser_action; \ - const bool fGetSize = false; \ - const bool fWrite = true; \ - const bool fRead = false; \ - unsigned int nSerSize = 0; \ - {statements} \ - } \ - template \ - void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) \ - { \ - CSerActionUnserialize ser_action; \ - const bool fGetSize = false; \ - const bool fWrite = false; \ - const bool fRead = true; \ - unsigned int nSerSize = 0; \ - {statements} \ - } - -#define READWRITE(obj) (nSerSize += ::SerReadWrite(s, (obj), nType, nVersion, ser_action)) - - - - - - -// -// Basic types -// -#define WRITEDATA(s, obj) s.write((char*)&(obj), sizeof(obj)) -#define READDATA(s, obj) s.read((char*)&(obj), sizeof(obj)) - -inline unsigned int GetSerializeSize(char a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed char a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned char a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed short a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned short a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed int a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned int a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed long a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned long a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(int64 a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(uint64 a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(float a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(double a, int, int=0) { return sizeof(a); } - -template inline void Serialize(Stream& s, char a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, signed char a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, unsigned char a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, signed short a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, unsigned short a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, signed int a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, unsigned int a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, signed long a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, unsigned long a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, int64 a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, uint64 a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, float a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, double a, int, int=0) { WRITEDATA(s, a); } - -template inline void Unserialize(Stream& s, char& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, signed char& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, unsigned char& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, signed short& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, unsigned short& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, signed int& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, unsigned int& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, signed long& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, unsigned long& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, int64& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, uint64& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, float& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, double& a, int, int=0) { READDATA(s, a); } - -inline unsigned int GetSerializeSize(bool a, int, int=0) { return sizeof(char); } -template inline void Serialize(Stream& s, bool a, int, int=0) { char f=a; WRITEDATA(s, f); } -template inline void Unserialize(Stream& s, bool& a, int, int=0) { char f; READDATA(s, f); a=f; } - - - - - - -// -// Compact size -// size < 253 -- 1 byte -// size <= USHRT_MAX -- 3 bytes (253 + 2 bytes) -// size <= UINT_MAX -- 5 bytes (254 + 4 bytes) -// size > UINT_MAX -- 9 bytes (255 + 8 bytes) -// -inline unsigned int GetSizeOfCompactSize(uint64 nSize) -{ - if (nSize < 253) return sizeof(unsigned char); - else if (nSize <= USHRT_MAX) return sizeof(unsigned char) + sizeof(unsigned short); - else if (nSize <= UINT_MAX) return sizeof(unsigned char) + sizeof(unsigned int); - else return sizeof(unsigned char) + sizeof(uint64); -} - -template -void WriteCompactSize(Stream& os, uint64 nSize) -{ - if (nSize < 253) - { - unsigned char chSize = nSize; - WRITEDATA(os, chSize); - } - else if (nSize <= USHRT_MAX) - { - unsigned char chSize = 253; - unsigned short xSize = nSize; - WRITEDATA(os, chSize); - WRITEDATA(os, xSize); - } - else if (nSize <= UINT_MAX) - { - unsigned char chSize = 254; - unsigned int xSize = nSize; - WRITEDATA(os, chSize); - WRITEDATA(os, xSize); - } - else - { - unsigned char chSize = 255; - uint64 xSize = nSize; - WRITEDATA(os, chSize); - WRITEDATA(os, xSize); - } - return; -} - -template -uint64 ReadCompactSize(Stream& is) -{ - unsigned char chSize; - READDATA(is, chSize); - uint64 nSizeRet = 0; - if (chSize < 253) - { - nSizeRet = chSize; - } - else if (chSize == 253) - { - unsigned short xSize; - READDATA(is, xSize); - nSizeRet = xSize; - } - else if (chSize == 254) - { - unsigned int xSize; - READDATA(is, xSize); - nSizeRet = xSize; - } - else - { - uint64 xSize; - READDATA(is, xSize); - nSizeRet = xSize; - } - if (nSizeRet > (uint64)MAX_SIZE) - throw std::ios_base::failure("ReadCompactSize() : size too large"); - return nSizeRet; -} - - - -// -// Wrapper for serializing arrays and POD -// There's a clever template way to make arrays serialize normally, but MSVC6 doesn't support it -// -#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) -class CFlatData -{ -protected: - char* pbegin; - char* pend; -public: - CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } - char* begin() { return pbegin; } - const char* begin() const { return pbegin; } - char* end() { return pend; } - const char* end() const { return pend; } - - unsigned int GetSerializeSize(int, int=0) const - { - return pend - pbegin; - } - - template - void Serialize(Stream& s, int, int=0) const - { - s.write(pbegin, pend - pbegin); - } - - template - void Unserialize(Stream& s, int, int=0) - { - s.read(pbegin, pend - pbegin); - } -}; - - - -// -// string stored as a fixed length field -// -template -class CFixedFieldString -{ -protected: - const std::string* pcstr; - std::string* pstr; -public: - explicit CFixedFieldString(const std::string& str) : pcstr(&str), pstr(NULL) { } - explicit CFixedFieldString(std::string& str) : pcstr(&str), pstr(&str) { } - - unsigned int GetSerializeSize(int, int=0) const - { - return LEN; - } - - template - void Serialize(Stream& s, int, int=0) const - { - char pszBuf[LEN]; - strncpy(pszBuf, pcstr->c_str(), LEN); - s.write(pszBuf, LEN); - } - - template - void Unserialize(Stream& s, int, int=0) - { - if (pstr == NULL) - throw std::ios_base::failure("CFixedFieldString::Unserialize : trying to unserialize to const string"); - char pszBuf[LEN+1]; - s.read(pszBuf, LEN); - pszBuf[LEN] = '\0'; - *pstr = pszBuf; - } -}; - - - - - -// -// Forward declarations -// - -// string -template unsigned int GetSerializeSize(const std::basic_string& str, int, int=0); -template void Serialize(Stream& os, const std::basic_string& str, int, int=0); -template void Unserialize(Stream& is, std::basic_string& str, int, int=0); - -// vector -template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&); -template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&); -template inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion=VERSION); -template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&); -template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&); -template inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion=VERSION); -template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&); -template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&); -template inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion=VERSION); - -// others derived from vector -extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion=VERSION); -template void Serialize(Stream& os, const CScript& v, int nType, int nVersion=VERSION); -template void Unserialize(Stream& is, CScript& v, int nType, int nVersion=VERSION); - -// pair -template unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion=VERSION); -template void Serialize(Stream& os, const std::pair& item, int nType, int nVersion=VERSION); -template void Unserialize(Stream& is, std::pair& item, int nType, int nVersion=VERSION); - -// 3 tuple -template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion=VERSION); -template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion=VERSION); -template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion=VERSION); - -// 4 tuple -template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion=VERSION); -template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion=VERSION); -template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion=VERSION); - -// map -template unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion=VERSION); -template void Serialize(Stream& os, const std::map& m, int nType, int nVersion=VERSION); -template void Unserialize(Stream& is, std::map& m, int nType, int nVersion=VERSION); - -// set -template unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion=VERSION); -template void Serialize(Stream& os, const std::set& m, int nType, int nVersion=VERSION); -template void Unserialize(Stream& is, std::set& m, int nType, int nVersion=VERSION); - - - - - -// -// If none of the specialized versions above matched, default to calling member function. -// "int nType" is changed to "long nType" to keep from getting an ambiguous overload error. -// The compiler will only cast int to long if none of the other templates matched. -// Thanks to Boost serialization for this idea. -// -template -inline unsigned int GetSerializeSize(const T& a, long nType, int nVersion=VERSION) -{ - return a.GetSerializeSize((int)nType, nVersion); -} - -template -inline void Serialize(Stream& os, const T& a, long nType, int nVersion=VERSION) -{ - a.Serialize(os, (int)nType, nVersion); -} - -template -inline void Unserialize(Stream& is, T& a, long nType, int nVersion=VERSION) -{ - a.Unserialize(is, (int)nType, nVersion); -} - - - - - -// -// string -// -template -unsigned int GetSerializeSize(const std::basic_string& str, int, int) -{ - return GetSizeOfCompactSize(str.size()) + str.size() * sizeof(str[0]); -} - -template -void Serialize(Stream& os, const std::basic_string& str, int, int) -{ - WriteCompactSize(os, str.size()); - if (!str.empty()) - os.write((char*)&str[0], str.size() * sizeof(str[0])); -} - -template -void Unserialize(Stream& is, std::basic_string& str, int, int) -{ - unsigned int nSize = ReadCompactSize(is); - str.resize(nSize); - if (nSize != 0) - is.read((char*)&str[0], nSize * sizeof(str[0])); -} - - - -// -// vector -// -template -unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&) -{ - return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); -} - -template -unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&) -{ - unsigned int nSize = GetSizeOfCompactSize(v.size()); - for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) - nSize += GetSerializeSize((*vi), nType, nVersion); - return nSize; -} - -template -inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion) -{ - return GetSerializeSize_impl(v, nType, nVersion, boost::is_fundamental()); -} - - -template -void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&) -{ - WriteCompactSize(os, v.size()); - if (!v.empty()) - os.write((char*)&v[0], v.size() * sizeof(T)); -} - -template -void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&) -{ - WriteCompactSize(os, v.size()); - for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) - ::Serialize(os, (*vi), nType, nVersion); -} - -template -inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion) -{ - Serialize_impl(os, v, nType, nVersion, boost::is_fundamental()); -} - - -template -void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&) -{ - //unsigned int nSize = ReadCompactSize(is); - //v.resize(nSize); - //is.read((char*)&v[0], nSize * sizeof(T)); - - // Limit size per read so bogus size value won't cause out of memory - v.clear(); - unsigned int nSize = ReadCompactSize(is); - unsigned int i = 0; - while (i < nSize) - { - unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); - v.resize(i + blk); - is.read((char*)&v[i], blk * sizeof(T)); - i += blk; - } -} - -template -void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&) -{ - //unsigned int nSize = ReadCompactSize(is); - //v.resize(nSize); - //for (std::vector::iterator vi = v.begin(); vi != v.end(); ++vi) - // Unserialize(is, (*vi), nType, nVersion); - - v.clear(); - unsigned int nSize = ReadCompactSize(is); - unsigned int i = 0; - unsigned int nMid = 0; - while (nMid < nSize) - { - nMid += 5000000 / sizeof(T); - if (nMid > nSize) - nMid = nSize; - v.resize(nMid); - for (; i < nMid; i++) - Unserialize(is, v[i], nType, nVersion); - } -} - -template -inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion) -{ - Unserialize_impl(is, v, nType, nVersion, boost::is_fundamental()); -} - - - -// -// others derived from vector -// -inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion) -{ - return GetSerializeSize((const std::vector&)v, nType, nVersion); -} - -template -void Serialize(Stream& os, const CScript& v, int nType, int nVersion) -{ - Serialize(os, (const std::vector&)v, nType, nVersion); -} - -template -void Unserialize(Stream& is, CScript& v, int nType, int nVersion) -{ - Unserialize(is, (std::vector&)v, nType, nVersion); -} - - - -// -// pair -// -template -unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion) -{ - return GetSerializeSize(item.first, nType, nVersion) + GetSerializeSize(item.second, nType, nVersion); -} - -template -void Serialize(Stream& os, const std::pair& item, int nType, int nVersion) -{ - Serialize(os, item.first, nType, nVersion); - Serialize(os, item.second, nType, nVersion); -} - -template -void Unserialize(Stream& is, std::pair& item, int nType, int nVersion) -{ - Unserialize(is, item.first, nType, nVersion); - Unserialize(is, item.second, nType, nVersion); -} - - - -// -// 3 tuple -// -template -unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) -{ - unsigned int nSize = 0; - nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); - return nSize; -} - -template -void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) -{ - Serialize(os, boost::get<0>(item), nType, nVersion); - Serialize(os, boost::get<1>(item), nType, nVersion); - Serialize(os, boost::get<2>(item), nType, nVersion); -} - -template -void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) -{ - Unserialize(is, boost::get<0>(item), nType, nVersion); - Unserialize(is, boost::get<1>(item), nType, nVersion); - Unserialize(is, boost::get<2>(item), nType, nVersion); -} - - - -// -// 4 tuple -// -template -unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) -{ - unsigned int nSize = 0; - nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<3>(item), nType, nVersion); - return nSize; -} - -template -void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) -{ - Serialize(os, boost::get<0>(item), nType, nVersion); - Serialize(os, boost::get<1>(item), nType, nVersion); - Serialize(os, boost::get<2>(item), nType, nVersion); - Serialize(os, boost::get<3>(item), nType, nVersion); -} - -template -void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) -{ - Unserialize(is, boost::get<0>(item), nType, nVersion); - Unserialize(is, boost::get<1>(item), nType, nVersion); - Unserialize(is, boost::get<2>(item), nType, nVersion); - Unserialize(is, boost::get<3>(item), nType, nVersion); -} - - - -// -// map -// -template -unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion) -{ - unsigned int nSize = GetSizeOfCompactSize(m.size()); - for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) - nSize += GetSerializeSize((*mi), nType, nVersion); - return nSize; -} - -template -void Serialize(Stream& os, const std::map& m, int nType, int nVersion) -{ - WriteCompactSize(os, m.size()); - for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) - Serialize(os, (*mi), nType, nVersion); -} - -template -void Unserialize(Stream& is, std::map& m, int nType, int nVersion) -{ - m.clear(); - unsigned int nSize = ReadCompactSize(is); - typename std::map::iterator mi = m.begin(); - for (unsigned int i = 0; i < nSize; i++) - { - std::pair item; - Unserialize(is, item, nType, nVersion); - mi = m.insert(mi, item); - } -} - - - -// -// set -// -template -unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion) -{ - unsigned int nSize = GetSizeOfCompactSize(m.size()); - for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) - nSize += GetSerializeSize((*it), nType, nVersion); - return nSize; -} - -template -void Serialize(Stream& os, const std::set& m, int nType, int nVersion) -{ - WriteCompactSize(os, m.size()); - for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) - Serialize(os, (*it), nType, nVersion); -} - -template -void Unserialize(Stream& is, std::set& m, int nType, int nVersion) -{ - m.clear(); - unsigned int nSize = ReadCompactSize(is); - typename std::set::iterator it = m.begin(); - for (unsigned int i = 0; i < nSize; i++) - { - K key; - Unserialize(is, key, nType, nVersion); - it = m.insert(it, key); - } -} - - - -// -// Support for IMPLEMENT_SERIALIZE and READWRITE macro -// -class CSerActionGetSerializeSize { }; -class CSerActionSerialize { }; -class CSerActionUnserialize { }; - -template -inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionGetSerializeSize ser_action) -{ - return ::GetSerializeSize(obj, nType, nVersion); -} - -template -inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionSerialize ser_action) -{ - ::Serialize(s, obj, nType, nVersion); - return 0; -} - -template -inline unsigned int SerReadWrite(Stream& s, T& obj, int nType, int nVersion, CSerActionUnserialize ser_action) -{ - ::Unserialize(s, obj, nType, nVersion); - return 0; -} - -struct ser_streamplaceholder -{ - int nType; - int nVersion; -}; - - - - - - - - - -// -// Allocator that clears its contents before deletion -// -template -struct secure_allocator : public std::allocator -{ - // MSVC8 default copy constructor is broken - typedef std::allocator base; - typedef typename base::size_type size_type; - typedef typename base::difference_type difference_type; - typedef typename base::pointer pointer; - typedef typename base::const_pointer const_pointer; - typedef typename base::reference reference; - typedef typename base::const_reference const_reference; - typedef typename base::value_type value_type; - secure_allocator() throw() {} - secure_allocator(const secure_allocator& a) throw() : base(a) {} - template - secure_allocator(const secure_allocator& a) throw() : base(a) {} - ~secure_allocator() throw() {} - template struct rebind - { typedef secure_allocator<_Other> other; }; - - void deallocate(T* p, std::size_t n) - { - if (p != NULL) - memset(p, 0, sizeof(T) * n); - std::allocator::deallocate(p, n); - } -}; - - - -// -// Double ended buffer combining vector and stream-like interfaces. -// >> and << read and write unformatted data using the above serialization templates. -// Fills with data in linear time; some stringstream implementations take N^2 time. -// -class CDataStream -{ -protected: - typedef std::vector > vector_type; - vector_type vch; - unsigned int nReadPos; - short state; - short exceptmask; -public: - int nType; - int nVersion; - - typedef vector_type::allocator_type allocator_type; - typedef vector_type::size_type size_type; - typedef vector_type::difference_type difference_type; - typedef vector_type::reference reference; - typedef vector_type::const_reference const_reference; - typedef vector_type::value_type value_type; - typedef vector_type::iterator iterator; - typedef vector_type::const_iterator const_iterator; - typedef vector_type::reverse_iterator reverse_iterator; - - explicit CDataStream(int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) - { - Init(nTypeIn, nVersionIn); - } - - CDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(pbegin, pend) - { - Init(nTypeIn, nVersionIn); - } - -#if !defined(_MSC_VER) || _MSC_VER >= 1300 - CDataStream(const char* pbegin, const char* pend, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(pbegin, pend) - { - Init(nTypeIn, nVersionIn); - } -#endif - - CDataStream(const vector_type& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end()) - { - Init(nTypeIn, nVersionIn); - } - - CDataStream(const std::vector& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end()) - { - Init(nTypeIn, nVersionIn); - } - - CDataStream(const std::vector& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch((char*)&vchIn.begin()[0], (char*)&vchIn.end()[0]) - { - Init(nTypeIn, nVersionIn); - } - - void Init(int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) - { - nReadPos = 0; - nType = nTypeIn; - nVersion = nVersionIn; - state = 0; - exceptmask = std::ios::badbit | std::ios::failbit; - } - - CDataStream& operator+=(const CDataStream& b) - { - vch.insert(vch.end(), b.begin(), b.end()); - return *this; - } - - friend CDataStream operator+(const CDataStream& a, const CDataStream& b) - { - CDataStream ret = a; - ret += b; - return (ret); - } - - std::string str() const - { - return (std::string(begin(), end())); - } - - - // - // Vector subset - // - const_iterator begin() const { return vch.begin() + nReadPos; } - iterator begin() { return vch.begin() + nReadPos; } - const_iterator end() const { return vch.end(); } - iterator end() { return vch.end(); } - size_type size() const { return vch.size() - nReadPos; } - bool empty() const { return vch.size() == nReadPos; } - void resize(size_type n, value_type c=0) { vch.resize(n + nReadPos, c); } - void reserve(size_type n) { vch.reserve(n + nReadPos); } - const_reference operator[](size_type pos) const { return vch[pos + nReadPos]; } - reference operator[](size_type pos) { return vch[pos + nReadPos]; } - void clear() { vch.clear(); nReadPos = 0; } - iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); } - void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); } - - void insert(iterator it, const_iterator first, const_iterator last) - { - if (it == vch.begin() + nReadPos && last - first <= nReadPos) - { - // special case for inserting at the front when there's room - nReadPos -= (last - first); - memcpy(&vch[nReadPos], &first[0], last - first); - } - else - vch.insert(it, first, last); - } - - void insert(iterator it, std::vector::const_iterator first, std::vector::const_iterator last) - { - if (it == vch.begin() + nReadPos && last - first <= nReadPos) - { - // special case for inserting at the front when there's room - nReadPos -= (last - first); - memcpy(&vch[nReadPos], &first[0], last - first); - } - else - vch.insert(it, first, last); - } - -#if !defined(_MSC_VER) || _MSC_VER >= 1300 - void insert(iterator it, const char* first, const char* last) - { - if (it == vch.begin() + nReadPos && last - first <= nReadPos) - { - // special case for inserting at the front when there's room - nReadPos -= (last - first); - memcpy(&vch[nReadPos], &first[0], last - first); - } - else - vch.insert(it, first, last); - } -#endif - - iterator erase(iterator it) - { - if (it == vch.begin() + nReadPos) - { - // special case for erasing from the front - if (++nReadPos >= vch.size()) - { - // whenever we reach the end, we take the opportunity to clear the buffer - nReadPos = 0; - return vch.erase(vch.begin(), vch.end()); - } - return vch.begin() + nReadPos; - } - else - return vch.erase(it); - } - - iterator erase(iterator first, iterator last) - { - if (first == vch.begin() + nReadPos) - { - // special case for erasing from the front - if (last == vch.end()) - { - nReadPos = 0; - return vch.erase(vch.begin(), vch.end()); - } - else - { - nReadPos = (last - vch.begin()); - return last; - } - } - else - return vch.erase(first, last); - } - - inline void Compact() - { - vch.erase(vch.begin(), vch.begin() + nReadPos); - nReadPos = 0; - } - - bool Rewind(size_type n) - { - // Rewind by n characters if the buffer hasn't been compacted yet - if (n > nReadPos) - return false; - nReadPos -= n; - return true; - } - - - // - // Stream subset - // - void setstate(short bits, const char* psz) - { - state |= bits; - if (state & exceptmask) - throw std::ios_base::failure(psz); - } - - bool eof() const { return size() == 0; } - bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } - bool good() const { return !eof() && (state == 0); } - void clear(short n) { state = n; } // name conflict with vector clear() - short exceptions() { return exceptmask; } - short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CDataStream"); return prev; } - CDataStream* rdbuf() { return this; } - int in_avail() { return size(); } - - void SetType(int n) { nType = n; } - int GetType() { return nType; } - void SetVersion(int n) { nVersion = n; } - int GetVersion() { return nVersion; } - void ReadVersion() { *this >> nVersion; } - void WriteVersion() { *this << nVersion; } - - CDataStream& read(char* pch, int nSize) - { - // Read from the beginning of the buffer - assert(nSize >= 0); - unsigned int nReadPosNext = nReadPos + nSize; - if (nReadPosNext >= vch.size()) - { - if (nReadPosNext > vch.size()) - { - setstate(std::ios::failbit, "CDataStream::read() : end of data"); - memset(pch, 0, nSize); - nSize = vch.size() - nReadPos; - } - memcpy(pch, &vch[nReadPos], nSize); - nReadPos = 0; - vch.clear(); - return (*this); - } - memcpy(pch, &vch[nReadPos], nSize); - nReadPos = nReadPosNext; - return (*this); - } - - CDataStream& ignore(int nSize) - { - // Ignore from the beginning of the buffer - assert(nSize >= 0); - unsigned int nReadPosNext = nReadPos + nSize; - if (nReadPosNext >= vch.size()) - { - if (nReadPosNext > vch.size()) - { - setstate(std::ios::failbit, "CDataStream::ignore() : end of data"); - nSize = vch.size() - nReadPos; - } - nReadPos = 0; - vch.clear(); - return (*this); - } - nReadPos = nReadPosNext; - return (*this); - } - - CDataStream& write(const char* pch, int nSize) - { - // Write to the end of the buffer - assert(nSize >= 0); - vch.insert(vch.end(), pch, pch + nSize); - return (*this); - } - - template - void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const - { - // Special case: stream << stream concatenates like stream += stream - if (!vch.empty()) - s.write((char*)&vch[0], vch.size() * sizeof(vch[0])); - } - - template - unsigned int GetSerializeSize(const T& obj) - { - // Tells the size of the object if serialized to this stream - return ::GetSerializeSize(obj, nType, nVersion); - } - - template - CDataStream& operator<<(const T& obj) - { - // Serialize to this stream - ::Serialize(*this, obj, nType, nVersion); - return (*this); - } - - template - CDataStream& operator>>(T& obj) - { - // Unserialize from this stream - ::Unserialize(*this, obj, nType, nVersion); - return (*this); - } -}; - -#ifdef TESTCDATASTREAM -// VC6sp6 -// CDataStream: -// n=1000 0 seconds -// n=2000 0 seconds -// n=4000 0 seconds -// n=8000 0 seconds -// n=16000 0 seconds -// n=32000 0 seconds -// n=64000 1 seconds -// n=128000 1 seconds -// n=256000 2 seconds -// n=512000 4 seconds -// n=1024000 8 seconds -// n=2048000 16 seconds -// n=4096000 32 seconds -// stringstream: -// n=1000 1 seconds -// n=2000 1 seconds -// n=4000 13 seconds -// n=8000 87 seconds -// n=16000 400 seconds -// n=32000 1660 seconds -// n=64000 6749 seconds -// n=128000 27241 seconds -// n=256000 109804 seconds -#include -int main(int argc, char *argv[]) -{ - vector vch(0xcc, 250); - printf("CDataStream:\n"); - for (int n = 1000; n <= 4500000; n *= 2) - { - CDataStream ss; - time_t nStart = time(NULL); - for (int i = 0; i < n; i++) - ss.write((char*)&vch[0], vch.size()); - printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); - } - printf("stringstream:\n"); - for (int n = 1000; n <= 4500000; n *= 2) - { - stringstream ss; - time_t nStart = time(NULL); - for (int i = 0; i < n; i++) - ss.write((char*)&vch[0], vch.size()); - printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); - } -} -#endif - - - - - - - - - - -// -// Automatic closing wrapper for FILE* -// - Will automatically close the file when it goes out of scope if not null. -// - If you're returning the file pointer, return file.release(). -// - If you need to close the file early, use file.fclose() instead of fclose(file). -// -class CAutoFile -{ -protected: - FILE* file; - short state; - short exceptmask; -public: - int nType; - int nVersion; - - typedef FILE element_type; - - CAutoFile(FILE* filenew=NULL, int nTypeIn=SER_DISK, int nVersionIn=VERSION) - { - file = filenew; - nType = nTypeIn; - nVersion = nVersionIn; - state = 0; - exceptmask = std::ios::badbit | std::ios::failbit; - } - - ~CAutoFile() - { - fclose(); - } - - void fclose() - { - if (file != NULL && file != stdin && file != stdout && file != stderr) - ::fclose(file); - file = NULL; - } - - FILE* release() { FILE* ret = file; file = NULL; return ret; } - operator FILE*() { return file; } - FILE* operator->() { return file; } - FILE& operator*() { return *file; } - FILE** operator&() { return &file; } - FILE* operator=(FILE* pnew) { return file = pnew; } - bool operator!() { return (file == NULL); } - - - // - // Stream subset - // - void setstate(short bits, const char* psz) - { - state |= bits; - if (state & exceptmask) - throw std::ios_base::failure(psz); - } - - bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } - bool good() const { return state == 0; } - void clear(short n = 0) { state = n; } - short exceptions() { return exceptmask; } - short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CAutoFile"); return prev; } - - void SetType(int n) { nType = n; } - int GetType() { return nType; } - void SetVersion(int n) { nVersion = n; } - int GetVersion() { return nVersion; } - void ReadVersion() { *this >> nVersion; } - void WriteVersion() { *this << nVersion; } - - CAutoFile& read(char* pch, int nSize) - { - if (!file) - throw std::ios_base::failure("CAutoFile::read : file handle is NULL"); - if (fread(pch, 1, nSize, file) != nSize) - setstate(std::ios::failbit, feof(file) ? "CAutoFile::read : end of file" : "CAutoFile::read : fread failed"); - return (*this); - } - - CAutoFile& write(const char* pch, int nSize) - { - if (!file) - throw std::ios_base::failure("CAutoFile::write : file handle is NULL"); - if (fwrite(pch, 1, nSize, file) != nSize) - setstate(std::ios::failbit, "CAutoFile::write : write failed"); - return (*this); - } - - template - unsigned int GetSerializeSize(const T& obj) - { - // Tells the size of the object if serialized to this stream - return ::GetSerializeSize(obj, nType, nVersion); - } - - template - CAutoFile& operator<<(const T& obj) - { - // Serialize to this stream - if (!file) - throw std::ios_base::failure("CAutoFile::operator<< : file handle is NULL"); - ::Serialize(*this, obj, nType, nVersion); - return (*this); - } - - template - CAutoFile& operator>>(T& obj) - { - // Unserialize from this stream - if (!file) - throw std::ios_base::failure("CAutoFile::operator>> : file handle is NULL"); - ::Unserialize(*this, obj, nType, nVersion); - return (*this); - } -}; - -#endif diff --git a/lib/include/uint256.h b/lib/include/uint256.h deleted file mode 100644 index 14feb1683d..0000000000 --- a/lib/include/uint256.h +++ /dev/null @@ -1,765 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_UINT256_H -#define BITCOIN_UINT256_H - -#include "serialize.h" - -#include -#include -#include - -#if defined(_MSC_VER) || defined(__BORLANDC__) -typedef __int64 int64; -typedef unsigned __int64 uint64; -#else -typedef long long int64; -typedef unsigned long long uint64; -#endif -#if defined(_MSC_VER) && _MSC_VER < 1300 -#define for if (false) ; else for -#endif - - -inline int Testuint256AdHoc(std::vector vArg); - - - -// We have to keep a separate base class without constructors -// so the compiler will let us use it in a union -template -class base_uint -{ -protected: - enum { WIDTH=BITS/32 }; - unsigned int pn[WIDTH]; -public: - - bool operator!() const - { - for (int i = 0; i < WIDTH; i++) - if (pn[i] != 0) - return false; - return true; - } - - const base_uint operator~() const - { - base_uint ret; - for (int i = 0; i < WIDTH; i++) - ret.pn[i] = ~pn[i]; - return ret; - } - - const base_uint operator-() const - { - base_uint ret; - for (int i = 0; i < WIDTH; i++) - ret.pn[i] = ~pn[i]; - ret++; - return ret; - } - - - base_uint& operator=(uint64 b) - { - pn[0] = (unsigned int)b; - pn[1] = (unsigned int)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - return *this; - } - - base_uint& operator^=(const base_uint& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] ^= b.pn[i]; - return *this; - } - - base_uint& operator&=(const base_uint& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] &= b.pn[i]; - return *this; - } - - base_uint& operator|=(const base_uint& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] |= b.pn[i]; - return *this; - } - - base_uint& operator^=(uint64 b) - { - pn[0] ^= (unsigned int)b; - pn[1] ^= (unsigned int)(b >> 32); - return *this; - } - - base_uint& operator&=(uint64 b) - { - pn[0] &= (unsigned int)b; - pn[1] &= (unsigned int)(b >> 32); - return *this; - } - - base_uint& operator|=(uint64 b) - { - pn[0] |= (unsigned int)b; - pn[1] |= (unsigned int)(b >> 32); - return *this; - } - - base_uint& operator<<=(unsigned int shift) - { - base_uint a(*this); - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - int k = shift / 32; - shift = shift % 32; - for (int i = 0; i < WIDTH; i++) - { - if (i+k+1 < WIDTH && shift != 0) - pn[i+k+1] |= (a.pn[i] >> (32-shift)); - if (i+k < WIDTH) - pn[i+k] |= (a.pn[i] << shift); - } - return *this; - } - - base_uint& operator>>=(unsigned int shift) - { - base_uint a(*this); - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - int k = shift / 32; - shift = shift % 32; - for (int i = 0; i < WIDTH; i++) - { - if (i-k-1 >= 0 && shift != 0) - pn[i-k-1] |= (a.pn[i] << (32-shift)); - if (i-k >= 0) - pn[i-k] |= (a.pn[i] >> shift); - } - return *this; - } - - base_uint& operator+=(const base_uint& b) - { - uint64 carry = 0; - for (int i = 0; i < WIDTH; i++) - { - uint64 n = carry + pn[i] + b.pn[i]; - pn[i] = n & 0xffffffff; - carry = n >> 32; - } - return *this; - } - - base_uint& operator-=(const base_uint& b) - { - *this += -b; - return *this; - } - - base_uint& operator+=(uint64 b64) - { - base_uint b; - b = b64; - *this += b; - return *this; - } - - base_uint& operator-=(uint64 b64) - { - base_uint b; - b = b64; - *this += -b; - return *this; - } - - - base_uint& operator++() - { - // prefix operator - int i = 0; - while (++pn[i] == 0 && i < WIDTH-1) - i++; - return *this; - } - - const base_uint operator++(int) - { - // postfix operator - const base_uint ret = *this; - ++(*this); - return ret; - } - - base_uint& operator--() - { - // prefix operator - int i = 0; - while (--pn[i] == -1 && i < WIDTH-1) - i++; - return *this; - } - - const base_uint operator--(int) - { - // postfix operator - const base_uint ret = *this; - --(*this); - return ret; - } - - - friend inline bool operator<(const base_uint& a, const base_uint& b) - { - for (int i = base_uint::WIDTH-1; i >= 0; i--) - { - if (a.pn[i] < b.pn[i]) - return true; - else if (a.pn[i] > b.pn[i]) - return false; - } - return false; - } - - friend inline bool operator<=(const base_uint& a, const base_uint& b) - { - for (int i = base_uint::WIDTH-1; i >= 0; i--) - { - if (a.pn[i] < b.pn[i]) - return true; - else if (a.pn[i] > b.pn[i]) - return false; - } - return true; - } - - friend inline bool operator>(const base_uint& a, const base_uint& b) - { - for (int i = base_uint::WIDTH-1; i >= 0; i--) - { - if (a.pn[i] > b.pn[i]) - return true; - else if (a.pn[i] < b.pn[i]) - return false; - } - return false; - } - - friend inline bool operator>=(const base_uint& a, const base_uint& b) - { - for (int i = base_uint::WIDTH-1; i >= 0; i--) - { - if (a.pn[i] > b.pn[i]) - return true; - else if (a.pn[i] < b.pn[i]) - return false; - } - return true; - } - - friend inline bool operator==(const base_uint& a, const base_uint& b) - { - for (int i = 0; i < base_uint::WIDTH; i++) - if (a.pn[i] != b.pn[i]) - return false; - return true; - } - - friend inline bool operator==(const base_uint& a, uint64 b) - { - if (a.pn[0] != (unsigned int)b) - return false; - if (a.pn[1] != (unsigned int)(b >> 32)) - return false; - for (int i = 2; i < base_uint::WIDTH; i++) - if (a.pn[i] != 0) - return false; - return true; - } - - friend inline bool operator!=(const base_uint& a, const base_uint& b) - { - return (!(a == b)); - } - - friend inline bool operator!=(const base_uint& a, uint64 b) - { - return (!(a == b)); - } - - - - std::string GetHex() const - { - char psz[sizeof(pn)*2 + 1]; - for (int i = 0; i < sizeof(pn); i++) - sprintf(psz + i*2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]); - return std::string(psz, psz + sizeof(pn)*2); - } - - void SetHex(const char* psz) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - - // skip leading spaces - while (isspace(*psz)) - psz++; - - // skip 0x - if (psz[0] == '0' && tolower(psz[1]) == 'x') - psz += 2; - - // hex string to uint - static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; - const char* pbegin = psz; - while (phexdigit[*psz] || *psz == '0') - psz++; - psz--; - unsigned char* p1 = (unsigned char*)pn; - unsigned char* pend = p1 + WIDTH * 4; - while (psz >= pbegin && p1 < pend) - { - *p1 = phexdigit[(unsigned char)*psz--]; - if (psz >= pbegin) - { - *p1 |= (phexdigit[(unsigned char)*psz--] << 4); - p1++; - } - } - } - - void SetHex(const std::string& str) - { - SetHex(str.c_str()); - } - - std::string ToString() const - { - return (GetHex()); - } - - unsigned char* begin() - { - return (unsigned char*)&pn[0]; - } - - unsigned char* end() - { - return (unsigned char*)&pn[WIDTH]; - } - - unsigned int size() - { - return sizeof(pn); - } - - - unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const - { - return sizeof(pn); - } - - template - void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const - { - s.write((char*)pn, sizeof(pn)); - } - - template - void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) - { - s.read((char*)pn, sizeof(pn)); - } - - - friend class uint160; - friend class uint256; - friend inline int Testuint256AdHoc(std::vector vArg); -}; - -typedef base_uint<160> base_uint160; -typedef base_uint<256> base_uint256; - - - -// -// uint160 and uint256 could be implemented as templates, but to keep -// compile errors and debugging cleaner, they're copy and pasted. -// - - - -////////////////////////////////////////////////////////////////////////////// -// -// uint160 -// - -class uint160 : public base_uint160 -{ -public: - typedef base_uint160 basetype; - - uint160() - { - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - } - - uint160(const basetype& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = b.pn[i]; - } - - uint160& operator=(const basetype& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = b.pn[i]; - return *this; - } - - uint160(uint64 b) - { - pn[0] = (unsigned int)b; - pn[1] = (unsigned int)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - } - - uint160& operator=(uint64 b) - { - pn[0] = (unsigned int)b; - pn[1] = (unsigned int)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - return *this; - } - - explicit uint160(const std::string& str) - { - SetHex(str); - } - - explicit uint160(const std::vector& vch) - { - if (vch.size() == sizeof(pn)) - memcpy(pn, &vch[0], sizeof(pn)); - else - *this = 0; - } -}; - -inline bool operator==(const uint160& a, uint64 b) { return (base_uint160)a == b; } -inline bool operator!=(const uint160& a, uint64 b) { return (base_uint160)a != b; } -inline const uint160 operator<<(const base_uint160& a, unsigned int shift) { return uint160(a) <<= shift; } -inline const uint160 operator>>(const base_uint160& a, unsigned int shift) { return uint160(a) >>= shift; } -inline const uint160 operator<<(const uint160& a, unsigned int shift) { return uint160(a) <<= shift; } -inline const uint160 operator>>(const uint160& a, unsigned int shift) { return uint160(a) >>= shift; } - -inline const uint160 operator^(const base_uint160& a, const base_uint160& b) { return uint160(a) ^= b; } -inline const uint160 operator&(const base_uint160& a, const base_uint160& b) { return uint160(a) &= b; } -inline const uint160 operator|(const base_uint160& a, const base_uint160& b) { return uint160(a) |= b; } -inline const uint160 operator+(const base_uint160& a, const base_uint160& b) { return uint160(a) += b; } -inline const uint160 operator-(const base_uint160& a, const base_uint160& b) { return uint160(a) -= b; } - -inline bool operator<(const base_uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } -inline bool operator<=(const base_uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } -inline bool operator>(const base_uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } -inline bool operator>=(const base_uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } -inline bool operator==(const base_uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } -inline bool operator!=(const base_uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } -inline const uint160 operator^(const base_uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } -inline const uint160 operator&(const base_uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } -inline const uint160 operator|(const base_uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } -inline const uint160 operator+(const base_uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } -inline const uint160 operator-(const base_uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } - -inline bool operator<(const uint160& a, const base_uint160& b) { return (base_uint160)a < (base_uint160)b; } -inline bool operator<=(const uint160& a, const base_uint160& b) { return (base_uint160)a <= (base_uint160)b; } -inline bool operator>(const uint160& a, const base_uint160& b) { return (base_uint160)a > (base_uint160)b; } -inline bool operator>=(const uint160& a, const base_uint160& b) { return (base_uint160)a >= (base_uint160)b; } -inline bool operator==(const uint160& a, const base_uint160& b) { return (base_uint160)a == (base_uint160)b; } -inline bool operator!=(const uint160& a, const base_uint160& b) { return (base_uint160)a != (base_uint160)b; } -inline const uint160 operator^(const uint160& a, const base_uint160& b) { return (base_uint160)a ^ (base_uint160)b; } -inline const uint160 operator&(const uint160& a, const base_uint160& b) { return (base_uint160)a & (base_uint160)b; } -inline const uint160 operator|(const uint160& a, const base_uint160& b) { return (base_uint160)a | (base_uint160)b; } -inline const uint160 operator+(const uint160& a, const base_uint160& b) { return (base_uint160)a + (base_uint160)b; } -inline const uint160 operator-(const uint160& a, const base_uint160& b) { return (base_uint160)a - (base_uint160)b; } - -inline bool operator<(const uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } -inline bool operator<=(const uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } -inline bool operator>(const uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } -inline bool operator>=(const uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } -inline bool operator==(const uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } -inline bool operator!=(const uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } -inline const uint160 operator^(const uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } -inline const uint160 operator&(const uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } -inline const uint160 operator|(const uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } -inline const uint160 operator+(const uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } -inline const uint160 operator-(const uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// uint256 -// - -class uint256 : public base_uint256 -{ -public: - typedef base_uint256 basetype; - - uint256() - { - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - } - - uint256(const basetype& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = b.pn[i]; - } - - uint256& operator=(const basetype& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = b.pn[i]; - return *this; - } - - uint256(uint64 b) - { - pn[0] = (unsigned int)b; - pn[1] = (unsigned int)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - } - - uint256& operator=(uint64 b) - { - pn[0] = (unsigned int)b; - pn[1] = (unsigned int)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - return *this; - } - - explicit uint256(const std::string& str) - { - SetHex(str); - } - - explicit uint256(const std::vector& vch) - { - if (vch.size() == sizeof(pn)) - memcpy(pn, &vch[0], sizeof(pn)); - else - *this = 0; - } -}; - -inline bool operator==(const uint256& a, uint64 b) { return (base_uint256)a == b; } -inline bool operator!=(const uint256& a, uint64 b) { return (base_uint256)a != b; } -inline const uint256 operator<<(const base_uint256& a, unsigned int shift) { return uint256(a) <<= shift; } -inline const uint256 operator>>(const base_uint256& a, unsigned int shift) { return uint256(a) >>= shift; } -inline const uint256 operator<<(const uint256& a, unsigned int shift) { return uint256(a) <<= shift; } -inline const uint256 operator>>(const uint256& a, unsigned int shift) { return uint256(a) >>= shift; } - -inline const uint256 operator^(const base_uint256& a, const base_uint256& b) { return uint256(a) ^= b; } -inline const uint256 operator&(const base_uint256& a, const base_uint256& b) { return uint256(a) &= b; } -inline const uint256 operator|(const base_uint256& a, const base_uint256& b) { return uint256(a) |= b; } -inline const uint256 operator+(const base_uint256& a, const base_uint256& b) { return uint256(a) += b; } -inline const uint256 operator-(const base_uint256& a, const base_uint256& b) { return uint256(a) -= b; } - -inline bool operator<(const base_uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } -inline bool operator<=(const base_uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } -inline bool operator>(const base_uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } -inline bool operator>=(const base_uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } -inline bool operator==(const base_uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } -inline bool operator!=(const base_uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } -inline const uint256 operator^(const base_uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } -inline const uint256 operator&(const base_uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } -inline const uint256 operator|(const base_uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } -inline const uint256 operator+(const base_uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } -inline const uint256 operator-(const base_uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } - -inline bool operator<(const uint256& a, const base_uint256& b) { return (base_uint256)a < (base_uint256)b; } -inline bool operator<=(const uint256& a, const base_uint256& b) { return (base_uint256)a <= (base_uint256)b; } -inline bool operator>(const uint256& a, const base_uint256& b) { return (base_uint256)a > (base_uint256)b; } -inline bool operator>=(const uint256& a, const base_uint256& b) { return (base_uint256)a >= (base_uint256)b; } -inline bool operator==(const uint256& a, const base_uint256& b) { return (base_uint256)a == (base_uint256)b; } -inline bool operator!=(const uint256& a, const base_uint256& b) { return (base_uint256)a != (base_uint256)b; } -inline const uint256 operator^(const uint256& a, const base_uint256& b) { return (base_uint256)a ^ (base_uint256)b; } -inline const uint256 operator&(const uint256& a, const base_uint256& b) { return (base_uint256)a & (base_uint256)b; } -inline const uint256 operator|(const uint256& a, const base_uint256& b) { return (base_uint256)a | (base_uint256)b; } -inline const uint256 operator+(const uint256& a, const base_uint256& b) { return (base_uint256)a + (base_uint256)b; } -inline const uint256 operator-(const uint256& a, const base_uint256& b) { return (base_uint256)a - (base_uint256)b; } - -inline bool operator<(const uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } -inline bool operator<=(const uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } -inline bool operator>(const uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } -inline bool operator>=(const uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } -inline bool operator==(const uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } -inline bool operator!=(const uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } -inline const uint256 operator^(const uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } -inline const uint256 operator&(const uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } -inline const uint256 operator|(const uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } -inline const uint256 operator+(const uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } -inline const uint256 operator-(const uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } - - - - - - - - - - - - -inline int Testuint256AdHoc(std::vector vArg) -{ - uint256 g(0); - - - printf("%s\n", g.ToString().c_str()); - g--; printf("g--\n"); - printf("%s\n", g.ToString().c_str()); - g--; printf("g--\n"); - printf("%s\n", g.ToString().c_str()); - g++; printf("g++\n"); - printf("%s\n", g.ToString().c_str()); - g++; printf("g++\n"); - printf("%s\n", g.ToString().c_str()); - g++; printf("g++\n"); - printf("%s\n", g.ToString().c_str()); - g++; printf("g++\n"); - printf("%s\n", g.ToString().c_str()); - - - - uint256 a(7); - printf("a=7\n"); - printf("%s\n", a.ToString().c_str()); - - uint256 b; - printf("b undefined\n"); - printf("%s\n", b.ToString().c_str()); - int c = 3; - - a = c; - a.pn[3] = 15; - printf("%s\n", a.ToString().c_str()); - uint256 k(c); - - a = 5; - a.pn[3] = 15; - printf("%s\n", a.ToString().c_str()); - b = 1; - b <<= 52; - - a |= b; - - a ^= 0x500; - - printf("a %s\n", a.ToString().c_str()); - - a = a | b | (uint256)0x1000; - - - printf("a %s\n", a.ToString().c_str()); - printf("b %s\n", b.ToString().c_str()); - - a = 0xfffffffe; - a.pn[4] = 9; - - printf("%s\n", a.ToString().c_str()); - a++; - printf("%s\n", a.ToString().c_str()); - a++; - printf("%s\n", a.ToString().c_str()); - a++; - printf("%s\n", a.ToString().c_str()); - a++; - printf("%s\n", a.ToString().c_str()); - - a--; - printf("%s\n", a.ToString().c_str()); - a--; - printf("%s\n", a.ToString().c_str()); - a--; - printf("%s\n", a.ToString().c_str()); - uint256 d = a--; - printf("%s\n", d.ToString().c_str()); - printf("%s\n", a.ToString().c_str()); - a--; - printf("%s\n", a.ToString().c_str()); - a--; - printf("%s\n", a.ToString().c_str()); - - d = a; - - printf("%s\n", d.ToString().c_str()); - for (int i = uint256::WIDTH-1; i >= 0; i--) printf("%08x", d.pn[i]); printf("\n"); - - uint256 neg = d; - neg = ~neg; - printf("%s\n", neg.ToString().c_str()); - - - uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111"); - printf("\n"); - printf("%s\n", e.ToString().c_str()); - - - printf("\n"); - uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111"); - uint256 x2; - printf("%s\n", x1.ToString().c_str()); - for (int i = 0; i < 270; i += 4) - { - x2 = x1 << i; - printf("%s\n", x2.ToString().c_str()); - } - - printf("\n"); - printf("%s\n", x1.ToString().c_str()); - for (int i = 0; i < 270; i += 4) - { - x2 = x1; - x2 >>= i; - printf("%s\n", x2.ToString().c_str()); - } - - - for (int i = 0; i < 100; i++) - { - uint256 k = (~uint256(0) >> i); - printf("%s\n", k.ToString().c_str()); - } - - for (int i = 0; i < 100; i++) - { - uint256 k = (~uint256(0) << i); - printf("%s\n", k.ToString().c_str()); - } - - return (0); -} - -#endif diff --git a/lib/include/util.h b/lib/include/util.h deleted file mode 100644 index e25004fa1c..0000000000 --- a/lib/include/util.h +++ /dev/null @@ -1,679 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_UTIL_H -#define BITCOIN_UTIL_H - -#include "uint256.h" -//#include "cryptopp/sha.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - - -#if defined(_MSC_VER) || defined(__BORLANDC__) -typedef __int64 int64; -typedef unsigned __int64 uint64; -#else -typedef long long int64; -typedef unsigned long long uint64; -#endif -#if defined(_MSC_VER) && _MSC_VER < 1300 -#define for if (false) ; else for -#endif -#ifndef _MSC_VER -#define __forceinline inline -#endif - -//#define foreach BOOST_FOREACH -#define loop for (;;) -#define BEGIN(a) ((char*)&(a)) -#define END(a) ((char*)&((&(a))[1])) -#define UBEGIN(a) ((unsigned char*)&(a)) -#define UEND(a) ((unsigned char*)&((&(a))[1])) -#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) -#define printf OutputDebugStringF - -#ifdef snprintf -#undef snprintf -#endif -#define snprintf my_snprintf - -#ifndef PRI64d -#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MSVCRT__) -#define PRI64d "I64d" -#define PRI64u "I64u" -#define PRI64x "I64x" -#else -#define PRI64d "lld" -#define PRI64u "llu" -#define PRI64x "llx" -#endif -#endif - -// This is needed because the foreach macro can't get over the comma in pair -#define PAIRTYPE(t1, t2) pair - -// Used to bypass the rule against non-const reference to temporary -// where it makes sense with wrappers such as CFlatData or CTxDB -template -inline T& REF(const T& val) -{ - return (T&)val; -} - -// Align by increasing pointer, must have extra space at end of buffer -template -T* alignup(T* p) -{ - union - { - T* ptr; - size_t n; - } u; - u.ptr = p; - u.n = (u.n + (nBytes-1)) & ~(nBytes-1); - return u.ptr; -} - -#ifdef __WXMSW__ -#define MSG_NOSIGNAL 0 -#define MSG_DONTWAIT 0 -#ifndef UINT64_MAX -#define UINT64_MAX _UI64_MAX -#define INT64_MAX _I64_MAX -#define INT64_MIN _I64_MIN -#endif -#ifndef S_IRUSR -#define S_IRUSR 0400 -#define S_IWUSR 0200 -#endif -#define unlink _unlink -typedef int socklen_t; -#else -#define WSAGetLastError() errno -#define WSAEWOULDBLOCK EWOULDBLOCK -#define WSAEMSGSIZE EMSGSIZE -#define WSAEINTR EINTR -#define WSAEINPROGRESS EINPROGRESS -#define WSAEADDRINUSE EADDRINUSE -#define WSAENOTSOCK EBADF -#define INVALID_SOCKET (SOCKET)(~0) -#define SOCKET_ERROR -1 -typedef u_int SOCKET; -#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) -#define strlwr(psz) to_lower(psz) -#define _strlwr(psz) to_lower(psz) -#define MAX_PATH 1024 -#define Beep(n1,n2) (0) -inline void Sleep(int64 n) -{ - boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(n)); -} -#endif - -inline int myclosesocket(SOCKET& hSocket) -{ - if (hSocket == INVALID_SOCKET) - return WSAENOTSOCK; -#ifdef __WXMSW__ - int ret = closesocket(hSocket); -#else - int ret = close(hSocket); -#endif - hSocket = INVALID_SOCKET; - return ret; -} -#define closesocket(s) myclosesocket(s) - -#ifndef GUI -inline const char* _(const char* psz) -{ - return psz; -} -#endif - - - - - - - - - - -extern std::map mapArgs; -extern std::map > mapMultiArgs; -extern bool fDebug; -extern bool fPrintToConsole; -extern bool fPrintToDebugger; -extern char pszSetDataDir[MAX_PATH]; -extern bool fRequestShutdown; -extern bool fShutdown; -extern bool fDaemon; -extern bool fServer; -extern bool fCommandLine; -extern std::string strMiscWarning; -extern bool fTestNet; -extern bool fNoListen; -extern bool fLogTimestamps; - -void RandAddSeed(); -void RandAddSeedPerfmon(); -int OutputDebugStringF(const char* pszFormat, ...); -int my_snprintf(char* buffer, size_t limit, const char* format, ...); -std::string strprintf(const char* format, ...); -bool error(const char* format, ...); -void LogException(std::exception* pex, const char* pszThread); -void PrintException(std::exception* pex, const char* pszThread); -void PrintExceptionContinue(std::exception* pex, const char* pszThread); -void ParseString(const std::string& str, char c, std::vector& v); -std::string FormatMoney(int64 n, bool fPlus=false); -bool ParseMoney(const std::string& str, int64& nRet); -bool ParseMoney(const char* pszIn, int64& nRet); -std::vector ParseHex(const char* psz); -std::vector ParseHex(const std::string& str); -void ParseParameters(int argc, char* argv[]); -const char* wxGetTranslation(const char* psz); -bool WildcardMatch(const char* psz, const char* mask); -bool WildcardMatch(const std::string& str, const std::string& mask); -int GetFilesize(FILE* file); -void GetDataDir(char* pszDirRet); -std::string GetConfigFile(); -std::string GetPidFile(); -void CreatePidFile(std::string pidFile, pid_t pid); -void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); -#ifdef __WXMSW__ -string MyGetSpecialFolderPath(int nFolder, bool fCreate); -#endif -std::string GetDefaultDataDir(); -std::string GetDataDir(); -void ShrinkDebugFile(); -int GetRandInt(int nMax); -uint64 GetRand(uint64 nMax); -int64 GetTime(); -int64 GetAdjustedTime(); -void AddTimeData(unsigned int ip, int64 nTime); -std::string FormatFullVersion(); - - - - - - - - - - - - - -// Wrapper to automatically initialize critical sections -class CCriticalSection -{ -#ifdef __WXMSW__ -protected: - CRITICAL_SECTION cs; -public: - explicit CCriticalSection() { InitializeCriticalSection(&cs); } - ~CCriticalSection() { DeleteCriticalSection(&cs); } - void Enter() { EnterCriticalSection(&cs); } - void Leave() { LeaveCriticalSection(&cs); } - bool TryEnter() { return TryEnterCriticalSection(&cs); } -#else -protected: - boost::interprocess::interprocess_recursive_mutex mutex; -public: - explicit CCriticalSection() { } - ~CCriticalSection() { } - void Enter() { mutex.lock(); } - void Leave() { mutex.unlock(); } - bool TryEnter() { return mutex.try_lock(); } -#endif -public: - const char* pszFile; - int nLine; -}; - -// Automatically leave critical section when leaving block, needed for exception safety -class CCriticalBlock -{ -protected: - CCriticalSection* pcs; -public: - CCriticalBlock(CCriticalSection& csIn) { pcs = &csIn; pcs->Enter(); } - ~CCriticalBlock() { pcs->Leave(); } -}; - -// WARNING: This will catch continue and break! -// break is caught with an assertion, but there's no way to detect continue. -// I'd rather be careful than suffer the other more error prone syntax. -// The compiler will optimise away all this loop junk. -#define CRITICAL_BLOCK(cs) \ - for (bool fcriticalblockonce=true; fcriticalblockonce; assert(("break caught by CRITICAL_BLOCK!", !fcriticalblockonce)), fcriticalblockonce=false) \ - for (CCriticalBlock criticalblock(cs); fcriticalblockonce && (cs.pszFile=__FILE__, cs.nLine=__LINE__, true); fcriticalblockonce=false, cs.pszFile=NULL, cs.nLine=0) - -class CTryCriticalBlock -{ -protected: - CCriticalSection* pcs; -public: - CTryCriticalBlock(CCriticalSection& csIn) { pcs = (csIn.TryEnter() ? &csIn : NULL); } - ~CTryCriticalBlock() { if (pcs) pcs->Leave(); } - bool Entered() { return pcs != NULL; } -}; - -#define TRY_CRITICAL_BLOCK(cs) \ - for (bool fcriticalblockonce=true; fcriticalblockonce; assert(("break caught by TRY_CRITICAL_BLOCK!", !fcriticalblockonce)), fcriticalblockonce=false) \ - for (CTryCriticalBlock criticalblock(cs); fcriticalblockonce && (fcriticalblockonce = criticalblock.Entered()) && (cs.pszFile=__FILE__, cs.nLine=__LINE__, true); fcriticalblockonce=false, cs.pszFile=NULL, cs.nLine=0) - - - - - - - - - - -inline std::string i64tostr(int64 n) -{ - return strprintf("%"PRI64d, n); -} - -inline std::string itostr(int n) -{ - return strprintf("%d", n); -} - -inline int64 atoi64(const char* psz) -{ -#ifdef _MSC_VER - return _atoi64(psz); -#else - return strtoll(psz, NULL, 10); -#endif -} - -inline int64 atoi64(const std::string& str) -{ -#ifdef _MSC_VER - return _atoi64(str.c_str()); -#else - return strtoll(str.c_str(), NULL, 10); -#endif -} - -inline int atoi(const std::string& str) -{ - return atoi(str.c_str()); -} - -inline int roundint(double d) -{ - return (int)(d > 0 ? d + 0.5 : d - 0.5); -} - -inline int64 roundint64(double d) -{ - return (int64)(d > 0 ? d + 0.5 : d - 0.5); -} - -inline int64 abs64(int64 n) -{ - return (n >= 0 ? n : -n); -} - -template -std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) -{ - if (itbegin == itend) - return ""; - const unsigned char* pbegin = (const unsigned char*)&itbegin[0]; - const unsigned char* pend = pbegin + (itend - itbegin) * sizeof(itbegin[0]); - std::string str; - str.reserve((pend-pbegin) * (fSpaces ? 3 : 2)); - for (const unsigned char* p = pbegin; p != pend; p++) - str += strprintf((fSpaces && p != pend-1 ? "%02x " : "%02x"), *p); - return str; -} - -inline std::string HexStr(const std::vector& vch, bool fSpaces=false) -{ - return HexStr(vch.begin(), vch.end(), fSpaces); -} - -template -std::string HexNumStr(const T itbegin, const T itend, bool f0x=true) -{ - if (itbegin == itend) - return ""; - const unsigned char* pbegin = (const unsigned char*)&itbegin[0]; - const unsigned char* pend = pbegin + (itend - itbegin) * sizeof(itbegin[0]); - std::string str = (f0x ? "0x" : ""); - str.reserve(str.size() + (pend-pbegin) * 2); - for (const unsigned char* p = pend-1; p >= pbegin; p--) - str += strprintf("%02x", *p); - return str; -} - -inline std::string HexNumStr(const std::vector& vch, bool f0x=true) -{ - return HexNumStr(vch.begin(), vch.end(), f0x); -} - -template -void PrintHex(const T pbegin, const T pend, const char* pszFormat="%s", bool fSpaces=true) -{ - printf(pszFormat, HexStr(pbegin, pend, fSpaces).c_str()); -} - -inline void PrintHex(const std::vector& vch, const char* pszFormat="%s", bool fSpaces=true) -{ - printf(pszFormat, HexStr(vch, fSpaces).c_str()); -} - -inline int64 GetPerformanceCounter() -{ - int64 nCounter = 0; -#ifdef __WXMSW__ - QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); -#else - timeval t; - gettimeofday(&t, NULL); - nCounter = t.tv_sec * 1000000 + t.tv_usec; -#endif - return nCounter; -} - -inline int64 GetTimeMillis() -{ - return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - - boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); -} - -inline std::string DateTimeStrFormat(const char* pszFormat, int64 nTime) -{ - time_t n = nTime; - struct tm* ptmTime = gmtime(&n); - char pszTime[200]; - strftime(pszTime, sizeof(pszTime), pszFormat, ptmTime); - return pszTime; -} - -template -void skipspaces(T& it) -{ - while (isspace(*it)) - ++it; -} - -inline bool IsSwitchChar(char c) -{ -#ifdef __WXMSW__ - return c == '-' || c == '/'; -#else - return c == '-'; -#endif -} - -inline std::string GetArg(const std::string& strArg, const std::string& strDefault) -{ - if (mapArgs.count(strArg)) - return mapArgs[strArg]; - return strDefault; -} - -inline int64 GetArg(const std::string& strArg, int64 nDefault) -{ - if (mapArgs.count(strArg)) - return atoi64(mapArgs[strArg]); - return nDefault; -} - -inline bool GetBoolArg(const std::string& strArg) -{ - if (mapArgs.count(strArg)) - { - if (mapArgs[strArg].empty()) - return true; - return (atoi(mapArgs[strArg]) != 0); - } - return false; -} - - - - - - - - - - -inline void heapchk() -{ -#ifdef __WXMSW__ - /// for debugging - //if (_heapchk() != _HEAPOK) - // DebugBreak(); -#endif -} - -// Randomize the stack to help protect against buffer overrun exploits -#define IMPLEMENT_RANDOMIZE_STACK(ThreadFn) \ - { \ - static char nLoops; \ - if (nLoops <= 0) \ - nLoops = GetRand(20) + 1; \ - if (nLoops-- > 1) \ - { \ - ThreadFn; \ - return; \ - } \ - } - -#define CATCH_PRINT_EXCEPTION(pszFn) \ - catch (std::exception& e) { \ - PrintException(&e, (pszFn)); \ - } catch (...) { \ - PrintException(NULL, (pszFn)); \ - } - - - - - - - - - - -template -inline uint256 Hash(const T1 pbegin, const T1 pend) -{ - static unsigned char pblank[1]; - uint256 hash1; - SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1); - uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; -} - -template -inline uint256 Hash(const T1 p1begin, const T1 p1end, - const T2 p2begin, const T2 p2end) -{ - static unsigned char pblank[1]; - uint256 hash1; - SHA256_CTX ctx; - SHA256_Init(&ctx); - SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); - SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); - SHA256_Final((unsigned char*)&hash1, &ctx); - uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; -} - -template -inline uint256 Hash(const T1 p1begin, const T1 p1end, - const T2 p2begin, const T2 p2end, - const T3 p3begin, const T3 p3end) -{ - static unsigned char pblank[1]; - uint256 hash1; - SHA256_CTX ctx; - SHA256_Init(&ctx); - SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); - SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); - SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0])); - SHA256_Final((unsigned char*)&hash1, &ctx); - uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; -} - -template -uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=VERSION) -{ - // Most of the time is spent allocating and deallocating CDataStream's - // buffer. If this ever needs to be optimized further, make a CStaticStream - // class with its buffer on the stack. - CDataStream ss(nType, nVersion); - ss.reserve(10000); - ss << obj; - return Hash(ss.begin(), ss.end()); -} - -inline uint160 Hash160(const std::vector& vch) -{ - uint256 hash1; - SHA256(&vch[0], vch.size(), (unsigned char*)&hash1); - uint160 hash2; - RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; -} - - - - - - - - - - - -// Note: It turns out we might have been able to use boost::thread -// by using TerminateThread(boost::thread.native_handle(), 0); -#ifdef __WXMSW__ -typedef HANDLE pthread_t; - -inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) -{ - DWORD nUnused = 0; - HANDLE hthread = - CreateThread( - NULL, // default security - 0, // inherit stack size from parent - (LPTHREAD_START_ROUTINE)pfn, // function pointer - parg, // argument - 0, // creation option, start immediately - &nUnused); // thread identifier - if (hthread == NULL) - { - printf("Error: CreateThread() returned %d\n", GetLastError()); - return (pthread_t)0; - } - if (!fWantHandle) - { - CloseHandle(hthread); - return (pthread_t)-1; - } - return hthread; -} - -inline void SetThreadPriority(int nPriority) -{ - SetThreadPriority(GetCurrentThread(), nPriority); -} -#else -inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) -{ - pthread_t hthread = 0; - int ret = pthread_create(&hthread, NULL, (void*(*)(void*))pfn, parg); - if (ret != 0) - { - printf("Error: pthread_create() returned %d\n", ret); - return (pthread_t)0; - } - if (!fWantHandle) - return (pthread_t)-1; - return hthread; -} - -#define THREAD_PRIORITY_LOWEST PRIO_MAX -#define THREAD_PRIORITY_BELOW_NORMAL 2 -#define THREAD_PRIORITY_NORMAL 0 -#define THREAD_PRIORITY_ABOVE_NORMAL 0 - -inline void SetThreadPriority(int nPriority) -{ - // It's unclear if it's even possible to change thread priorities on Linux, - // but we really and truly need it for the generation threads. -#ifdef PRIO_THREAD - setpriority(PRIO_THREAD, 0, nPriority); -#else - setpriority(PRIO_PROCESS, 0, nPriority); -#endif -} - -inline bool TerminateThread(pthread_t hthread, unsigned int nExitCode) -{ - return (pthread_cancel(hthread) == 0); -} - -inline void ExitThread(unsigned int nExitCode) -{ - pthread_exit((void*)nExitCode); -} -#endif - - - - - -inline bool AffinityBugWorkaround(void(*pfn)(void*)) -{ -#ifdef __WXMSW__ - // Sometimes after a few hours affinity gets stuck on one processor - DWORD dwProcessAffinityMask = -1; - DWORD dwSystemAffinityMask = -1; - GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask); - DWORD dwPrev1 = SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinityMask); - DWORD dwPrev2 = SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinityMask); - if (dwPrev2 != dwProcessAffinityMask) - { - printf("AffinityBugWorkaround() : SetThreadAffinityMask=%d, ProcessAffinityMask=%d, restarting thread\n", dwPrev2, dwProcessAffinityMask); - if (!CreateThread(pfn, NULL)) - printf("Error: CreateThread() failed\n"); - return true; - } -#endif - return false; -} - -#endif -- cgit v1.2.3 From 2097c09a9b5dda5bfa0de1906a8c155c3c3b529b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 14 May 2011 17:25:05 +0200 Subject: integrate a few extra .h files --- bitcoin.pro | 10 +- core/include/bignum.h | 5 +- core/include/key.h | 171 +++ core/include/main.h | 2059 +++++++++++++++++++++++++++++++++ core/include/net.h | 1046 +++++++++++++++++ core/include/serialize.h | 3 + core/include/strlcpy.h | 86 ++ core/include/util.h | 2 +- core/src/util.cpp | 917 +++++++++++++++ gui/include/bitcoinaddressvalidator.h | 2 - gui/src/sendcoinsdialog.cpp | 13 +- 11 files changed, 4304 insertions(+), 10 deletions(-) create mode 100644 core/include/key.h create mode 100644 core/include/main.h create mode 100644 core/include/net.h create mode 100644 core/include/strlcpy.h create mode 100644 core/src/util.cpp diff --git a/bitcoin.pro b/bitcoin.pro index 84ecc9a0b2..69340137aa 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -2,6 +2,7 @@ TEMPLATE = app TARGET = DEPENDPATH += . INCLUDEPATH += gui/include core/include cryptopp/include +unix:LIBS += -lssl # Input HEADERS += gui/include/bitcoingui.h \ @@ -29,7 +30,11 @@ HEADERS += gui/include/bitcoingui.h \ cryptopp/include/cryptopp/iterhash.h \ cryptopp/include/cryptopp/cryptlib.h \ cryptopp/include/cryptopp/cpu.h \ - cryptopp/include/cryptopp/config.h + cryptopp/include/cryptopp/config.h \ + core/include/strlcpy.h \ + core/include/main.h \ + core/include/net.h \ + core/include/key.h SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/transactiontablemodel.cpp \ gui/src/addresstablemodel.cpp \ @@ -41,7 +46,8 @@ SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/editaddressdialog.cpp \ gui/src/bitcoinaddressvalidator.cpp \ cryptopp/src/sha.cpp \ - cryptopp/src/cpu.cpp + cryptopp/src/cpu.cpp \ + core/src/util.cpp RESOURCES += \ gui/bitcoin.qrc diff --git a/core/include/bignum.h b/core/include/bignum.h index c207af1004..5b4c78e7fa 100644 --- a/core/include/bignum.h +++ b/core/include/bignum.h @@ -7,11 +7,8 @@ #include #include #include -#include - - - +#include "util.h" class bignum_error : public std::runtime_error { diff --git a/core/include/key.h b/core/include/key.h new file mode 100644 index 0000000000..390602e1ef --- /dev/null +++ b/core/include/key.h @@ -0,0 +1,171 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_KEY_H +#define BITCOIN_KEY_H + +// secp160k1 +// const unsigned int PRIVATE_KEY_SIZE = 192; +// const unsigned int PUBLIC_KEY_SIZE = 41; +// const unsigned int SIGNATURE_SIZE = 48; +// +// secp192k1 +// const unsigned int PRIVATE_KEY_SIZE = 222; +// const unsigned int PUBLIC_KEY_SIZE = 49; +// const unsigned int SIGNATURE_SIZE = 57; +// +// secp224k1 +// const unsigned int PRIVATE_KEY_SIZE = 250; +// const unsigned int PUBLIC_KEY_SIZE = 57; +// const unsigned int SIGNATURE_SIZE = 66; +// +// secp256k1: +// const unsigned int PRIVATE_KEY_SIZE = 279; +// const unsigned int PUBLIC_KEY_SIZE = 65; +// const unsigned int SIGNATURE_SIZE = 72; +// +// see www.keylength.com +// script supports up to 75 for single byte push + + + +class key_error : public std::runtime_error +{ +public: + explicit key_error(const std::string& str) : std::runtime_error(str) {} +}; + + +// secure_allocator is defined in serialize.h +typedef std::vector > CPrivKey; + + + +class CKey +{ +protected: + EC_KEY* pkey; + bool fSet; + +public: + CKey() + { + pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + if (pkey == NULL) + throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed"); + fSet = false; + } + + CKey(const CKey& b) + { + pkey = EC_KEY_dup(b.pkey); + if (pkey == NULL) + throw key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed"); + fSet = b.fSet; + } + + CKey& operator=(const CKey& b) + { + if (!EC_KEY_copy(pkey, b.pkey)) + throw key_error("CKey::operator=(const CKey&) : EC_KEY_copy failed"); + fSet = b.fSet; + return (*this); + } + + ~CKey() + { + EC_KEY_free(pkey); + } + + bool IsNull() const + { + return !fSet; + } + + void MakeNewKey() + { + if (!EC_KEY_generate_key(pkey)) + throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed"); + fSet = true; + } + + bool SetPrivKey(const CPrivKey& vchPrivKey) + { + const unsigned char* pbegin = &vchPrivKey[0]; + if (!d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size())) + return false; + fSet = true; + return true; + } + + CPrivKey GetPrivKey() const + { + unsigned int nSize = i2d_ECPrivateKey(pkey, NULL); + if (!nSize) + throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed"); + CPrivKey vchPrivKey(nSize, 0); + unsigned char* pbegin = &vchPrivKey[0]; + if (i2d_ECPrivateKey(pkey, &pbegin) != nSize) + throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size"); + return vchPrivKey; + } + + bool SetPubKey(const vector& vchPubKey) + { + const unsigned char* pbegin = &vchPubKey[0]; + if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size())) + return false; + fSet = true; + return true; + } + + vector GetPubKey() const + { + unsigned int nSize = i2o_ECPublicKey(pkey, NULL); + if (!nSize) + throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed"); + vector vchPubKey(nSize, 0); + unsigned char* pbegin = &vchPubKey[0]; + if (i2o_ECPublicKey(pkey, &pbegin) != nSize) + throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size"); + return vchPubKey; + } + + bool Sign(uint256 hash, vector& vchSig) + { + vchSig.clear(); + unsigned char pchSig[10000]; + unsigned int nSize = 0; + if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), pchSig, &nSize, pkey)) + return false; + vchSig.resize(nSize); + memcpy(&vchSig[0], pchSig, nSize); + return true; + } + + bool Verify(uint256 hash, const vector& vchSig) + { + // -1 = error, 0 = bad sig, 1 = good + if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) + return false; + return true; + } + + static bool Sign(const CPrivKey& vchPrivKey, uint256 hash, vector& vchSig) + { + CKey key; + if (!key.SetPrivKey(vchPrivKey)) + return false; + return key.Sign(hash, vchSig); + } + + static bool Verify(const vector& vchPubKey, uint256 hash, const vector& vchSig) + { + CKey key; + if (!key.SetPubKey(vchPubKey)) + return false; + return key.Verify(hash, vchSig); + } +}; + +#endif diff --git a/core/include/main.h b/core/include/main.h new file mode 100644 index 0000000000..7c9ace06c3 --- /dev/null +++ b/core/include/main.h @@ -0,0 +1,2059 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_MAIN_H +#define BITCOIN_MAIN_H + +#include "bignum.h" +#include "net.h" +#include "key.h" + +class COutPoint; +class CInPoint; +class CDiskTxPos; +class CCoinBase; +class CTxIn; +class CTxOut; +class CTransaction; +class CBlock; +class CBlockIndex; +class CWalletTx; +class CKeyItem; + +static const unsigned int MAX_BLOCK_SIZE = 1000000; +static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; +static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; +static const int64 COIN = 100000000; +static const int64 CENT = 1000000; +static const int64 MIN_TX_FEE = 50000; +static const int64 MAX_MONEY = 21000000 * COIN; +inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } +static const int COINBASE_MATURITY = 100; +#ifdef USE_UPNP +static const int fHaveUPnP = true; +#else +static const int fHaveUPnP = false; +#endif + + + + + + +extern CCriticalSection cs_main; +extern std::map mapBlockIndex; +extern uint256 hashGenesisBlock; +extern CBigNum bnProofOfWorkLimit; +extern CBlockIndex* pindexGenesisBlock; +extern int nBestHeight; +extern CBigNum bnBestChainWork; +extern CBigNum bnBestInvalidWork; +extern uint256 hashBestChain; +extern CBlockIndex* pindexBest; +extern unsigned int nTransactionsUpdated; +extern std::map mapRequestCount; +extern CCriticalSection cs_mapRequestCount; +extern std::map mapAddressBook; +extern CCriticalSection cs_mapAddressBook; +extern std::vector vchDefaultKey; +extern double dHashesPerSec; +extern int64 nHPSTimerStart; + +// Settings +extern int fGenerateBitcoins; +extern int64 nTransactionFee; +extern CAddress addrIncoming; +extern int fLimitProcessors; +extern int nLimitProcessors; +extern int fMinimizeToTray; +extern int fMinimizeOnClose; +extern int fUseUPnP; + + + + + + + +bool CheckDiskSpace(uint64 nAdditionalBytes=0); +FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); +FILE* AppendBlockFile(unsigned int& nFileRet); +bool AddKey(const CKey& key); +vector GenerateNewKey(); +bool AddToWallet(const CWalletTx& wtxIn); +void WalletUpdateSpent(const COutPoint& prevout); +int ScanForWalletTransactions(CBlockIndex* pindexStart); +void ReacceptWalletTransactions(); +bool LoadBlockIndex(bool fAllowNew=true); +void PrintBlockTree(); +bool ProcessMessages(CNode* pfrom); +bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv); +bool SendMessages(CNode* pto, bool fSendTrickle); +int64 GetBalance(); +bool CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); +bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); +bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); +bool BroadcastTransaction(CWalletTx& wtxNew); +string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); +string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); +void GenerateBitcoins(bool fGenerate); +void ThreadBitcoinMiner(void* parg); +CBlock* CreateNewBlock(CReserveKey& reservekey); +void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, int64& nPrevTime); +void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); +bool CheckWork(CBlock* pblock, CReserveKey& reservekey); +void BitcoinMiner(); +bool CheckProofOfWork(uint256 hash, unsigned int nBits); +bool IsInitialBlockDownload(); +string GetWarnings(string strFor); + + + + + + + + + + + + +class CDiskTxPos +{ +public: + unsigned int nFile; + unsigned int nBlockPos; + unsigned int nTxPos; + + CDiskTxPos() + { + SetNull(); + } + + CDiskTxPos(unsigned int nFileIn, unsigned int nBlockPosIn, unsigned int nTxPosIn) + { + nFile = nFileIn; + nBlockPos = nBlockPosIn; + nTxPos = nTxPosIn; + } + + IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) + void SetNull() { nFile = -1; nBlockPos = 0; nTxPos = 0; } + bool IsNull() const { return (nFile == -1); } + + friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b) + { + return (a.nFile == b.nFile && + a.nBlockPos == b.nBlockPos && + a.nTxPos == b.nTxPos); + } + + friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b) + { + return !(a == b); + } + + string ToString() const + { + if (IsNull()) + return strprintf("null"); + else + return strprintf("(nFile=%d, nBlockPos=%d, nTxPos=%d)", nFile, nBlockPos, nTxPos); + } + + void print() const + { + printf("%s", ToString().c_str()); + } +}; + + + + +class CInPoint +{ +public: + CTransaction* ptx; + unsigned int n; + + CInPoint() { SetNull(); } + CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } + void SetNull() { ptx = NULL; n = -1; } + bool IsNull() const { return (ptx == NULL && n == -1); } +}; + + + + +class COutPoint +{ +public: + uint256 hash; + unsigned int n; + + COutPoint() { SetNull(); } + COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; } + IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) + void SetNull() { hash = 0; n = -1; } + bool IsNull() const { return (hash == 0 && n == -1); } + + friend bool operator<(const COutPoint& a, const COutPoint& b) + { + return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); + } + + friend bool operator==(const COutPoint& a, const COutPoint& b) + { + return (a.hash == b.hash && a.n == b.n); + } + + friend bool operator!=(const COutPoint& a, const COutPoint& b) + { + return !(a == b); + } + + string ToString() const + { + return strprintf("COutPoint(%s, %d)", hash.ToString().substr(0,10).c_str(), n); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +// +// An input of a transaction. It contains the location of the previous +// transaction's output that it claims and a signature that matches the +// output's public key. +// +class CTxIn +{ +public: + COutPoint prevout; + CScript scriptSig; + unsigned int nSequence; + + CTxIn() + { + nSequence = UINT_MAX; + } + + explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX) + { + prevout = prevoutIn; + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX) + { + prevout = COutPoint(hashPrevTx, nOut); + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(prevout); + READWRITE(scriptSig); + READWRITE(nSequence); + ) + + bool IsFinal() const + { + return (nSequence == UINT_MAX); + } + + friend bool operator==(const CTxIn& a, const CTxIn& b) + { + return (a.prevout == b.prevout && + a.scriptSig == b.scriptSig && + a.nSequence == b.nSequence); + } + + friend bool operator!=(const CTxIn& a, const CTxIn& b) + { + return !(a == b); + } + + string ToString() const + { + string str; + str += strprintf("CTxIn("); + str += prevout.ToString(); + if (prevout.IsNull()) + str += strprintf(", coinbase %s", HexStr(scriptSig).c_str()); + else + str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str()); + if (nSequence != UINT_MAX) + str += strprintf(", nSequence=%u", nSequence); + str += ")"; + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } + + bool IsMine() const; + int64 GetDebit() const; +}; + + + + +// +// An output of a transaction. It contains the public key that the next input +// must be able to sign with to claim it. +// +class CTxOut +{ +public: + int64 nValue; + CScript scriptPubKey; + + CTxOut() + { + SetNull(); + } + + CTxOut(int64 nValueIn, CScript scriptPubKeyIn) + { + nValue = nValueIn; + scriptPubKey = scriptPubKeyIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(nValue); + READWRITE(scriptPubKey); + ) + + void SetNull() + { + nValue = -1; + scriptPubKey.clear(); + } + + bool IsNull() + { + return (nValue == -1); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsMine() const + { + return ::IsMine(scriptPubKey); + } + + int64 GetCredit() const + { + if (!MoneyRange(nValue)) + throw runtime_error("CTxOut::GetCredit() : value out of range"); + return (IsMine() ? nValue : 0); + } + + bool IsChange() const + { + // On a debit transaction, a txout that's mine but isn't in the address book is change + vector vchPubKey; + if (ExtractPubKey(scriptPubKey, true, vchPubKey)) + CRITICAL_BLOCK(cs_mapAddressBook) + if (!mapAddressBook.count(PubKeyToAddress(vchPubKey))) + return true; + return false; + } + + int64 GetChange() const + { + if (!MoneyRange(nValue)) + throw runtime_error("CTxOut::GetChange() : value out of range"); + return (IsChange() ? nValue : 0); + } + + friend bool operator==(const CTxOut& a, const CTxOut& b) + { + return (a.nValue == b.nValue && + a.scriptPubKey == b.scriptPubKey); + } + + friend bool operator!=(const CTxOut& a, const CTxOut& b) + { + return !(a == b); + } + + string ToString() const + { + if (scriptPubKey.size() < 6) + return "CTxOut(error)"; + return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +// +// The basic transaction that is broadcasted on the network and contained in +// blocks. A transaction can contain multiple inputs and outputs. +// +class CTransaction +{ +public: + int nVersion; + vector vin; + vector vout; + unsigned int nLockTime; + + + CTransaction() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(vin); + READWRITE(vout); + READWRITE(nLockTime); + ) + + void SetNull() + { + nVersion = 1; + vin.clear(); + vout.clear(); + nLockTime = 0; + } + + bool IsNull() const + { + return (vin.empty() && vout.empty()); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsFinal(int nBlockHeight=0, int64 nBlockTime=0) const + { + // Time based nLockTime implemented in 0.1.6 + if (nLockTime == 0) + return true; + if (nBlockHeight == 0) + nBlockHeight = nBestHeight; + if (nBlockTime == 0) + nBlockTime = GetAdjustedTime(); + if ((int64)nLockTime < (nLockTime < 500000000 ? (int64)nBlockHeight : nBlockTime)) + return true; + foreach(const CTxIn& txin, vin) + if (!txin.IsFinal()) + return false; + return true; + } + + bool IsNewerThan(const CTransaction& old) const + { + if (vin.size() != old.vin.size()) + return false; + for (int i = 0; i < vin.size(); i++) + if (vin[i].prevout != old.vin[i].prevout) + return false; + + bool fNewer = false; + unsigned int nLowest = UINT_MAX; + for (int i = 0; i < vin.size(); i++) + { + if (vin[i].nSequence != old.vin[i].nSequence) + { + if (vin[i].nSequence <= nLowest) + { + fNewer = false; + nLowest = vin[i].nSequence; + } + if (old.vin[i].nSequence < nLowest) + { + fNewer = true; + nLowest = old.vin[i].nSequence; + } + } + } + return fNewer; + } + + bool IsCoinBase() const + { + return (vin.size() == 1 && vin[0].prevout.IsNull()); + } + + int GetSigOpCount() const + { + int n = 0; + foreach(const CTxIn& txin, vin) + n += txin.scriptSig.GetSigOpCount(); + foreach(const CTxOut& txout, vout) + n += txout.scriptPubKey.GetSigOpCount(); + return n; + } + + bool IsStandard() const + { + foreach(const CTxIn& txin, vin) + if (!txin.scriptSig.IsPushOnly()) + return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str()); + foreach(const CTxOut& txout, vout) + if (!::IsStandard(txout.scriptPubKey)) + return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str()); + return true; + } + + bool IsMine() const + { + foreach(const CTxOut& txout, vout) + if (txout.IsMine()) + return true; + return false; + } + + bool IsFromMe() const + { + return (GetDebit() > 0); + } + + int64 GetDebit() const + { + int64 nDebit = 0; + foreach(const CTxIn& txin, vin) + { + nDebit += txin.GetDebit(); + if (!MoneyRange(nDebit)) + throw runtime_error("CTransaction::GetDebit() : value out of range"); + } + return nDebit; + } + + int64 GetCredit() const + { + int64 nCredit = 0; + foreach(const CTxOut& txout, vout) + { + nCredit += txout.GetCredit(); + if (!MoneyRange(nCredit)) + throw runtime_error("CTransaction::GetCredit() : value out of range"); + } + return nCredit; + } + + int64 GetChange() const + { + if (IsCoinBase()) + return 0; + int64 nChange = 0; + foreach(const CTxOut& txout, vout) + { + nChange += txout.GetChange(); + if (!MoneyRange(nChange)) + throw runtime_error("CTransaction::GetChange() : value out of range"); + } + return nChange; + } + + int64 GetValueOut() const + { + int64 nValueOut = 0; + foreach(const CTxOut& txout, vout) + { + nValueOut += txout.nValue; + if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) + throw runtime_error("CTransaction::GetValueOut() : value out of range"); + } + return nValueOut; + } + + static bool AllowFree(double dPriority) + { + // Large (in bytes) low-priority (new, small-coin) transactions + // need a fee. + return dPriority > COIN * 144 / 250; + } + + int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true) const + { + // Base fee is 1 cent per kilobyte + unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK); + unsigned int nNewBlockSize = nBlockSize + nBytes; + int64 nMinFee = (1 + (int64)nBytes / 1000) * MIN_TX_FEE; + + if (fAllowFree) + { + if (nBlockSize == 1) + { + // Transactions under 10K are free + // (about 4500bc if made of 50bc inputs) + if (nBytes < 10000) + nMinFee = 0; + } + else + { + // Free transaction area + if (nNewBlockSize < 27000) + nMinFee = 0; + } + } + + // To limit dust spam, require MIN_TX_FEE if any output is less than 0.01 + if (nMinFee < MIN_TX_FEE) + foreach(const CTxOut& txout, vout) + if (txout.nValue < CENT) + nMinFee = MIN_TX_FEE; + + // Raise the price as the block approaches full + if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) + { + if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) + return MAX_MONEY; + nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); + } + + if (!MoneyRange(nMinFee)) + nMinFee = MAX_MONEY; + return nMinFee; + } + + + bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL) + { + CAutoFile filein = OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb"); + if (!filein) + return error("CTransaction::ReadFromDisk() : OpenBlockFile failed"); + + // Read transaction + if (fseek(filein, pos.nTxPos, SEEK_SET) != 0) + return error("CTransaction::ReadFromDisk() : fseek failed"); + filein >> *this; + + // Return file pointer + if (pfileRet) + { + if (fseek(filein, pos.nTxPos, SEEK_SET) != 0) + return error("CTransaction::ReadFromDisk() : second fseek failed"); + *pfileRet = filein.release(); + } + return true; + } + + friend bool operator==(const CTransaction& a, const CTransaction& b) + { + return (a.nVersion == b.nVersion && + a.vin == b.vin && + a.vout == b.vout && + a.nLockTime == b.nLockTime); + } + + friend bool operator!=(const CTransaction& a, const CTransaction& b) + { + return !(a == b); + } + + + string ToString() const + { + string str; + str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n", + GetHash().ToString().substr(0,10).c_str(), + nVersion, + vin.size(), + vout.size(), + nLockTime); + for (int i = 0; i < vin.size(); i++) + str += " " + vin[i].ToString() + "\n"; + for (int i = 0; i < vout.size(); i++) + str += " " + vout[i].ToString() + "\n"; + return str; + } + + void print() const + { + printf("%s", ToString().c_str()); + } + + + bool ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet); + bool ReadFromDisk(CTxDB& txdb, COutPoint prevout); + bool ReadFromDisk(COutPoint prevout); + bool DisconnectInputs(CTxDB& txdb); + bool ConnectInputs(CTxDB& txdb, map& mapTestPool, CDiskTxPos posThisTx, + CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0); + bool ClientConnectInputs(); + bool CheckTransaction() const; + bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); + bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL) + { + CTxDB txdb("r"); + return AcceptToMemoryPool(txdb, fCheckInputs, pfMissingInputs); + } +protected: + bool AddToMemoryPoolUnchecked(); +public: + bool RemoveFromMemoryPool(); +}; + + + + + +// +// A transaction with a merkle branch linking it to the block chain +// +class CMerkleTx : public CTransaction +{ +public: + uint256 hashBlock; + vector vMerkleBranch; + int nIndex; + + // memory only + mutable char fMerkleVerified; + + + CMerkleTx() + { + Init(); + } + + CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) + { + Init(); + } + + void Init() + { + hashBlock = 0; + nIndex = -1; + fMerkleVerified = false; + } + + + IMPLEMENT_SERIALIZE + ( + nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action); + nVersion = this->nVersion; + READWRITE(hashBlock); + READWRITE(vMerkleBranch); + READWRITE(nIndex); + ) + + + int SetMerkleBranch(const CBlock* pblock=NULL); + int GetDepthInMainChain(int& nHeightRet) const; + int GetDepthInMainChain() const { int nHeight; return GetDepthInMainChain(nHeight); } + bool IsInMainChain() const { return GetDepthInMainChain() > 0; } + int GetBlocksToMaturity() const; + bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true); + bool AcceptToMemoryPool() { CTxDB txdb("r"); return AcceptToMemoryPool(txdb); } +}; + + + + +// +// A transaction with a bunch of additional info that only the owner cares +// about. It includes any unrecorded transactions needed to link it back +// to the block chain. +// +class CWalletTx : public CMerkleTx +{ +public: + vector vtxPrev; + map mapValue; + vector > vOrderForm; + unsigned int fTimeReceivedIsTxTime; + unsigned int nTimeReceived; // time received by this node + char fFromMe; + string strFromAccount; + vector vfSpent; + + // memory only + mutable char fDebitCached; + mutable char fCreditCached; + mutable char fAvailableCreditCached; + mutable char fChangeCached; + mutable int64 nDebitCached; + mutable int64 nCreditCached; + mutable int64 nAvailableCreditCached; + mutable int64 nChangeCached; + + // memory only UI hints + mutable unsigned int nTimeDisplayed; + mutable int nLinesDisplayed; + mutable char fConfirmedDisplayed; + + + CWalletTx() + { + Init(); + } + + CWalletTx(const CMerkleTx& txIn) : CMerkleTx(txIn) + { + Init(); + } + + CWalletTx(const CTransaction& txIn) : CMerkleTx(txIn) + { + Init(); + } + + void Init() + { + vtxPrev.clear(); + mapValue.clear(); + vOrderForm.clear(); + fTimeReceivedIsTxTime = false; + nTimeReceived = 0; + fFromMe = false; + strFromAccount.clear(); + vfSpent.clear(); + fDebitCached = false; + fCreditCached = false; + fAvailableCreditCached = false; + fChangeCached = false; + nDebitCached = 0; + nCreditCached = 0; + nAvailableCreditCached = 0; + nChangeCached = 0; + nTimeDisplayed = 0; + nLinesDisplayed = 0; + fConfirmedDisplayed = false; + } + + IMPLEMENT_SERIALIZE + ( + CWalletTx* pthis = const_cast(this); + if (fRead) + pthis->Init(); + char fSpent = false; + + if (!fRead) + { + pthis->mapValue["fromaccount"] = pthis->strFromAccount; + + string str; + foreach(char f, vfSpent) + { + str += (f ? '1' : '0'); + if (f) + fSpent = true; + } + pthis->mapValue["spent"] = str; + } + + nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action); + READWRITE(vtxPrev); + READWRITE(mapValue); + READWRITE(vOrderForm); + READWRITE(fTimeReceivedIsTxTime); + READWRITE(nTimeReceived); + READWRITE(fFromMe); + READWRITE(fSpent); + + if (fRead) + { + pthis->strFromAccount = pthis->mapValue["fromaccount"]; + + if (mapValue.count("spent")) + foreach(char c, pthis->mapValue["spent"]) + pthis->vfSpent.push_back(c != '0'); + else + pthis->vfSpent.assign(vout.size(), fSpent); + } + + pthis->mapValue.erase("fromaccount"); + pthis->mapValue.erase("version"); + pthis->mapValue.erase("spent"); + ) + + // marks certain txout's as spent + // returns true if any update took place + bool UpdateSpent(const vector& vfNewSpent) + { + bool fReturn = false; + for (int i=0; i < vfNewSpent.size(); i++) + { + if (i == vfSpent.size()) + break; + + if (vfNewSpent[i] && !vfSpent[i]) + { + vfSpent[i] = true; + fReturn = true; + fAvailableCreditCached = false; + } + } + return fReturn; + } + + void MarkDirty() + { + fCreditCached = false; + fAvailableCreditCached = false; + fDebitCached = false; + fChangeCached = false; + } + + void MarkSpent(unsigned int nOut) + { + if (nOut >= vout.size()) + throw runtime_error("CWalletTx::MarkSpent() : nOut out of range"); + vfSpent.resize(vout.size()); + if (!vfSpent[nOut]) + { + vfSpent[nOut] = true; + fAvailableCreditCached = false; + } + } + + bool IsSpent(unsigned int nOut) const + { + if (nOut >= vout.size()) + throw runtime_error("CWalletTx::IsSpent() : nOut out of range"); + if (nOut >= vfSpent.size()) + return false; + return (!!vfSpent[nOut]); + } + + int64 GetDebit() const + { + if (vin.empty()) + return 0; + if (fDebitCached) + return nDebitCached; + nDebitCached = CTransaction::GetDebit(); + fDebitCached = true; + return nDebitCached; + } + + int64 GetCredit(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + // GetBalance can assume transactions in mapWallet won't change + if (fUseCache && fCreditCached) + return nCreditCached; + nCreditCached = CTransaction::GetCredit(); + fCreditCached = true; + return nCreditCached; + } + + int64 GetAvailableCredit(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableCreditCached) + return nAvailableCreditCached; + + int64 nCredit = 0; + for (int i = 0; i < vout.size(); i++) + { + if (!IsSpent(i)) + { + const CTxOut &txout = vout[i]; + nCredit += txout.GetCredit(); + if (!MoneyRange(nCredit)) + throw runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableCreditCached = nCredit; + fAvailableCreditCached = true; + return nCredit; + } + + + int64 GetChange() const + { + if (fChangeCached) + return nChangeCached; + nChangeCached = CTransaction::GetChange(); + fChangeCached = true; + return nChangeCached; + } + + void GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list >& listReceived, + list >& listSent, int64& nFee, string& strSentAccount) const; + + void GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, + int64& nSent, int64& nFee) const; + + bool IsFromMe() const + { + return (GetDebit() > 0); + } + + bool IsConfirmed() const + { + // Quick answer in most cases + if (!IsFinal()) + return false; + if (GetDepthInMainChain() >= 1) + return true; + if (!IsFromMe()) // using wtx's cached debit + return false; + + // If no confirmations but it's from us, we can still + // consider it confirmed if all dependencies are confirmed + map mapPrev; + vector vWorkQueue; + vWorkQueue.reserve(vtxPrev.size()+1); + vWorkQueue.push_back(this); + for (int i = 0; i < vWorkQueue.size(); i++) + { + const CMerkleTx* ptx = vWorkQueue[i]; + + if (!ptx->IsFinal()) + return false; + if (ptx->GetDepthInMainChain() >= 1) + continue; + if (!ptx->IsFromMe()) + return false; + + if (mapPrev.empty()) + foreach(const CMerkleTx& tx, vtxPrev) + mapPrev[tx.GetHash()] = &tx; + + foreach(const CTxIn& txin, ptx->vin) + { + if (!mapPrev.count(txin.prevout.hash)) + return false; + vWorkQueue.push_back(mapPrev[txin.prevout.hash]); + } + } + return true; + } + + bool WriteToDisk() + { + return CWalletDB().WriteTx(GetHash(), *this); + } + + + int64 GetTxTime() const; + int GetRequestCount() const; + + void AddSupportingTransactions(CTxDB& txdb); + + bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true); + bool AcceptWalletTransaction() { CTxDB txdb("r"); return AcceptWalletTransaction(txdb); } + + void RelayWalletTransaction(CTxDB& txdb); + void RelayWalletTransaction() { CTxDB txdb("r"); RelayWalletTransaction(txdb); } +}; + + + + +// +// A txdb record that contains the disk location of a transaction and the +// locations of transactions that spend its outputs. vSpent is really only +// used as a flag, but having the location is very helpful for debugging. +// +class CTxIndex +{ +public: + CDiskTxPos pos; + vector vSpent; + + CTxIndex() + { + SetNull(); + } + + CTxIndex(const CDiskTxPos& posIn, unsigned int nOutputs) + { + pos = posIn; + vSpent.resize(nOutputs); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(pos); + READWRITE(vSpent); + ) + + void SetNull() + { + pos.SetNull(); + vSpent.clear(); + } + + bool IsNull() + { + return pos.IsNull(); + } + + friend bool operator==(const CTxIndex& a, const CTxIndex& b) + { + return (a.pos == b.pos && + a.vSpent == b.vSpent); + } + + friend bool operator!=(const CTxIndex& a, const CTxIndex& b) + { + return !(a == b); + } + int GetDepthInMainChain() const; +}; + + + + + +// +// Nodes collect new transactions into a block, hash them into a hash tree, +// and scan through nonce values to make the block's hash satisfy proof-of-work +// requirements. When they solve the proof-of-work, they broadcast the block +// to everyone and the block is added to the block chain. The first transaction +// in the block is a special one that creates a new coin owned by the creator +// of the block. +// +// Blocks are appended to blk0001.dat files on disk. Their location on disk +// is indexed by CBlockIndex objects in memory. +// +class CBlock +{ +public: + // header + int nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + + // network and disk + vector vtx; + + // memory only + mutable vector vMerkleTree; + + + CBlock() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(hashPrevBlock); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + + // ConnectBlock depends on vtx being last so it can calculate offset + if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY))) + READWRITE(vtx); + else if (fRead) + const_cast(this)->vtx.clear(); + ) + + void SetNull() + { + nVersion = 1; + hashPrevBlock = 0; + hashMerkleRoot = 0; + nTime = 0; + nBits = 0; + nNonce = 0; + vtx.clear(); + vMerkleTree.clear(); + } + + bool IsNull() const + { + return (nBits == 0); + } + + uint256 GetHash() const + { + return Hash(BEGIN(nVersion), END(nNonce)); + } + + int64 GetBlockTime() const + { + return (int64)nTime; + } + + int GetSigOpCount() const + { + int n = 0; + foreach(const CTransaction& tx, vtx) + n += tx.GetSigOpCount(); + return n; + } + + + uint256 BuildMerkleTree() const + { + vMerkleTree.clear(); + foreach(const CTransaction& tx, vtx) + vMerkleTree.push_back(tx.GetHash()); + int j = 0; + for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + for (int i = 0; i < nSize; i += 2) + { + int i2 = min(i+1, nSize-1); + vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), + BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); + } + j += nSize; + } + return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); + } + + vector GetMerkleBranch(int nIndex) const + { + if (vMerkleTree.empty()) + BuildMerkleTree(); + vector vMerkleBranch; + int j = 0; + for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + int i = min(nIndex^1, nSize-1); + vMerkleBranch.push_back(vMerkleTree[j+i]); + nIndex >>= 1; + j += nSize; + } + return vMerkleBranch; + } + + static uint256 CheckMerkleBranch(uint256 hash, const vector& vMerkleBranch, int nIndex) + { + if (nIndex == -1) + return 0; + foreach(const uint256& otherside, vMerkleBranch) + { + if (nIndex & 1) + hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); + else + hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); + nIndex >>= 1; + } + return hash; + } + + + bool WriteToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet) + { + // Open history file to append + CAutoFile fileout = AppendBlockFile(nFileRet); + if (!fileout) + return error("CBlock::WriteToDisk() : AppendBlockFile failed"); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(*this); + fileout << FLATDATA(pchMessageStart) << nSize; + + // Write block + nBlockPosRet = ftell(fileout); + if (nBlockPosRet == -1) + return error("CBlock::WriteToDisk() : ftell failed"); + fileout << *this; + + // Flush stdio buffers and commit to disk before returning + fflush(fileout); + if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0) + { +#ifdef __WXMSW__ + _commit(_fileno(fileout)); +#else + fsync(fileno(fileout)); +#endif + } + + return true; + } + + bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true) + { + SetNull(); + + // Open history file to read + CAutoFile filein = OpenBlockFile(nFile, nBlockPos, "rb"); + if (!filein) + return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); + if (!fReadTransactions) + filein.nType |= SER_BLOCKHEADERONLY; + + // Read block + filein >> *this; + + // Check the header + if (!CheckProofOfWork(GetHash(), nBits)) + return error("CBlock::ReadFromDisk() : errors in block header"); + + return true; + } + + + + void print() const + { + printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d)\n", + GetHash().ToString().substr(0,20).c_str(), + nVersion, + hashPrevBlock.ToString().substr(0,20).c_str(), + hashMerkleRoot.ToString().substr(0,10).c_str(), + nTime, nBits, nNonce, + vtx.size()); + for (int i = 0; i < vtx.size(); i++) + { + printf(" "); + vtx[i].print(); + } + printf(" vMerkleTree: "); + for (int i = 0; i < vMerkleTree.size(); i++) + printf("%s ", vMerkleTree[i].ToString().substr(0,10).c_str()); + printf("\n"); + } + + + bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex); + bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex); + bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true); + bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew); + bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); + bool CheckBlock() const; + bool AcceptBlock(); +}; + + + + + + +// +// The block chain is a tree shaped structure starting with the +// genesis block at the root, with each block potentially having multiple +// candidates to be the next block. pprev and pnext link a path through the +// main/longest chain. A blockindex may have multiple pprev pointing back +// to it, but pnext will only point forward to the longest branch, or will +// be null if the block is not part of the longest chain. +// +class CBlockIndex +{ +public: + const uint256* phashBlock; + CBlockIndex* pprev; + CBlockIndex* pnext; + unsigned int nFile; + unsigned int nBlockPos; + int nHeight; + CBigNum bnChainWork; + + // block header + int nVersion; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + + + CBlockIndex() + { + phashBlock = NULL; + pprev = NULL; + pnext = NULL; + nFile = 0; + nBlockPos = 0; + nHeight = 0; + bnChainWork = 0; + + nVersion = 0; + hashMerkleRoot = 0; + nTime = 0; + nBits = 0; + nNonce = 0; + } + + CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block) + { + phashBlock = NULL; + pprev = NULL; + pnext = NULL; + nFile = nFileIn; + nBlockPos = nBlockPosIn; + nHeight = 0; + bnChainWork = 0; + + nVersion = block.nVersion; + hashMerkleRoot = block.hashMerkleRoot; + nTime = block.nTime; + nBits = block.nBits; + nNonce = block.nNonce; + } + + CBlock GetBlockHeader() const + { + CBlock block; + block.nVersion = nVersion; + if (pprev) + block.hashPrevBlock = pprev->GetBlockHash(); + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block; + } + + uint256 GetBlockHash() const + { + return *phashBlock; + } + + int64 GetBlockTime() const + { + return (int64)nTime; + } + + CBigNum GetBlockWork() const + { + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + if (bnTarget <= 0) + return 0; + return (CBigNum(1)<<256) / (bnTarget+1); + } + + bool IsInMainChain() const + { + return (pnext || this == pindexBest); + } + + bool CheckIndex() const + { + return CheckProofOfWork(GetBlockHash(), nBits); + } + + bool EraseBlockFromDisk() + { + // Open history file + CAutoFile fileout = OpenBlockFile(nFile, nBlockPos, "rb+"); + if (!fileout) + return false; + + // Overwrite with empty null block + CBlock block; + block.SetNull(); + fileout << block; + + return true; + } + + enum { nMedianTimeSpan=11 }; + + int64 GetMedianTimePast() const + { + int64 pmedian[nMedianTimeSpan]; + int64* pbegin = &pmedian[nMedianTimeSpan]; + int64* pend = &pmedian[nMedianTimeSpan]; + + const CBlockIndex* pindex = this; + for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev) + *(--pbegin) = pindex->GetBlockTime(); + + sort(pbegin, pend); + return pbegin[(pend - pbegin)/2]; + } + + int64 GetMedianTime() const + { + const CBlockIndex* pindex = this; + for (int i = 0; i < nMedianTimeSpan/2; i++) + { + if (!pindex->pnext) + return GetBlockTime(); + pindex = pindex->pnext; + } + return pindex->GetMedianTimePast(); + } + + + + string ToString() const + { + return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, merkle=%s, hashBlock=%s)", + pprev, pnext, nFile, nBlockPos, nHeight, + hashMerkleRoot.ToString().substr(0,10).c_str(), + GetBlockHash().ToString().substr(0,20).c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + +// +// Used to marshal pointers into hashes for db storage. +// +class CDiskBlockIndex : public CBlockIndex +{ +public: + uint256 hashPrev; + uint256 hashNext; + + CDiskBlockIndex() + { + hashPrev = 0; + hashNext = 0; + } + + explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) + { + hashPrev = (pprev ? pprev->GetBlockHash() : 0); + hashNext = (pnext ? pnext->GetBlockHash() : 0); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + + READWRITE(hashNext); + READWRITE(nFile); + READWRITE(nBlockPos); + READWRITE(nHeight); + + // block header + READWRITE(this->nVersion); + READWRITE(hashPrev); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + ) + + uint256 GetBlockHash() const + { + CBlock block; + block.nVersion = nVersion; + block.hashPrevBlock = hashPrev; + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block.GetHash(); + } + + + string ToString() const + { + string str = "CDiskBlockIndex("; + str += CBlockIndex::ToString(); + str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)", + GetBlockHash().ToString().c_str(), + hashPrev.ToString().substr(0,20).c_str(), + hashNext.ToString().substr(0,20).c_str()); + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + + + + + +// +// Describes a place in the block chain to another node such that if the +// other node doesn't have the same branch, it can find a recent common trunk. +// The further back it is, the further before the fork it may be. +// +class CBlockLocator +{ +protected: + vector vHave; +public: + + CBlockLocator() + { + } + + explicit CBlockLocator(const CBlockIndex* pindex) + { + Set(pindex); + } + + explicit CBlockLocator(uint256 hashBlock) + { + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end()) + Set((*mi).second); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vHave); + ) + + void SetNull() + { + vHave.clear(); + } + + bool IsNull() + { + return vHave.empty(); + } + + void Set(const CBlockIndex* pindex) + { + vHave.clear(); + int nStep = 1; + while (pindex) + { + vHave.push_back(pindex->GetBlockHash()); + + // Exponentially larger steps back + for (int i = 0; pindex && i < nStep; i++) + pindex = pindex->pprev; + if (vHave.size() > 10) + nStep *= 2; + } + vHave.push_back(hashGenesisBlock); + } + + int GetDistanceBack() + { + // Retrace how far back it was in the sender's branch + int nDistance = 0; + int nStep = 1; + foreach(const uint256& hash, vHave) + { + map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return nDistance; + } + nDistance += nStep; + if (nDistance > 10) + nStep *= 2; + } + return nDistance; + } + + CBlockIndex* GetBlockIndex() + { + // Find the first block the caller has in the main chain + foreach(const uint256& hash, vHave) + { + map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return pindex; + } + } + return pindexGenesisBlock; + } + + uint256 GetBlockHash() + { + // Find the first block the caller has in the main chain + foreach(const uint256& hash, vHave) + { + map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return hash; + } + } + return hashGenesisBlock; + } + + int GetHeight() + { + CBlockIndex* pindex = GetBlockIndex(); + if (!pindex) + return 0; + return pindex->nHeight; + } +}; + + + + + + +// +// Private key that includes an expiration date in case it never gets used. +// +class CWalletKey +{ +public: + CPrivKey vchPrivKey; + int64 nTimeCreated; + int64 nTimeExpires; + string strComment; + //// todo: add something to note what created it (user, getnewaddress, change) + //// maybe should have a map property map + + CWalletKey(int64 nExpires=0) + { + nTimeCreated = (nExpires ? GetTime() : 0); + nTimeExpires = nExpires; + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPrivKey); + READWRITE(nTimeCreated); + READWRITE(nTimeExpires); + READWRITE(strComment); + ) +}; + + + + + + +// +// Account information. +// Stored in wallet with key "acc"+string account name +// +class CAccount +{ +public: + vector vchPubKey; + + CAccount() + { + SetNull(); + } + + void SetNull() + { + vchPubKey.clear(); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPubKey); + ) +}; + + + +// +// Internal transfers. +// Database key is acentry +// +class CAccountingEntry +{ +public: + string strAccount; + int64 nCreditDebit; + int64 nTime; + string strOtherAccount; + string strComment; + + CAccountingEntry() + { + SetNull(); + } + + void SetNull() + { + nCreditDebit = 0; + nTime = 0; + strAccount.clear(); + strOtherAccount.clear(); + strComment.clear(); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + // Note: strAccount is serialized as part of the key, not here. + READWRITE(nCreditDebit); + READWRITE(nTime); + READWRITE(strOtherAccount); + READWRITE(strComment); + ) +}; + + + + + + + + + +// +// Alerts are for notifying old versions if they become too obsolete and +// need to upgrade. The message is displayed in the status bar. +// Alert messages are broadcast as a vector of signed data. Unserializing may +// not read the entire buffer if the alert is for a newer version, but older +// versions can still relay the original data. +// +class CUnsignedAlert +{ +public: + int nVersion; + int64 nRelayUntil; // when newer nodes stop relaying to newer nodes + int64 nExpiration; + int nID; + int nCancel; + set setCancel; + int nMinVer; // lowest version inclusive + int nMaxVer; // highest version inclusive + set setSubVer; // empty matches all + int nPriority; + + // Actions + string strComment; + string strStatusBar; + string strReserved; + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(nRelayUntil); + READWRITE(nExpiration); + READWRITE(nID); + READWRITE(nCancel); + READWRITE(setCancel); + READWRITE(nMinVer); + READWRITE(nMaxVer); + READWRITE(setSubVer); + READWRITE(nPriority); + + READWRITE(strComment); + READWRITE(strStatusBar); + READWRITE(strReserved); + ) + + void SetNull() + { + nVersion = 1; + nRelayUntil = 0; + nExpiration = 0; + nID = 0; + nCancel = 0; + setCancel.clear(); + nMinVer = 0; + nMaxVer = 0; + setSubVer.clear(); + nPriority = 0; + + strComment.clear(); + strStatusBar.clear(); + strReserved.clear(); + } + + string ToString() const + { + string strSetCancel; + foreach(int n, setCancel) + strSetCancel += strprintf("%d ", n); + string strSetSubVer; + foreach(string str, setSubVer) + strSetSubVer += "\"" + str + "\" "; + return strprintf( + "CAlert(\n" + " nVersion = %d\n" + " nRelayUntil = %"PRI64d"\n" + " nExpiration = %"PRI64d"\n" + " nID = %d\n" + " nCancel = %d\n" + " setCancel = %s\n" + " nMinVer = %d\n" + " nMaxVer = %d\n" + " setSubVer = %s\n" + " nPriority = %d\n" + " strComment = \"%s\"\n" + " strStatusBar = \"%s\"\n" + ")\n", + nVersion, + nRelayUntil, + nExpiration, + nID, + nCancel, + strSetCancel.c_str(), + nMinVer, + nMaxVer, + strSetSubVer.c_str(), + nPriority, + strComment.c_str(), + strStatusBar.c_str()); + } + + void print() const + { + printf("%s", ToString().c_str()); + } +}; + +class CAlert : public CUnsignedAlert +{ +public: + vector vchMsg; + vector vchSig; + + CAlert() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(vchMsg); + READWRITE(vchSig); + ) + + void SetNull() + { + CUnsignedAlert::SetNull(); + vchMsg.clear(); + vchSig.clear(); + } + + bool IsNull() const + { + return (nExpiration == 0); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsInEffect() const + { + return (GetAdjustedTime() < nExpiration); + } + + bool Cancels(const CAlert& alert) const + { + if (!IsInEffect()) + return false; // this was a no-op before 31403 + return (alert.nID <= nCancel || setCancel.count(alert.nID)); + } + + bool AppliesTo(int nVersion, string strSubVerIn) const + { + return (IsInEffect() && + nMinVer <= nVersion && nVersion <= nMaxVer && + (setSubVer.empty() || setSubVer.count(strSubVerIn))); + } + + bool AppliesToMe() const + { + return AppliesTo(VERSION, ::pszSubVer); + } + + bool RelayTo(CNode* pnode) const + { + if (!IsInEffect()) + return false; + // returns true if wasn't already contained in the set + if (pnode->setKnown.insert(GetHash()).second) + { + if (AppliesTo(pnode->nVersion, pnode->strSubVer) || + AppliesToMe() || + GetAdjustedTime() < nRelayUntil) + { + pnode->PushMessage("alert", *this); + return true; + } + } + return false; + } + + bool CheckSignature() + { + CKey key; + if (!key.SetPubKey(ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284"))) + return error("CAlert::CheckSignature() : SetPubKey failed"); + if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) + return error("CAlert::CheckSignature() : verify signature failed"); + + // Now unserialize the data + CDataStream sMsg(vchMsg); + sMsg >> *(CUnsignedAlert*)this; + return true; + } + + bool ProcessAlert(); +}; + + + + + + + + + + +extern map mapTransactions; +extern map mapWallet; +extern vector vWalletUpdated; +extern CCriticalSection cs_mapWallet; +extern map, CPrivKey> mapKeys; +extern map > mapPubKeys; +extern CCriticalSection cs_mapKeys; +extern CKey keyUser; + +#endif diff --git a/core/include/net.h b/core/include/net.h new file mode 100644 index 0000000000..746dd02fe8 --- /dev/null +++ b/core/include/net.h @@ -0,0 +1,1046 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_NET_H +#define BITCOIN_NET_H + +#include +#include +#include +#include + +class CMessageHeader; +class CAddress; +class CInv; +class CRequestTracker; +class CNode; +class CBlockIndex; +extern int nBestHeight; + + + +inline unsigned short GetDefaultPort() { return fTestNet ? 18333 : 8333; } +static const unsigned int PUBLISH_HOPS = 5; +enum +{ + NODE_NETWORK = (1 << 0), +}; + + + + +bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet); +bool Lookup(const char *pszName, std::vector& vaddr, int nServices, int nMaxSolutions, bool fAllowLookup = false, int portDefault = 0, bool fAllowPort = false); +bool Lookup(const char *pszName, CAddress& addr, int nServices, bool fAllowLookup = false, int portDefault = 0, bool fAllowPort = false); +bool GetMyExternalIP(unsigned int& ipRet); +bool AddAddress(CAddress addr, int64 nTimePenalty=0); +void AddressCurrentlyConnected(const CAddress& addr); +CNode* FindNode(unsigned int ip); +CNode* ConnectNode(CAddress addrConnect, int64 nTimeout=0); +void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); +bool AnySubscribed(unsigned int nChannel); +void MapPort(bool fMapPort); +void DNSAddressSeed(); +bool BindListenPort(std::string& strError=REF(std::string())); +void StartNode(void* parg); +bool StopNode(); + + + + + + + + +// +// Message header +// (4) message start +// (12) command +// (4) size +// (4) checksum + +extern char pchMessageStart[4]; + +class CMessageHeader +{ +public: + enum { COMMAND_SIZE=12 }; + char pchMessageStart[sizeof(::pchMessageStart)]; + char pchCommand[COMMAND_SIZE]; + unsigned int nMessageSize; + unsigned int nChecksum; + + CMessageHeader() + { + memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + memset(pchCommand, 0, sizeof(pchCommand)); + pchCommand[1] = 1; + nMessageSize = -1; + nChecksum = 0; + } + + CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn) + { + memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + strncpy(pchCommand, pszCommand, COMMAND_SIZE); + nMessageSize = nMessageSizeIn; + nChecksum = 0; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(FLATDATA(pchMessageStart)); + READWRITE(FLATDATA(pchCommand)); + READWRITE(nMessageSize); + if (nVersion >= 209) + READWRITE(nChecksum); + ) + + std::string GetCommand() + { + if (pchCommand[COMMAND_SIZE-1] == 0) + return std::string(pchCommand, pchCommand + strlen(pchCommand)); + else + return std::string(pchCommand, pchCommand + COMMAND_SIZE); + } + + bool IsValid() + { + // Check start string + if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0) + return false; + + // Check the command string for errors + for (char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++) + { + if (*p1 == 0) + { + // Must be all zeros after the first zero + for (; p1 < pchCommand + COMMAND_SIZE; p1++) + if (*p1 != 0) + return false; + } + else if (*p1 < ' ' || *p1 > 0x7E) + return false; + } + + // Message size + if (nMessageSize > MAX_SIZE) + { + printf("CMessageHeader::IsValid() : (%s, %u bytes) nMessageSize > MAX_SIZE\n", GetCommand().c_str(), nMessageSize); + return false; + } + + return true; + } +}; + + + + + + +static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + +class CAddress +{ +public: + uint64 nServices; + unsigned char pchReserved[12]; + unsigned int ip; + unsigned short port; + + // disk and network only + unsigned int nTime; + + // memory only + unsigned int nLastTry; + + CAddress() + { + Init(); + } + + CAddress(unsigned int ipIn, unsigned short portIn=0, uint64 nServicesIn=NODE_NETWORK) + { + Init(); + ip = ipIn; + port = htons(portIn == 0 ? GetDefaultPort() : portIn); + nServices = nServicesIn; + } + + explicit CAddress(const struct sockaddr_in& sockaddr, uint64 nServicesIn=NODE_NETWORK) + { + Init(); + ip = sockaddr.sin_addr.s_addr; + port = sockaddr.sin_port; + nServices = nServicesIn; + } + + explicit CAddress(const char* pszIn, int portIn, bool fNameLookup = false, uint64 nServicesIn=NODE_NETWORK) + { + Init(); + Lookup(pszIn, *this, nServicesIn, fNameLookup, portIn); + } + + explicit CAddress(const char* pszIn, bool fNameLookup = false, uint64 nServicesIn=NODE_NETWORK) + { + Init(); + Lookup(pszIn, *this, nServicesIn, fNameLookup, 0, true); + } + + explicit CAddress(std::string strIn, int portIn, bool fNameLookup = false, uint64 nServicesIn=NODE_NETWORK) + { + Init(); + Lookup(strIn.c_str(), *this, nServicesIn, fNameLookup, portIn); + } + + explicit CAddress(std::string strIn, bool fNameLookup = false, uint64 nServicesIn=NODE_NETWORK) + { + Init(); + Lookup(strIn.c_str(), *this, nServicesIn, fNameLookup, 0, true); + } + + void Init() + { + nServices = NODE_NETWORK; + memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); + ip = INADDR_NONE; + port = htons(GetDefaultPort()); + nTime = 100000000; + nLastTry = 0; + } + + IMPLEMENT_SERIALIZE + ( + if (fRead) + const_cast(this)->Init(); + if (nType & SER_DISK) + READWRITE(nVersion); + if ((nType & SER_DISK) || (nVersion >= 31402 && !(nType & SER_GETHASH))) + READWRITE(nTime); + READWRITE(nServices); + READWRITE(FLATDATA(pchReserved)); // for IPv6 + READWRITE(ip); + READWRITE(port); + ) + + friend inline bool operator==(const CAddress& a, const CAddress& b) + { + return (memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)) == 0 && + a.ip == b.ip && + a.port == b.port); + } + + friend inline bool operator!=(const CAddress& a, const CAddress& b) + { + return (!(a == b)); + } + + friend inline bool operator<(const CAddress& a, const CAddress& b) + { + int ret = memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)); + if (ret < 0) + return true; + else if (ret == 0) + { + if (ntohl(a.ip) < ntohl(b.ip)) + return true; + else if (a.ip == b.ip) + return ntohs(a.port) < ntohs(b.port); + } + return false; + } + + std::vector GetKey() const + { + CDataStream ss; + ss.reserve(18); + ss << FLATDATA(pchReserved) << ip << port; + + #if defined(_MSC_VER) && _MSC_VER < 1300 + return std::vector((unsigned char*)&ss.begin()[0], (unsigned char*)&ss.end()[0]); + #else + return std::vector(ss.begin(), ss.end()); + #endif + } + + struct sockaddr_in GetSockAddr() const + { + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = ip; + sockaddr.sin_port = port; + return sockaddr; + } + + bool IsIPv4() const + { + return (memcmp(pchReserved, pchIPv4, sizeof(pchIPv4)) == 0); + } + + bool IsRoutable() const + { + return IsValid() && + !(GetByte(3) == 10 || + (GetByte(3) == 192 && GetByte(2) == 168) || + GetByte(3) == 127 || + GetByte(3) == 0); + } + + bool IsValid() const + { + // Clean up 3-byte shifted addresses caused by garbage in size field + // of addr messages from versions before 0.2.9 checksum. + // Two consecutive addr messages look like this: + // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26... + // so if the first length field is garbled, it reads the second batch + // of addr misaligned by 3 bytes. + if (memcmp(pchReserved, pchIPv4+3, sizeof(pchIPv4)-3) == 0) + return false; + + return (ip != 0 && ip != INADDR_NONE && port != htons(USHRT_MAX)); + } + + unsigned char GetByte(int n) const + { + return ((unsigned char*)&ip)[3-n]; + } + + std::string ToStringIPPort() const + { + return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port)); + } + + std::string ToStringIP() const + { + return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0)); + } + + std::string ToStringPort() const + { + return strprintf("%u", ntohs(port)); + } + + std::string ToString() const + { + return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port)); + } + + void print() const + { + printf("CAddress(%s)\n", ToString().c_str()); + } +}; + + + + + + + +enum +{ + MSG_TX = 1, + MSG_BLOCK, +}; + +static const char* ppszTypeName[] = +{ + "ERROR", + "tx", + "block", +}; + +class CInv +{ +public: + int type; + uint256 hash; + + CInv() + { + type = 0; + hash = 0; + } + + CInv(int typeIn, const uint256& hashIn) + { + type = typeIn; + hash = hashIn; + } + + CInv(const std::string& strType, const uint256& hashIn) + { + int i; + for (i = 1; i < ARRAYLEN(ppszTypeName); i++) + { + if (strType == ppszTypeName[i]) + { + type = i; + break; + } + } + if (i == ARRAYLEN(ppszTypeName)) + throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType.c_str())); + hash = hashIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(type); + READWRITE(hash); + ) + + friend inline bool operator<(const CInv& a, const CInv& b) + { + return (a.type < b.type || (a.type == b.type && a.hash < b.hash)); + } + + bool IsKnownType() const + { + return (type >= 1 && type < ARRAYLEN(ppszTypeName)); + } + + const char* GetCommand() const + { + if (!IsKnownType()) + throw std::out_of_range(strprintf("CInv::GetCommand() : type=%d unknown type", type)); + return ppszTypeName[type]; + } + + std::string ToString() const + { + return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,20).c_str()); + } + + void print() const + { + printf("CInv(%s)\n", ToString().c_str()); + } +}; + + + + + +class CRequestTracker +{ +public: + void (*fn)(void*, CDataStream&); + void* param1; + + explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL) + { + fn = fnIn; + param1 = param1In; + } + + bool IsNull() + { + return fn == NULL; + } +}; + + + + + +extern bool fClient; +extern bool fAllowDNS; +extern uint64 nLocalServices; +extern CAddress addrLocalHost; +extern CNode* pnodeLocalHost; +extern uint64 nLocalHostNonce; +extern boost::array vnThreadsRunning; +extern SOCKET hListenSocket; + +extern std::vector vNodes; +extern CCriticalSection cs_vNodes; +extern std::map, CAddress> mapAddresses; +extern CCriticalSection cs_mapAddresses; +extern std::map mapRelay; +extern std::deque > vRelayExpiration; +extern CCriticalSection cs_mapRelay; +extern std::map mapAlreadyAskedFor; + +// Settings +extern int fUseProxy; +extern CAddress addrProxy; + + + + + + +class CNode +{ +public: + // socket + uint64 nServices; + SOCKET hSocket; + CDataStream vSend; + CDataStream vRecv; + CCriticalSection cs_vSend; + CCriticalSection cs_vRecv; + int64 nLastSend; + int64 nLastRecv; + int64 nLastSendEmpty; + int64 nTimeConnected; + unsigned int nHeaderStart; + unsigned int nMessageStart; + CAddress addr; + int nVersion; + std::string strSubVer; + bool fClient; + bool fInbound; + bool fNetworkNode; + bool fSuccessfullyConnected; + bool fDisconnect; +protected: + int nRefCount; +public: + int64 nReleaseTime; + std::map mapRequests; + CCriticalSection cs_mapRequests; + uint256 hashContinue; + CBlockIndex* pindexLastGetBlocksBegin; + uint256 hashLastGetBlocksEnd; + int nStartingHeight; + + // flood relay + std::vector vAddrToSend; + std::set setAddrKnown; + bool fGetAddr; + std::set setKnown; + + // inventory based relay + std::set setInventoryKnown; + std::vector vInventoryToSend; + CCriticalSection cs_inventory; + std::multimap mapAskFor; + + // publish and subscription + std::vector vfSubscribe; + + + CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false) + { + nServices = 0; + hSocket = hSocketIn; + vSend.SetType(SER_NETWORK); + vSend.SetVersion(0); + vRecv.SetType(SER_NETWORK); + vRecv.SetVersion(0); + // Version 0.2 obsoletes 20 Feb 2012 + if (GetTime() > 1329696000) + { + vSend.SetVersion(209); + vRecv.SetVersion(209); + } + nLastSend = 0; + nLastRecv = 0; + nLastSendEmpty = GetTime(); + nTimeConnected = GetTime(); + nHeaderStart = -1; + nMessageStart = -1; + addr = addrIn; + nVersion = 0; + strSubVer = ""; + fClient = false; // set by version message + fInbound = fInboundIn; + fNetworkNode = false; + fSuccessfullyConnected = false; + fDisconnect = false; + nRefCount = 0; + nReleaseTime = 0; + hashContinue = 0; + pindexLastGetBlocksBegin = 0; + hashLastGetBlocksEnd = 0; + nStartingHeight = -1; + fGetAddr = false; + vfSubscribe.assign(256, false); + + // Be shy and don't send version until we hear + if (!fInbound) + PushVersion(); + } + + ~CNode() + { + if (hSocket != INVALID_SOCKET) + { + closesocket(hSocket); + hSocket = INVALID_SOCKET; + } + } + +private: + CNode(const CNode&); + void operator=(const CNode&); +public: + + + int GetRefCount() + { + return std::max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0); + } + + CNode* AddRef(int64 nTimeout=0) + { + if (nTimeout != 0) + nReleaseTime = std::max(nReleaseTime, GetTime() + nTimeout); + else + nRefCount++; + return this; + } + + void Release() + { + nRefCount--; + } + + + + void AddAddressKnown(const CAddress& addr) + { + setAddrKnown.insert(addr); + } + + void PushAddress(const CAddress& addr) + { + // Known checking here is only to save space from duplicates. + // SendMessages will filter it again for knowns that were added + // after addresses were pushed. + if (addr.IsValid() && !setAddrKnown.count(addr)) + vAddrToSend.push_back(addr); + } + + + void AddInventoryKnown(const CInv& inv) + { + CRITICAL_BLOCK(cs_inventory) + setInventoryKnown.insert(inv); + } + + void PushInventory(const CInv& inv) + { + CRITICAL_BLOCK(cs_inventory) + if (!setInventoryKnown.count(inv)) + vInventoryToSend.push_back(inv); + } + + void AskFor(const CInv& inv) + { + // We're using mapAskFor as a priority queue, + // the key is the earliest time the request can be sent + int64& nRequestTime = mapAlreadyAskedFor[inv]; + printf("askfor %s %"PRI64d"\n", inv.ToString().c_str(), nRequestTime); + + // Make sure not to reuse time indexes to keep things in the same order + int64 nNow = (GetTime() - 1) * 1000000; + static int64 nLastTime; + nLastTime = nNow = std::max(nNow, ++nLastTime); + + // Each retry is 2 minutes after the last + nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow); + mapAskFor.insert(std::make_pair(nRequestTime, inv)); + } + + + + void BeginMessage(const char* pszCommand) + { + cs_vSend.Enter(); + if (nHeaderStart != -1) + AbortMessage(); + nHeaderStart = vSend.size(); + vSend << CMessageHeader(pszCommand, 0); + nMessageStart = vSend.size(); + if (fDebug) + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); + printf("sending: %s ", pszCommand); + } + + void AbortMessage() + { + if (nHeaderStart == -1) + return; + vSend.resize(nHeaderStart); + nHeaderStart = -1; + nMessageStart = -1; + cs_vSend.Leave(); + printf("(aborted)\n"); + } + + void EndMessage() + { + if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) + { + printf("dropmessages DROPPING SEND MESSAGE\n"); + AbortMessage(); + return; + } + + if (nHeaderStart == -1) + return; + + // Set the size + unsigned int nSize = vSend.size() - nMessageStart; + memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize)); + + // Set the checksum + if (vSend.GetVersion() >= 209) + { + uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end()); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + assert(nMessageStart - nHeaderStart >= offsetof(CMessageHeader, nChecksum) + sizeof(nChecksum)); + memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nChecksum), &nChecksum, sizeof(nChecksum)); + } + + printf("(%d bytes) ", nSize); + printf("\n"); + + nHeaderStart = -1; + nMessageStart = -1; + cs_vSend.Leave(); + } + + void EndMessageAbortIfEmpty() + { + if (nHeaderStart == -1) + return; + int nSize = vSend.size() - nMessageStart; + if (nSize > 0) + EndMessage(); + else + AbortMessage(); + } + + + + void PushVersion() + { + /// when NTP implemented, change to just nTime = GetAdjustedTime() + int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); + CAddress addrYou = (fUseProxy ? CAddress("0.0.0.0") : addr); + CAddress addrMe = (fUseProxy ? CAddress("0.0.0.0") : addrLocalHost); + RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); + PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe, + nLocalHostNonce, std::string(pszSubVer), nBestHeight); + } + + + + + void PushMessage(const char* pszCommand) + { + try + { + BeginMessage(pszCommand); + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1) + { + try + { + BeginMessage(pszCommand); + vSend << a1; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3 << a4; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3 << a4 << a5; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3 << a4 << a5 << a6; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8, const T9& a9) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + + void PushRequest(const char* pszCommand, + void (*fn)(void*, CDataStream&), void* param1) + { + uint256 hashReply; + RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); + + CRITICAL_BLOCK(cs_mapRequests) + mapRequests[hashReply] = CRequestTracker(fn, param1); + + PushMessage(pszCommand, hashReply); + } + + template + void PushRequest(const char* pszCommand, const T1& a1, + void (*fn)(void*, CDataStream&), void* param1) + { + uint256 hashReply; + RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); + + CRITICAL_BLOCK(cs_mapRequests) + mapRequests[hashReply] = CRequestTracker(fn, param1); + + PushMessage(pszCommand, hashReply, a1); + } + + template + void PushRequest(const char* pszCommand, const T1& a1, const T2& a2, + void (*fn)(void*, CDataStream&), void* param1) + { + uint256 hashReply; + RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); + + CRITICAL_BLOCK(cs_mapRequests) + mapRequests[hashReply] = CRequestTracker(fn, param1); + + PushMessage(pszCommand, hashReply, a1, a2); + } + + + + void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd); + bool IsSubscribed(unsigned int nChannel); + void Subscribe(unsigned int nChannel, unsigned int nHops=0); + void CancelSubscribe(unsigned int nChannel); + void CloseSocketDisconnect(); + void Cleanup(); +}; + + + + + + + + + + +inline void RelayInventory(const CInv& inv) +{ + // Put on lists to offer to the other nodes + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + pnode->PushInventory(inv); +} + +template +void RelayMessage(const CInv& inv, const T& a) +{ + CDataStream ss(SER_NETWORK); + ss.reserve(10000); + ss << a; + RelayMessage(inv, ss); +} + +template<> +inline void RelayMessage<>(const CInv& inv, const CDataStream& ss) +{ + CRITICAL_BLOCK(cs_mapRelay) + { + // Expire old relay messages + while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) + { + mapRelay.erase(vRelayExpiration.front().second); + vRelayExpiration.pop_front(); + } + + // Save original serialized message so newer versions are preserved + mapRelay[inv] = ss; + vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv)); + } + + RelayInventory(inv); +} + + + + + + + + +// +// Templates for the publish and subscription system. +// The object being published as T& obj needs to have: +// a set setSources member +// specializations of AdvertInsert and AdvertErase +// Currently implemented for CTable and CProduct. +// + +template +void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) +{ + // Add to sources + obj.setSources.insert(pfrom->addr.ip); + + if (!AdvertInsert(obj)) + return; + + // Relay + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) + pnode->PushMessage("publish", nChannel, nHops, obj); +} + +template +void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) +{ + uint256 hash = obj.GetHash(); + + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) + pnode->PushMessage("pub-cancel", nChannel, nHops, hash); + + AdvertErase(obj); +} + +template +void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) +{ + // Remove a source + obj.setSources.erase(pfrom->addr.ip); + + // If no longer supported by any sources, cancel it + if (obj.setSources.empty()) + AdvertStopPublish(pfrom, nChannel, nHops, obj); +} + +#endif diff --git a/core/include/serialize.h b/core/include/serialize.h index f810b339c2..8e7677a2eb 100644 --- a/core/include/serialize.h +++ b/core/include/serialize.h @@ -9,6 +9,9 @@ #include #include #include +#include +#include +#include #include #include diff --git a/core/include/strlcpy.h b/core/include/strlcpy.h new file mode 100644 index 0000000000..d4d1908e7a --- /dev/null +++ b/core/include/strlcpy.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BITCOIN_STRLCPY_H +#define BITCOIN_STRLCPY_H +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +inline size_t strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) + { + while (--n != 0) + { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) + { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +inline size_t strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') + { + if (n != 1) + { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} +#endif diff --git a/core/include/util.h b/core/include/util.h index e25004fa1c..2d1d42975b 100644 --- a/core/include/util.h +++ b/core/include/util.h @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -37,7 +38,6 @@ typedef unsigned long long uint64; #define __forceinline inline #endif -//#define foreach BOOST_FOREACH #define loop for (;;) #define BEGIN(a) ((char*)&(a)) #define END(a) ((char*)&((&(a))[1])) diff --git a/core/src/util.cpp b/core/src/util.cpp new file mode 100644 index 0000000000..0e88e2eaf7 --- /dev/null +++ b/core/src/util.cpp @@ -0,0 +1,917 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#define __STDC_LIMIT_MACROS // to enable UINT64_MAX from stdint.h + +#include "util.h" +#include "main.h" +#include "strlcpy.h" + +#include + +#include +#include + +#include +#include + +using namespace std; + +map mapArgs; +map > mapMultiArgs; +bool fDebug = false; +bool fPrintToConsole = false; +bool fPrintToDebugger = false; +char pszSetDataDir[MAX_PATH] = ""; +bool fRequestShutdown = false; +bool fShutdown = false; +bool fDaemon = false; +bool fServer = false; +bool fCommandLine = false; +string strMiscWarning; +bool fTestNet = false; +bool fNoListen = false; +bool fLogTimestamps = false; + + + + +// Workaround for "multiple definition of `_tls_used'" +// http://svn.boost.org/trac/boost/ticket/4258 +extern "C" void tss_cleanup_implemented() { } + + + + + +// Init openssl library multithreading support +static boost::interprocess::interprocess_mutex** ppmutexOpenSSL; +void locking_callback(int mode, int i, const char* file, int line) +{ + if (mode & CRYPTO_LOCK) + ppmutexOpenSSL[i]->lock(); + else + ppmutexOpenSSL[i]->unlock(); +} + +// Init +class CInit +{ +public: + CInit() + { + // Init openssl library multithreading support + ppmutexOpenSSL = (boost::interprocess::interprocess_mutex**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(boost::interprocess::interprocess_mutex*)); + for (int i = 0; i < CRYPTO_num_locks(); i++) + ppmutexOpenSSL[i] = new boost::interprocess::interprocess_mutex(); + CRYPTO_set_locking_callback(locking_callback); + +#ifdef __WXMSW__ + // Seed random number generator with screen scrape and other hardware sources + RAND_screen(); +#endif + + // Seed random number generator with performance counter + RandAddSeed(); + } + ~CInit() + { + // Shutdown openssl library multithreading support + CRYPTO_set_locking_callback(NULL); + for (int i = 0; i < CRYPTO_num_locks(); i++) + delete ppmutexOpenSSL[i]; + OPENSSL_free(ppmutexOpenSSL); + } +} +instance_of_cinit; + + + + + + + + +void RandAddSeed() +{ + // Seed with CPU performance counter + int64 nCounter = GetPerformanceCounter(); + RAND_add(&nCounter, sizeof(nCounter), 1.5); + memset(&nCounter, 0, sizeof(nCounter)); +} + +void RandAddSeedPerfmon() +{ + RandAddSeed(); + + // This can take up to 2 seconds, so only do it every 10 minutes + static int64 nLastPerfmon; + if (GetTime() < nLastPerfmon + 10 * 60) + return; + nLastPerfmon = GetTime(); + +#ifdef __WXMSW__ + // Don't need this on Linux, OpenSSL automatically uses /dev/urandom + // Seed with the entire set of perfmon data + unsigned char pdata[250000]; + memset(pdata, 0, sizeof(pdata)); + unsigned long nSize = sizeof(pdata); + long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize); + RegCloseKey(HKEY_PERFORMANCE_DATA); + if (ret == ERROR_SUCCESS) + { + RAND_add(pdata, nSize, nSize/100.0); + memset(pdata, 0, nSize); + printf("%s RandAddSeed() %d bytes\n", DateTimeStrFormat("%x %H:%M", GetTime()).c_str(), nSize); + } +#endif +} + +uint64 GetRand(uint64 nMax) +{ + if (nMax == 0) + return 0; + + // The range of the random source must be a multiple of the modulus + // to give every possible output value an equal possibility + uint64 nRange = (UINT64_MAX / nMax) * nMax; + uint64 nRand = 0; + do + RAND_bytes((unsigned char*)&nRand, sizeof(nRand)); + while (nRand >= nRange); + return (nRand % nMax); +} + +int GetRandInt(int nMax) +{ + return GetRand(nMax); +} + + + + + + + + + + + +inline int OutputDebugStringF(const char* pszFormat, ...) +{ + int ret = 0; + if (fPrintToConsole) + { + // print to console + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + ret = vprintf(pszFormat, arg_ptr); + va_end(arg_ptr); + } + else + { + // print to debug.log + static FILE* fileout = NULL; + + if (!fileout) + { + char pszFile[MAX_PATH+100]; + GetDataDir(pszFile); + strlcat(pszFile, "/debug.log", sizeof(pszFile)); + fileout = fopen(pszFile, "a"); + if (fileout) setbuf(fileout, NULL); // unbuffered + } + if (fileout) + { + static bool fStartedNewLine = true; + + // Debug print useful for profiling + if (fLogTimestamps && fStartedNewLine) + fprintf(fileout, "%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); + if (pszFormat[strlen(pszFormat) - 1] == '\n') + fStartedNewLine = true; + else + fStartedNewLine = false; + + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + ret = vfprintf(fileout, pszFormat, arg_ptr); + va_end(arg_ptr); + } + } + +#ifdef __WXMSW__ + if (fPrintToDebugger) + { + static CCriticalSection cs_OutputDebugStringF; + + // accumulate a line at a time + CRITICAL_BLOCK(cs_OutputDebugStringF) + { + static char pszBuffer[50000]; + static char* pend; + if (pend == NULL) + pend = pszBuffer; + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + int limit = END(pszBuffer) - pend - 2; + int ret = _vsnprintf(pend, limit, pszFormat, arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + pend = END(pszBuffer) - 2; + *pend++ = '\n'; + } + else + pend += ret; + *pend = '\0'; + char* p1 = pszBuffer; + char* p2; + while (p2 = strchr(p1, '\n')) + { + p2++; + char c = *p2; + *p2 = '\0'; + OutputDebugStringA(p1); + *p2 = c; + p1 = p2; + } + if (p1 != pszBuffer) + memmove(pszBuffer, p1, pend - p1 + 1); + pend -= (p1 - pszBuffer); + } + } +#endif + return ret; +} + + +// Safer snprintf +// - prints up to limit-1 characters +// - output string is always null terminated even if limit reached +// - return value is the number of characters actually printed +int my_snprintf(char* buffer, size_t limit, const char* format, ...) +{ + if (limit == 0) + return 0; + va_list arg_ptr; + va_start(arg_ptr, format); + int ret = _vsnprintf(buffer, limit, format, arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + ret = limit - 1; + buffer[limit-1] = 0; + } + return ret; +} + + +string strprintf(const char* format, ...) +{ + char buffer[50000]; + char* p = buffer; + int limit = sizeof(buffer); + int ret; + loop + { + va_list arg_ptr; + va_start(arg_ptr, format); + ret = _vsnprintf(p, limit, format, arg_ptr); + va_end(arg_ptr); + if (ret >= 0 && ret < limit) + break; + if (p != buffer) + delete p; + limit *= 2; + p = new char[limit]; + if (p == NULL) + throw std::bad_alloc(); + } + string str(p, p+ret); + if (p != buffer) + delete p; + return str; +} + + +bool error(const char* format, ...) +{ + char buffer[50000]; + int limit = sizeof(buffer); + va_list arg_ptr; + va_start(arg_ptr, format); + int ret = _vsnprintf(buffer, limit, format, arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + ret = limit - 1; + buffer[limit-1] = 0; + } + printf("ERROR: %s\n", buffer); + return false; +} + + +void ParseString(const string& str, char c, vector& v) +{ + if (str.empty()) + return; + string::size_type i1 = 0; + string::size_type i2; + loop + { + i2 = str.find(c, i1); + if (i2 == str.npos) + { + v.push_back(str.substr(i1)); + return; + } + v.push_back(str.substr(i1, i2-i1)); + i1 = i2+1; + } +} + + +string FormatMoney(int64 n, bool fPlus) +{ + // Note: not using straight sprintf here because we do NOT want + // localized number formatting. + int64 n_abs = (n > 0 ? n : -n); + int64 quotient = n_abs/COIN; + int64 remainder = n_abs%COIN; + string str = strprintf("%"PRI64d".%08"PRI64d, quotient, remainder); + + // Right-trim excess 0's before the decimal point: + int nTrim = 0; + for (int i = str.size()-1; (str[i] == '0' && isdigit(str[i-2])); --i) + ++nTrim; + if (nTrim) + str.erase(str.size()-nTrim, nTrim); + + // Insert thousands-separators: + size_t point = str.find("."); + for (int i = (str.size()-point)+3; i < str.size(); i += 4) + if (isdigit(str[str.size() - i - 1])) + str.insert(str.size() - i, 1, ','); + if (n < 0) + str.insert((unsigned int)0, 1, '-'); + else if (fPlus && n > 0) + str.insert((unsigned int)0, 1, '+'); + return str; +} + + +bool ParseMoney(const string& str, int64& nRet) +{ + return ParseMoney(str.c_str(), nRet); +} + +bool ParseMoney(const char* pszIn, int64& nRet) +{ + string strWhole; + int64 nUnits = 0; + const char* p = pszIn; + while (isspace(*p)) + p++; + for (; *p; p++) + { + if (*p == ',' && p > pszIn && isdigit(p[-1]) && isdigit(p[1]) && isdigit(p[2]) && isdigit(p[3]) && !isdigit(p[4])) + continue; + if (*p == '.') + { + p++; + int64 nMult = CENT*10; + while (isdigit(*p) && (nMult > 0)) + { + nUnits += nMult * (*p++ - '0'); + nMult /= 10; + } + break; + } + if (isspace(*p)) + break; + if (!isdigit(*p)) + return false; + strWhole.insert(strWhole.end(), *p); + } + for (; *p; p++) + if (!isspace(*p)) + return false; + if (strWhole.size() > 14) + return false; + if (nUnits < 0 || nUnits > COIN) + return false; + int64 nWhole = atoi64(strWhole); + int64 nValue = nWhole*COIN + nUnits; + + nRet = nValue; + return true; +} + + +vector ParseHex(const char* psz) +{ + static char phexdigit[256] = + { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1, + -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, }; + + // convert hex dump to vector + vector vch; + loop + { + while (isspace(*psz)) + psz++; + char c = phexdigit[(unsigned char)*psz++]; + if (c == (char)-1) + break; + unsigned char n = (c << 4); + c = phexdigit[(unsigned char)*psz++]; + if (c == (char)-1) + break; + n |= c; + vch.push_back(n); + } + return vch; +} + +vector ParseHex(const string& str) +{ + return ParseHex(str.c_str()); +} + + +void ParseParameters(int argc, char* argv[]) +{ + mapArgs.clear(); + mapMultiArgs.clear(); + for (int i = 1; i < argc; i++) + { + char psz[10000]; + strlcpy(psz, argv[i], sizeof(psz)); + char* pszValue = (char*)""; + if (strchr(psz, '=')) + { + pszValue = strchr(psz, '='); + *pszValue++ = '\0'; + } + #ifdef __WXMSW__ + _strlwr(psz); + if (psz[0] == '/') + psz[0] = '-'; + #endif + if (psz[0] != '-') + break; + mapArgs[psz] = pszValue; + mapMultiArgs[psz].push_back(pszValue); + } +} + + +const char* wxGetTranslation(const char* pszEnglish) +{ +#ifdef GUI + // Wrapper of wxGetTranslation returning the same const char* type as was passed in + static CCriticalSection cs; + CRITICAL_BLOCK(cs) + { + // Look in cache + static map mapCache; + map::iterator mi = mapCache.find(pszEnglish); + if (mi != mapCache.end()) + return (*mi).second; + + // wxWidgets translation + wxString strTranslated = wxGetTranslation(wxString(pszEnglish, wxConvUTF8)); + + // We don't cache unknown strings because caller might be passing in a + // dynamic string and we would keep allocating memory for each variation. + if (strcmp(pszEnglish, strTranslated.utf8_str()) == 0) + return pszEnglish; + + // Add to cache, memory doesn't need to be freed. We only cache because + // we must pass back a pointer to permanently allocated memory. + char* pszCached = new char[strlen(strTranslated.utf8_str())+1]; + strcpy(pszCached, strTranslated.utf8_str()); + mapCache[pszEnglish] = pszCached; + return pszCached; + } + return NULL; +#else + return pszEnglish; +#endif +} + + +bool WildcardMatch(const char* psz, const char* mask) +{ + loop + { + switch (*mask) + { + case '\0': + return (*psz == '\0'); + case '*': + return WildcardMatch(psz, mask+1) || (*psz && WildcardMatch(psz+1, mask)); + case '?': + if (*psz == '\0') + return false; + break; + default: + if (*psz != *mask) + return false; + break; + } + psz++; + mask++; + } +} + +bool WildcardMatch(const string& str, const string& mask) +{ + return WildcardMatch(str.c_str(), mask.c_str()); +} + + + + + + + + +void FormatException(char* pszMessage, std::exception* pex, const char* pszThread) +{ +#ifdef __WXMSW__ + char pszModule[MAX_PATH]; + pszModule[0] = '\0'; + GetModuleFileNameA(NULL, pszModule, sizeof(pszModule)); +#else + const char* pszModule = "bitcoin"; +#endif + if (pex) + snprintf(pszMessage, 1000, + "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread); + else + snprintf(pszMessage, 1000, + "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread); +} + +void LogException(std::exception* pex, const char* pszThread) +{ + char pszMessage[10000]; + FormatException(pszMessage, pex, pszThread); + printf("\n%s", pszMessage); +} + +void PrintException(std::exception* pex, const char* pszThread) +{ + char pszMessage[10000]; + FormatException(pszMessage, pex, pszThread); + printf("\n\n************************\n%s\n", pszMessage); + fprintf(stderr, "\n\n************************\n%s\n", pszMessage); + strMiscWarning = pszMessage; +#ifdef GUI + if (wxTheApp && !fDaemon) + MyMessageBox(pszMessage, "Bitcoin", wxOK | wxICON_ERROR); +#endif + throw; +} + +void ThreadOneMessageBox(string strMessage) +{ + // Skip message boxes if one is already open + static bool fMessageBoxOpen; + if (fMessageBoxOpen) + return; + fMessageBoxOpen = true; + ThreadSafeMessageBox(strMessage, "Bitcoin", wxOK | wxICON_EXCLAMATION); + fMessageBoxOpen = false; +} + +void PrintExceptionContinue(std::exception* pex, const char* pszThread) +{ + char pszMessage[10000]; + FormatException(pszMessage, pex, pszThread); + printf("\n\n************************\n%s\n", pszMessage); + fprintf(stderr, "\n\n************************\n%s\n", pszMessage); + strMiscWarning = pszMessage; +#ifdef GUI + if (wxTheApp && !fDaemon) + boost::thread(boost::bind(ThreadOneMessageBox, string(pszMessage))); +#endif +} + + + + + + + + +#ifdef __WXMSW__ +typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate); + +string MyGetSpecialFolderPath(int nFolder, bool fCreate) +{ + char pszPath[MAX_PATH+100] = ""; + + // SHGetSpecialFolderPath isn't always available on old Windows versions + HMODULE hShell32 = LoadLibraryA("shell32.dll"); + if (hShell32) + { + PSHGETSPECIALFOLDERPATHA pSHGetSpecialFolderPath = + (PSHGETSPECIALFOLDERPATHA)GetProcAddress(hShell32, "SHGetSpecialFolderPathA"); + if (pSHGetSpecialFolderPath) + (*pSHGetSpecialFolderPath)(NULL, pszPath, nFolder, fCreate); + FreeModule(hShell32); + } + + // Backup option + if (pszPath[0] == '\0') + { + if (nFolder == CSIDL_STARTUP) + { + strcpy(pszPath, getenv("USERPROFILE")); + strcat(pszPath, "\\Start Menu\\Programs\\Startup"); + } + else if (nFolder == CSIDL_APPDATA) + { + strcpy(pszPath, getenv("APPDATA")); + } + } + + return pszPath; +} +#endif + +string GetDefaultDataDir() +{ + // Windows: C:\Documents and Settings\username\Application Data\Bitcoin + // Mac: ~/Library/Application Support/Bitcoin + // Unix: ~/.bitcoin +#ifdef __WXMSW__ + // Windows + return MyGetSpecialFolderPath(CSIDL_APPDATA, true) + "\\Bitcoin"; +#else + char* pszHome = getenv("HOME"); + if (pszHome == NULL || strlen(pszHome) == 0) + pszHome = (char*)"/"; + string strHome = pszHome; + if (strHome[strHome.size()-1] != '/') + strHome += '/'; +#ifdef __WXMAC_OSX__ + // Mac + strHome += "Library/Application Support/"; + filesystem::create_directory(strHome.c_str()); + return strHome + "Bitcoin"; +#else + // Unix + return strHome + ".bitcoin"; +#endif +#endif +} + +void GetDataDir(char* pszDir) +{ + // pszDir must be at least MAX_PATH length. + int nVariation; + if (pszSetDataDir[0] != 0) + { + strlcpy(pszDir, pszSetDataDir, MAX_PATH); + nVariation = 0; + } + else + { + // This can be called during exceptions by printf, so we cache the + // value so we don't have to do memory allocations after that. + static char pszCachedDir[MAX_PATH]; + if (pszCachedDir[0] == 0) + strlcpy(pszCachedDir, GetDefaultDataDir().c_str(), sizeof(pszCachedDir)); + strlcpy(pszDir, pszCachedDir, MAX_PATH); + nVariation = 1; + } + if (fTestNet) + { + char* p = pszDir + strlen(pszDir); + if (p > pszDir && p[-1] != '/' && p[-1] != '\\') + *p++ = '/'; + strcpy(p, "testnet"); + nVariation += 2; + } + static bool pfMkdir[4]; + if (!pfMkdir[nVariation]) + { + pfMkdir[nVariation] = true; + filesystem::create_directory(pszDir); + } +} + +string GetDataDir() +{ + char pszDir[MAX_PATH]; + GetDataDir(pszDir); + return pszDir; +} + +string GetConfigFile() +{ + namespace fs = boost::filesystem; + fs::path pathConfig(GetArg("-conf", "bitcoin.conf")); + if (!pathConfig.is_complete()) + pathConfig = fs::path(GetDataDir()) / pathConfig; + return pathConfig.string(); +} + +void ReadConfigFile(map& mapSettingsRet, + map >& mapMultiSettingsRet) +{ + namespace fs = boost::filesystem; + namespace pod = boost::program_options::detail; + + fs::ifstream streamConfig(GetConfigFile()); + if (!streamConfig.good()) + return; + + set setOptions; + setOptions.insert("*"); + + for (pod::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) + { + // Don't overwrite existing settings so command line settings override bitcoin.conf + string strKey = string("-") + it->string_key; + if (mapSettingsRet.count(strKey) == 0) + mapSettingsRet[strKey] = it->value[0]; + mapMultiSettingsRet[strKey].push_back(it->value[0]); + } +} + +string GetPidFile() +{ + namespace fs = boost::filesystem; + fs::path pathConfig(GetArg("-pid", "bitcoind.pid")); + if (!pathConfig.is_complete()) + pathConfig = fs::path(GetDataDir()) / pathConfig; + return pathConfig.string(); +} + +void CreatePidFile(string pidFile, pid_t pid) +{ + FILE* file; + if (file = fopen(pidFile.c_str(), "w")) + { + fprintf(file, "%d\n", pid); + fclose(file); + } +} + +int GetFilesize(FILE* file) +{ + int nSavePos = ftell(file); + int nFilesize = -1; + if (fseek(file, 0, SEEK_END) == 0) + nFilesize = ftell(file); + fseek(file, nSavePos, SEEK_SET); + return nFilesize; +} + +void ShrinkDebugFile() +{ + // Scroll debug.log if it's getting too big + string strFile = GetDataDir() + "/debug.log"; + FILE* file = fopen(strFile.c_str(), "r"); + if (file && GetFilesize(file) > 10 * 1000000) + { + // Restart the file with some of the end + char pch[200000]; + fseek(file, -sizeof(pch), SEEK_END); + int nBytes = fread(pch, 1, sizeof(pch), file); + fclose(file); + if (file = fopen(strFile.c_str(), "w")) + { + fwrite(pch, 1, nBytes, file); + fclose(file); + } + } +} + + + + + + + + +// +// "Never go to sea with two chronometers; take one or three." +// Our three time sources are: +// - System clock +// - Median of other nodes's clocks +// - The user (asking the user to fix the system clock if the first two disagree) +// +int64 GetTime() +{ + return time(NULL); +} + +static int64 nTimeOffset = 0; + +int64 GetAdjustedTime() +{ + return GetTime() + nTimeOffset; +} + +void AddTimeData(unsigned int ip, int64 nTime) +{ + int64 nOffsetSample = nTime - GetTime(); + + // Ignore duplicates + static set setKnown; + if (!setKnown.insert(ip).second) + return; + + // Add data + static vector vTimeOffsets; + if (vTimeOffsets.empty()) + vTimeOffsets.push_back(0); + vTimeOffsets.push_back(nOffsetSample); + printf("Added time data, samples %d, offset %+"PRI64d" (%+"PRI64d" minutes)\n", vTimeOffsets.size(), vTimeOffsets.back(), vTimeOffsets.back()/60); + if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) + { + sort(vTimeOffsets.begin(), vTimeOffsets.end()); + int64 nMedian = vTimeOffsets[vTimeOffsets.size()/2]; + // Only let other nodes change our time by so much + if (abs64(nMedian) < 70 * 60) + { + nTimeOffset = nMedian; + } + else + { + nTimeOffset = 0; + + static bool fDone; + if (!fDone) + { + // If nobody has a time different than ours but within 5 minutes of ours, give a warning + bool fMatch = false; + foreach(int64 nOffset, vTimeOffsets) + if (nOffset != 0 && abs64(nOffset) < 5 * 60) + fMatch = true; + + if (!fMatch) + { + fDone = true; + string strMessage = _("Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly."); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + boost::thread(boost::bind(ThreadSafeMessageBox, strMessage+" ", string("Bitcoin"), wxOK | wxICON_EXCLAMATION, (wxWindow*)NULL, -1, -1)); + } + } + } + foreach(int64 n, vTimeOffsets) + printf("%+"PRI64d" ", n); + printf("| nTimeOffset = %+"PRI64d" (%+"PRI64d" minutes)\n", nTimeOffset, nTimeOffset/60); + } +} + + + + + + + + + +string FormatVersion(int nVersion) +{ + if (nVersion%100 == 0) + return strprintf("%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100); + else + return strprintf("%d.%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100, nVersion%100); +} + +string FormatFullVersion() +{ + string s = FormatVersion(VERSION) + pszSubVer; + if (VERSION_IS_BETA) + s += _("-beta"); + return s; +} + + + + + diff --git a/gui/include/bitcoinaddressvalidator.h b/gui/include/bitcoinaddressvalidator.h index d1a06f7e57..8322eef739 100644 --- a/gui/include/bitcoinaddressvalidator.h +++ b/gui/include/bitcoinaddressvalidator.h @@ -3,8 +3,6 @@ #include -#include - class BitcoinAddressValidator : public QRegExpValidator { Q_OBJECT diff --git a/gui/src/sendcoinsdialog.cpp b/gui/src/sendcoinsdialog.cpp index 6c216d397b..ce95244dcb 100644 --- a/gui/src/sendcoinsdialog.cpp +++ b/gui/src/sendcoinsdialog.cpp @@ -7,6 +7,8 @@ #include #include +#include "base58.h" + SendCoinsDialog::SendCoinsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::SendCoinsDialog) @@ -24,7 +26,16 @@ SendCoinsDialog::~SendCoinsDialog() void SendCoinsDialog::on_sendButton_clicked() { - accept(); + QByteArray payTo = ui->payTo->text().toUtf8(); + uint160 payToHash = 0; + if(AddressToHash160(payTo.constData(), payToHash)) + { + accept(); + } + else + { + + } } void SendCoinsDialog::on_pasteButton_clicked() -- cgit v1.2.3 From 69d605f410c83235aa7b757445e7d0166fcfe2d9 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 14 May 2011 20:10:21 +0200 Subject: integration of core bitcoin --- TODO | 26 + bitcoin.pro | 34 +- core/include/db.h | 526 +++ core/include/headers.h | 147 + core/include/init.h | 11 + core/include/irc.h | 13 + core/include/key.h | 18 +- core/include/main.h | 204 +- core/include/noui.h | 67 + core/include/rpc.h | 6 + core/include/script.h | 718 ++++ core/src/db.cpp | 1026 ++++++ core/src/init.cpp | 522 +++ core/src/irc.cpp | 438 +++ core/src/main.cpp | 4024 +++++++++++++++++++++++ core/src/net.cpp | 1665 ++++++++++ core/src/rpc.cpp | 2195 +++++++++++++ core/src/script.cpp | 1208 +++++++ core/src/util.cpp | 21 +- json/include/json/json_spirit.h | 18 + json/include/json/json_spirit_error_position.h | 54 + json/include/json/json_spirit_reader.h | 62 + json/include/json/json_spirit_reader_template.h | 612 ++++ json/include/json/json_spirit_stream_reader.h | 70 + json/include/json/json_spirit_utils.h | 61 + json/include/json/json_spirit_value.h | 534 +++ json/include/json/json_spirit_writer.h | 50 + json/include/json/json_spirit_writer_template.h | 248 ++ json/src/json_spirit_reader.cpp | 137 + json/src/json_spirit_value.cpp | 8 + json/src/json_spirit_writer.cpp | 95 + 31 files changed, 14691 insertions(+), 127 deletions(-) create mode 100644 core/include/db.h create mode 100644 core/include/headers.h create mode 100644 core/include/init.h create mode 100644 core/include/irc.h create mode 100644 core/include/noui.h create mode 100644 core/include/rpc.h create mode 100644 core/include/script.h create mode 100644 core/src/db.cpp create mode 100644 core/src/init.cpp create mode 100644 core/src/irc.cpp create mode 100644 core/src/main.cpp create mode 100644 core/src/net.cpp create mode 100644 core/src/rpc.cpp create mode 100644 core/src/script.cpp create mode 100644 json/include/json/json_spirit.h create mode 100644 json/include/json/json_spirit_error_position.h create mode 100644 json/include/json/json_spirit_reader.h create mode 100644 json/include/json/json_spirit_reader_template.h create mode 100644 json/include/json/json_spirit_stream_reader.h create mode 100644 json/include/json/json_spirit_utils.h create mode 100644 json/include/json/json_spirit_value.h create mode 100644 json/include/json/json_spirit_writer.h create mode 100644 json/include/json/json_spirit_writer_template.h create mode 100644 json/src/json_spirit_reader.cpp create mode 100644 json/src/json_spirit_value.cpp create mode 100644 json/src/json_spirit_writer.cpp diff --git a/TODO b/TODO index 4f81ec95f6..33cf4063f7 100644 --- a/TODO +++ b/TODO @@ -62,3 +62,29 @@ AboutDialog - Make icon blend into taskbar and maybe silver/grey Same for the other icons? + + +Done: + +Compatibility with Qt, and some more modularity + +- Put guard statements around header files. + +- Proper include between header files; remove central "header.h" file. + +- Removed macro foreach: conflicts with Qt keyword foreach, replaced back with BOOST_FOREACH + +- Prefix stdlib structures and functions with std:: in headers; "using namespace" in header files is + generally frowned upon + +Todo: + +- Repeated in all files? +#define __STDC_LIMIT_MACROS // to enable UINT64_MAX from stdint.h +#include "main.h" +#ifndef GUI +#include "noui.h" +#endif + +- Check windows support / cross platform + diff --git a/bitcoin.pro b/bitcoin.pro index 69340137aa..b323ba8a54 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -1,8 +1,8 @@ TEMPLATE = app TARGET = DEPENDPATH += . -INCLUDEPATH += gui/include core/include cryptopp/include -unix:LIBS += -lssl +INCLUDEPATH += gui/include core/include cryptopp/include json/include +unix:LIBS += -lssl -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx # Input HEADERS += gui/include/bitcoingui.h \ @@ -34,7 +34,23 @@ HEADERS += gui/include/bitcoingui.h \ core/include/strlcpy.h \ core/include/main.h \ core/include/net.h \ - core/include/key.h + core/include/key.h \ + core/include/db.h \ + core/include/script.h \ + core/include/noui.h \ + core/include/init.h \ + core/include/headers.h \ + core/include/irc.h \ + json/include/json/json_spirit_writer_template.h \ + json/include/json/json_spirit_writer.h \ + json/include/json/json_spirit_value.h \ + json/include/json/json_spirit_utils.h \ + json/include/json/json_spirit_stream_reader.h \ + json/include/json/json_spirit_reader_template.h \ + json/include/json/json_spirit_reader.h \ + json/include/json/json_spirit_error_position.h \ + json/include/json/json_spirit.h \ + core/include/rpc.h SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/transactiontablemodel.cpp \ gui/src/addresstablemodel.cpp \ @@ -47,7 +63,17 @@ SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/bitcoinaddressvalidator.cpp \ cryptopp/src/sha.cpp \ cryptopp/src/cpu.cpp \ - core/src/util.cpp + core/src/util.cpp \ + core/src/script.cpp \ + core/src/main.cpp \ + core/src/init.cpp \ + core/src/rpc.cpp \ + core/src/net.cpp \ + core/src/irc.cpp \ + core/src/db.cpp \ + json/src/json_spirit_writer.cpp \ + json/src/json_spirit_value.cpp \ + json/src/json_spirit_reader.cpp RESOURCES += \ gui/bitcoin.qrc diff --git a/core/include/db.h b/core/include/db.h new file mode 100644 index 0000000000..9826194ed0 --- /dev/null +++ b/core/include/db.h @@ -0,0 +1,526 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_DB_H +#define BITCOIN_DB_H + +#include "key.h" + +#include +#include +#include + +#include + +class CTransaction; +class CTxIndex; +class CDiskBlockIndex; +class CDiskTxPos; +class COutPoint; +class CUser; +class CReview; +class CAddress; +class CWalletTx; +class CAccount; +class CAccountingEntry; +class CBlockLocator; + +extern std::map mapAddressBook; +extern CCriticalSection cs_mapAddressBook; +extern std::vector vchDefaultKey; +extern bool fClient; +extern int nBestHeight; + + +extern unsigned int nWalletDBUpdated; +extern DbEnv dbenv; + + +extern void DBFlush(bool fShutdown); +extern std::vector GetKeyFromKeyPool(); +extern int64 GetOldestKeyPoolTime(); + + + + +class CDB +{ +protected: + Db* pdb; + std::string strFile; + std::vector vTxn; + bool fReadOnly; + + explicit CDB(const char* pszFile, const char* pszMode="r+"); + ~CDB() { Close(); } +public: + void Close(); +private: + CDB(const CDB&); + void operator=(const CDB&); + +protected: + template + bool Read(const K& key, T& value) + { + if (!pdb) + return false; + + // Key + CDataStream ssKey(SER_DISK); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Read + Dbt datValue; + datValue.set_flags(DB_DBT_MALLOC); + int ret = pdb->get(GetTxn(), &datKey, &datValue, 0); + memset(datKey.get_data(), 0, datKey.get_size()); + if (datValue.get_data() == NULL) + return false; + + // Unserialize value + CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK); + ssValue >> value; + + // Clear and free memory + memset(datValue.get_data(), 0, datValue.get_size()); + free(datValue.get_data()); + return (ret == 0); + } + + template + bool Write(const K& key, const T& value, bool fOverwrite=true) + { + if (!pdb) + return false; + if (fReadOnly) + assert(("Write called on database in read-only mode", false)); + + // Key + CDataStream ssKey(SER_DISK); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Value + CDataStream ssValue(SER_DISK); + ssValue.reserve(10000); + ssValue << value; + Dbt datValue(&ssValue[0], ssValue.size()); + + // Write + int ret = pdb->put(GetTxn(), &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); + + // Clear memory in case it was a private key + memset(datKey.get_data(), 0, datKey.get_size()); + memset(datValue.get_data(), 0, datValue.get_size()); + return (ret == 0); + } + + template + bool Erase(const K& key) + { + if (!pdb) + return false; + if (fReadOnly) + assert(("Erase called on database in read-only mode", false)); + + // Key + CDataStream ssKey(SER_DISK); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Erase + int ret = pdb->del(GetTxn(), &datKey, 0); + + // Clear memory + memset(datKey.get_data(), 0, datKey.get_size()); + return (ret == 0 || ret == DB_NOTFOUND); + } + + template + bool Exists(const K& key) + { + if (!pdb) + return false; + + // Key + CDataStream ssKey(SER_DISK); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Exists + int ret = pdb->exists(GetTxn(), &datKey, 0); + + // Clear memory + memset(datKey.get_data(), 0, datKey.get_size()); + return (ret == 0); + } + + Dbc* GetCursor() + { + if (!pdb) + return NULL; + Dbc* pcursor = NULL; + int ret = pdb->cursor(NULL, &pcursor, 0); + if (ret != 0) + return NULL; + return pcursor; + } + + int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags=DB_NEXT) + { + // Read at cursor + Dbt datKey; + if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) + { + datKey.set_data(&ssKey[0]); + datKey.set_size(ssKey.size()); + } + Dbt datValue; + if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) + { + datValue.set_data(&ssValue[0]); + datValue.set_size(ssValue.size()); + } + datKey.set_flags(DB_DBT_MALLOC); + datValue.set_flags(DB_DBT_MALLOC); + int ret = pcursor->get(&datKey, &datValue, fFlags); + if (ret != 0) + return ret; + else if (datKey.get_data() == NULL || datValue.get_data() == NULL) + return 99999; + + // Convert to streams + ssKey.SetType(SER_DISK); + ssKey.clear(); + ssKey.write((char*)datKey.get_data(), datKey.get_size()); + ssValue.SetType(SER_DISK); + ssValue.clear(); + ssValue.write((char*)datValue.get_data(), datValue.get_size()); + + // Clear and free memory + memset(datKey.get_data(), 0, datKey.get_size()); + memset(datValue.get_data(), 0, datValue.get_size()); + free(datKey.get_data()); + free(datValue.get_data()); + return 0; + } + + DbTxn* GetTxn() + { + if (!vTxn.empty()) + return vTxn.back(); + else + return NULL; + } + +public: + bool TxnBegin() + { + if (!pdb) + return false; + DbTxn* ptxn = NULL; + int ret = dbenv.txn_begin(GetTxn(), &ptxn, DB_TXN_NOSYNC); + if (!ptxn || ret != 0) + return false; + vTxn.push_back(ptxn); + return true; + } + + bool TxnCommit() + { + if (!pdb) + return false; + if (vTxn.empty()) + return false; + int ret = vTxn.back()->commit(0); + vTxn.pop_back(); + return (ret == 0); + } + + bool TxnAbort() + { + if (!pdb) + return false; + if (vTxn.empty()) + return false; + int ret = vTxn.back()->abort(); + vTxn.pop_back(); + return (ret == 0); + } + + bool ReadVersion(int& nVersion) + { + nVersion = 0; + return Read(std::string("version"), nVersion); + } + + bool WriteVersion(int nVersion) + { + return Write(std::string("version"), nVersion); + } +}; + + + + + + + + +class CTxDB : public CDB +{ +public: + CTxDB(const char* pszMode="r+") : CDB("blkindex.dat", pszMode) { } +private: + CTxDB(const CTxDB&); + void operator=(const CTxDB&); +public: + bool ReadTxIndex(uint256 hash, CTxIndex& txindex); + bool UpdateTxIndex(uint256 hash, const CTxIndex& txindex); + bool AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight); + bool EraseTxIndex(const CTransaction& tx); + bool ContainsTx(uint256 hash); + bool ReadOwnerTxes(uint160 hash160, int nHeight, std::vector& vtx); + bool ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex); + bool ReadDiskTx(uint256 hash, CTransaction& tx); + bool ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex); + bool ReadDiskTx(COutPoint outpoint, CTransaction& tx); + bool WriteBlockIndex(const CDiskBlockIndex& blockindex); + bool EraseBlockIndex(uint256 hash); + bool ReadHashBestChain(uint256& hashBestChain); + bool WriteHashBestChain(uint256 hashBestChain); + bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork); + bool WriteBestInvalidWork(CBigNum bnBestInvalidWork); + bool LoadBlockIndex(); +}; + + + + + +class CAddrDB : public CDB +{ +public: + CAddrDB(const char* pszMode="r+") : CDB("addr.dat", pszMode) { } +private: + CAddrDB(const CAddrDB&); + void operator=(const CAddrDB&); +public: + bool WriteAddress(const CAddress& addr); + bool EraseAddress(const CAddress& addr); + bool LoadAddresses(); +}; + +bool LoadAddresses(); + + + + + + +class CKeyPool +{ +public: + int64 nTime; + std::vector vchPubKey; + + CKeyPool() + { + nTime = GetTime(); + } + + CKeyPool(const std::vector& vchPubKeyIn) + { + nTime = GetTime(); + vchPubKey = vchPubKeyIn; + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(nTime); + READWRITE(vchPubKey); + ) +}; + + + + +class CWalletDB : public CDB +{ +public: + CWalletDB(const char* pszMode="r+") : CDB("wallet.dat", pszMode) + { + } +private: + CWalletDB(const CWalletDB&); + void operator=(const CWalletDB&); +public: + bool ReadName(const std::string& strAddress, std::string& strName) + { + strName = ""; + return Read(std::make_pair(std::string("name"), strAddress), strName); + } + + bool WriteName(const std::string& strAddress, const std::string& strName) + { + CRITICAL_BLOCK(cs_mapAddressBook) + mapAddressBook[strAddress] = strName; + nWalletDBUpdated++; + return Write(std::make_pair(std::string("name"), strAddress), strName); + } + + bool EraseName(const std::string& strAddress) + { + // This should only be used for sending addresses, never for receiving addresses, + // receiving addresses must always have an address book entry if they're not change return. + CRITICAL_BLOCK(cs_mapAddressBook) + mapAddressBook.erase(strAddress); + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("name"), strAddress)); + } + + bool ReadTx(uint256 hash, CWalletTx& wtx) + { + return Read(std::make_pair(std::string("tx"), hash), wtx); + } + + bool WriteTx(uint256 hash, const CWalletTx& wtx) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("tx"), hash), wtx); + } + + bool EraseTx(uint256 hash) + { + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("tx"), hash)); + } + + bool ReadKey(const std::vector& vchPubKey, CPrivKey& vchPrivKey) + { + vchPrivKey.clear(); + return Read(std::make_pair(std::string("key"), vchPubKey), vchPrivKey); + } + + bool WriteKey(const std::vector& vchPubKey, const CPrivKey& vchPrivKey) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false); + } + + bool WriteBestBlock(const CBlockLocator& locator) + { + nWalletDBUpdated++; + return Write(std::string("bestblock"), locator); + } + + bool ReadBestBlock(CBlockLocator& locator) + { + return Read(std::string("bestblock"), locator); + } + + bool ReadDefaultKey(std::vector& vchPubKey) + { + vchPubKey.clear(); + return Read(std::string("defaultkey"), vchPubKey); + } + + bool WriteDefaultKey(const std::vector& vchPubKey) + { + vchDefaultKey = vchPubKey; + nWalletDBUpdated++; + return Write(std::string("defaultkey"), vchPubKey); + } + + template + bool ReadSetting(const std::string& strKey, T& value) + { + return Read(std::make_pair(std::string("setting"), strKey), value); + } + + template + bool WriteSetting(const std::string& strKey, const T& value) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("setting"), strKey), value); + } + + bool ReadAccount(const std::string& strAccount, CAccount& account); + bool WriteAccount(const std::string& strAccount, const CAccount& account); + bool WriteAccountingEntry(const CAccountingEntry& acentry); + int64 GetAccountCreditDebit(const std::string& strAccount); + void ListAccountCreditDebit(const std::string& strAccount, std::list& acentries); + + bool LoadWallet(); +protected: + void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool); + void KeepKey(int64 nIndex); + static void ReturnKey(int64 nIndex); + friend class CReserveKey; + friend std::vector GetKeyFromKeyPool(); + friend int64 GetOldestKeyPoolTime(); +}; + +bool LoadWallet(bool& fFirstRunRet); +void BackupWallet(const std::string& strDest); + +inline bool SetAddressBookName(const std::string& strAddress, const std::string& strName) +{ + return CWalletDB().WriteName(strAddress, strName); +} + +class CReserveKey +{ +protected: + int64 nIndex; + std::vector vchPubKey; +public: + CReserveKey() + { + nIndex = -1; + } + + ~CReserveKey() + { + if (!fShutdown) + ReturnKey(); + } + + std::vector GetReservedKey() + { + if (nIndex == -1) + { + CKeyPool keypool; + CWalletDB().ReserveKeyFromKeyPool(nIndex, keypool); + vchPubKey = keypool.vchPubKey; + } + assert(!vchPubKey.empty()); + return vchPubKey; + } + + void KeepKey() + { + if (nIndex != -1) + CWalletDB().KeepKey(nIndex); + nIndex = -1; + vchPubKey.clear(); + } + + void ReturnKey() + { + if (nIndex != -1) + CWalletDB::ReturnKey(nIndex); + nIndex = -1; + vchPubKey.clear(); + } +}; + +#endif diff --git a/core/include/headers.h b/core/include/headers.h new file mode 100644 index 0000000000..d40c5ed0a9 --- /dev/null +++ b/core/include/headers.h @@ -0,0 +1,147 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#ifdef _MSC_VER +#pragma warning(disable:4786) +#pragma warning(disable:4804) +#pragma warning(disable:4805) +#pragma warning(disable:4717) +#endif +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0500 +#ifdef _WIN32_IE +#undef _WIN32_IE +#endif +#define _WIN32_IE 0x0400 +#define WIN32_LEAN_AND_MEAN 1 +#define __STDC_LIMIT_MACROS // to enable UINT64_MAX from stdint.h +#if (defined(__unix__) || defined(unix)) && !defined(USG) +#include // to get BSD define +#endif +#ifdef __WXMAC_OSX__ +#ifndef BSD +#define BSD 1 +#endif +#endif +#ifdef GUI +#include +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __WXMSW__ +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#ifdef BSD +#include +#endif + + +#pragma hdrstop + +#include "strlcpy.h" +#include "serialize.h" +#include "uint256.h" +#include "util.h" +#include "key.h" +#include "bignum.h" +#include "base58.h" +#include "script.h" +#include "db.h" +#include "net.h" +#include "irc.h" +#include "main.h" +#include "rpc.h" +#ifdef GUI +#include "uibase.h" +#include "ui.h" +#else +#include "noui.h" +#endif +#include "init.h" + +#ifdef GUI +#include "xpm/addressbook16.xpm" +#include "xpm/addressbook20.xpm" +#include "xpm/bitcoin16.xpm" +#include "xpm/bitcoin20.xpm" +#include "xpm/bitcoin32.xpm" +#include "xpm/bitcoin48.xpm" +#include "xpm/bitcoin80.xpm" +#include "xpm/check.xpm" +#include "xpm/send16.xpm" +#include "xpm/send16noshadow.xpm" +#include "xpm/send20.xpm" +#include "xpm/about.xpm" +#endif diff --git a/core/include/init.h b/core/include/init.h new file mode 100644 index 0000000000..61b2728576 --- /dev/null +++ b/core/include/init.h @@ -0,0 +1,11 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_INIT_H +#define BITCOIN_INIT_H + +void Shutdown(void* parg); +bool AppInit(int argc, char* argv[]); +bool AppInit2(int argc, char* argv[]); + +#endif diff --git a/core/include/irc.h b/core/include/irc.h new file mode 100644 index 0000000000..18e53597f6 --- /dev/null +++ b/core/include/irc.h @@ -0,0 +1,13 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_IRC_H +#define BITCOIN_IRC_H + +bool RecvLine(SOCKET hSocket, std::string& strLine); +void ThreadIRCSeed(void* parg); + +extern int nGotIRCAddresses; +extern bool fGotExternalIP; + +#endif diff --git a/core/include/key.h b/core/include/key.h index 390602e1ef..c973d6eb82 100644 --- a/core/include/key.h +++ b/core/include/key.h @@ -4,6 +4,10 @@ #ifndef BITCOIN_KEY_H #define BITCOIN_KEY_H +#include +#include +#include + // secp160k1 // const unsigned int PRIVATE_KEY_SIZE = 192; // const unsigned int PUBLIC_KEY_SIZE = 41; @@ -110,7 +114,7 @@ public: return vchPrivKey; } - bool SetPubKey(const vector& vchPubKey) + bool SetPubKey(const std::vector& vchPubKey) { const unsigned char* pbegin = &vchPubKey[0]; if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size())) @@ -119,19 +123,19 @@ public: return true; } - vector GetPubKey() const + std::vector GetPubKey() const { unsigned int nSize = i2o_ECPublicKey(pkey, NULL); if (!nSize) throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed"); - vector vchPubKey(nSize, 0); + std::vector vchPubKey(nSize, 0); unsigned char* pbegin = &vchPubKey[0]; if (i2o_ECPublicKey(pkey, &pbegin) != nSize) throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size"); return vchPubKey; } - bool Sign(uint256 hash, vector& vchSig) + bool Sign(uint256 hash, std::vector& vchSig) { vchSig.clear(); unsigned char pchSig[10000]; @@ -143,7 +147,7 @@ public: return true; } - bool Verify(uint256 hash, const vector& vchSig) + bool Verify(uint256 hash, const std::vector& vchSig) { // -1 = error, 0 = bad sig, 1 = good if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) @@ -151,7 +155,7 @@ public: return true; } - static bool Sign(const CPrivKey& vchPrivKey, uint256 hash, vector& vchSig) + static bool Sign(const CPrivKey& vchPrivKey, uint256 hash, std::vector& vchSig) { CKey key; if (!key.SetPrivKey(vchPrivKey)) @@ -159,7 +163,7 @@ public: return key.Sign(hash, vchSig); } - static bool Verify(const vector& vchPubKey, uint256 hash, const vector& vchSig) + static bool Verify(const std::vector& vchPubKey, uint256 hash, const std::vector& vchSig) { CKey key; if (!key.SetPubKey(vchPubKey)) diff --git a/core/include/main.h b/core/include/main.h index 7c9ace06c3..92b73fe5ad 100644 --- a/core/include/main.h +++ b/core/include/main.h @@ -7,6 +7,10 @@ #include "bignum.h" #include "net.h" #include "key.h" +#include "db.h" +#include "script.h" + +#include class COutPoint; class CInPoint; @@ -79,7 +83,7 @@ bool CheckDiskSpace(uint64 nAdditionalBytes=0); FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); FILE* AppendBlockFile(unsigned int& nFileRet); bool AddKey(const CKey& key); -vector GenerateNewKey(); +std::vector GenerateNewKey(); bool AddToWallet(const CWalletTx& wtxIn); void WalletUpdateSpent(const COutPoint& prevout); int ScanForWalletTransactions(CBlockIndex* pindexStart); @@ -87,15 +91,15 @@ void ReacceptWalletTransactions(); bool LoadBlockIndex(bool fAllowNew=true); void PrintBlockTree(); bool ProcessMessages(CNode* pfrom); -bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv); +bool ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vRecv); bool SendMessages(CNode* pto, bool fSendTrickle); int64 GetBalance(); -bool CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); +bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); bool BroadcastTransaction(CWalletTx& wtxNew); -string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); -string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); +std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); +std::string SendMoneyToBitcoinAddress(std::string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); void GenerateBitcoins(bool fGenerate); void ThreadBitcoinMiner(void* parg); CBlock* CreateNewBlock(CReserveKey& reservekey); @@ -105,7 +109,7 @@ bool CheckWork(CBlock* pblock, CReserveKey& reservekey); void BitcoinMiner(); bool CheckProofOfWork(uint256 hash, unsigned int nBits); bool IsInitialBlockDownload(); -string GetWarnings(string strFor); +std::string GetWarnings(std::string strFor); @@ -153,7 +157,7 @@ public: return !(a == b); } - string ToString() const + std::string ToString() const { if (IsNull()) return strprintf("null"); @@ -212,7 +216,7 @@ public: return !(a == b); } - string ToString() const + std::string ToString() const { return strprintf("COutPoint(%s, %d)", hash.ToString().substr(0,10).c_str(), n); } @@ -281,9 +285,9 @@ public: return !(a == b); } - string ToString() const + std::string ToString() const { - string str; + std::string str; str += strprintf("CTxIn("); str += prevout.ToString(); if (prevout.IsNull()) @@ -359,14 +363,14 @@ public: int64 GetCredit() const { if (!MoneyRange(nValue)) - throw runtime_error("CTxOut::GetCredit() : value out of range"); + throw std::runtime_error("CTxOut::GetCredit() : value out of range"); return (IsMine() ? nValue : 0); } bool IsChange() const { // On a debit transaction, a txout that's mine but isn't in the address book is change - vector vchPubKey; + std::vector vchPubKey; if (ExtractPubKey(scriptPubKey, true, vchPubKey)) CRITICAL_BLOCK(cs_mapAddressBook) if (!mapAddressBook.count(PubKeyToAddress(vchPubKey))) @@ -377,7 +381,7 @@ public: int64 GetChange() const { if (!MoneyRange(nValue)) - throw runtime_error("CTxOut::GetChange() : value out of range"); + throw std::runtime_error("CTxOut::GetChange() : value out of range"); return (IsChange() ? nValue : 0); } @@ -392,7 +396,7 @@ public: return !(a == b); } - string ToString() const + std::string ToString() const { if (scriptPubKey.size() < 6) return "CTxOut(error)"; @@ -416,8 +420,8 @@ class CTransaction { public: int nVersion; - vector vin; - vector vout; + std::vector vin; + std::vector vout; unsigned int nLockTime; @@ -464,7 +468,7 @@ public: nBlockTime = GetAdjustedTime(); if ((int64)nLockTime < (nLockTime < 500000000 ? (int64)nBlockHeight : nBlockTime)) return true; - foreach(const CTxIn& txin, vin) + BOOST_FOREACH(const CTxIn& txin, vin) if (!txin.IsFinal()) return false; return true; @@ -507,19 +511,19 @@ public: int GetSigOpCount() const { int n = 0; - foreach(const CTxIn& txin, vin) + BOOST_FOREACH(const CTxIn& txin, vin) n += txin.scriptSig.GetSigOpCount(); - foreach(const CTxOut& txout, vout) + BOOST_FOREACH(const CTxOut& txout, vout) n += txout.scriptPubKey.GetSigOpCount(); return n; } bool IsStandard() const { - foreach(const CTxIn& txin, vin) + BOOST_FOREACH(const CTxIn& txin, vin) if (!txin.scriptSig.IsPushOnly()) return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str()); - foreach(const CTxOut& txout, vout) + BOOST_FOREACH(const CTxOut& txout, vout) if (!::IsStandard(txout.scriptPubKey)) return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str()); return true; @@ -527,7 +531,7 @@ public: bool IsMine() const { - foreach(const CTxOut& txout, vout) + BOOST_FOREACH(const CTxOut& txout, vout) if (txout.IsMine()) return true; return false; @@ -541,11 +545,11 @@ public: int64 GetDebit() const { int64 nDebit = 0; - foreach(const CTxIn& txin, vin) + BOOST_FOREACH(const CTxIn& txin, vin) { nDebit += txin.GetDebit(); if (!MoneyRange(nDebit)) - throw runtime_error("CTransaction::GetDebit() : value out of range"); + throw std::runtime_error("CTransaction::GetDebit() : value out of range"); } return nDebit; } @@ -553,11 +557,11 @@ public: int64 GetCredit() const { int64 nCredit = 0; - foreach(const CTxOut& txout, vout) + BOOST_FOREACH(const CTxOut& txout, vout) { nCredit += txout.GetCredit(); if (!MoneyRange(nCredit)) - throw runtime_error("CTransaction::GetCredit() : value out of range"); + throw std::runtime_error("CTransaction::GetCredit() : value out of range"); } return nCredit; } @@ -567,11 +571,11 @@ public: if (IsCoinBase()) return 0; int64 nChange = 0; - foreach(const CTxOut& txout, vout) + BOOST_FOREACH(const CTxOut& txout, vout) { nChange += txout.GetChange(); if (!MoneyRange(nChange)) - throw runtime_error("CTransaction::GetChange() : value out of range"); + throw std::runtime_error("CTransaction::GetChange() : value out of range"); } return nChange; } @@ -579,11 +583,11 @@ public: int64 GetValueOut() const { int64 nValueOut = 0; - foreach(const CTxOut& txout, vout) + BOOST_FOREACH(const CTxOut& txout, vout) { nValueOut += txout.nValue; if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) - throw runtime_error("CTransaction::GetValueOut() : value out of range"); + throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); } return nValueOut; } @@ -621,7 +625,7 @@ public: // To limit dust spam, require MIN_TX_FEE if any output is less than 0.01 if (nMinFee < MIN_TX_FEE) - foreach(const CTxOut& txout, vout) + BOOST_FOREACH(const CTxOut& txout, vout) if (txout.nValue < CENT) nMinFee = MIN_TX_FEE; @@ -674,9 +678,9 @@ public: } - string ToString() const + std::string ToString() const { - string str; + std::string str; str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n", GetHash().ToString().substr(0,10).c_str(), nVersion, @@ -700,7 +704,7 @@ public: bool ReadFromDisk(CTxDB& txdb, COutPoint prevout); bool ReadFromDisk(COutPoint prevout); bool DisconnectInputs(CTxDB& txdb); - bool ConnectInputs(CTxDB& txdb, map& mapTestPool, CDiskTxPos posThisTx, + bool ConnectInputs(CTxDB& txdb, std::map& mapTestPool, CDiskTxPos posThisTx, CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0); bool ClientConnectInputs(); bool CheckTransaction() const; @@ -727,7 +731,7 @@ class CMerkleTx : public CTransaction { public: uint256 hashBlock; - vector vMerkleBranch; + std::vector vMerkleBranch; int nIndex; // memory only @@ -782,14 +786,14 @@ public: class CWalletTx : public CMerkleTx { public: - vector vtxPrev; - map mapValue; - vector > vOrderForm; + std::vector vtxPrev; + std::map mapValue; + std::vector > vOrderForm; unsigned int fTimeReceivedIsTxTime; unsigned int nTimeReceived; // time received by this node char fFromMe; - string strFromAccount; - vector vfSpent; + std::string strFromAccount; + std::vector vfSpent; // memory only mutable char fDebitCached; @@ -856,8 +860,8 @@ public: { pthis->mapValue["fromaccount"] = pthis->strFromAccount; - string str; - foreach(char f, vfSpent) + std::string str; + BOOST_FOREACH(char f, vfSpent) { str += (f ? '1' : '0'); if (f) @@ -880,7 +884,7 @@ public: pthis->strFromAccount = pthis->mapValue["fromaccount"]; if (mapValue.count("spent")) - foreach(char c, pthis->mapValue["spent"]) + BOOST_FOREACH(char c, pthis->mapValue["spent"]) pthis->vfSpent.push_back(c != '0'); else pthis->vfSpent.assign(vout.size(), fSpent); @@ -893,7 +897,7 @@ public: // marks certain txout's as spent // returns true if any update took place - bool UpdateSpent(const vector& vfNewSpent) + bool UpdateSpent(const std::vector& vfNewSpent) { bool fReturn = false; for (int i=0; i < vfNewSpent.size(); i++) @@ -922,7 +926,7 @@ public: void MarkSpent(unsigned int nOut) { if (nOut >= vout.size()) - throw runtime_error("CWalletTx::MarkSpent() : nOut out of range"); + throw std::runtime_error("CWalletTx::MarkSpent() : nOut out of range"); vfSpent.resize(vout.size()); if (!vfSpent[nOut]) { @@ -934,7 +938,7 @@ public: bool IsSpent(unsigned int nOut) const { if (nOut >= vout.size()) - throw runtime_error("CWalletTx::IsSpent() : nOut out of range"); + throw std::runtime_error("CWalletTx::IsSpent() : nOut out of range"); if (nOut >= vfSpent.size()) return false; return (!!vfSpent[nOut]); @@ -982,7 +986,7 @@ public: const CTxOut &txout = vout[i]; nCredit += txout.GetCredit(); if (!MoneyRange(nCredit)) - throw runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); } } @@ -1001,10 +1005,10 @@ public: return nChangeCached; } - void GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list >& listReceived, - list >& listSent, int64& nFee, string& strSentAccount) const; + void GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, std::list >& listReceived, + std::list >& listSent, int64& nFee, std::string& strSentAccount) const; - void GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, + void GetAccountAmounts(const std::string& strAccount, int64& nGenerated, int64& nReceived, int64& nSent, int64& nFee) const; bool IsFromMe() const @@ -1024,8 +1028,8 @@ public: // If no confirmations but it's from us, we can still // consider it confirmed if all dependencies are confirmed - map mapPrev; - vector vWorkQueue; + std::map mapPrev; + std::vector vWorkQueue; vWorkQueue.reserve(vtxPrev.size()+1); vWorkQueue.push_back(this); for (int i = 0; i < vWorkQueue.size(); i++) @@ -1040,10 +1044,10 @@ public: return false; if (mapPrev.empty()) - foreach(const CMerkleTx& tx, vtxPrev) + BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) mapPrev[tx.GetHash()] = &tx; - foreach(const CTxIn& txin, ptx->vin) + BOOST_FOREACH(const CTxIn& txin, ptx->vin) { if (!mapPrev.count(txin.prevout.hash)) return false; @@ -1083,7 +1087,7 @@ class CTxIndex { public: CDiskTxPos pos; - vector vSpent; + std::vector vSpent; CTxIndex() { @@ -1155,10 +1159,10 @@ public: unsigned int nNonce; // network and disk - vector vtx; + std::vector vtx; // memory only - mutable vector vMerkleTree; + mutable std::vector vMerkleTree; CBlock() @@ -1213,7 +1217,7 @@ public: int GetSigOpCount() const { int n = 0; - foreach(const CTransaction& tx, vtx) + BOOST_FOREACH(const CTransaction& tx, vtx) n += tx.GetSigOpCount(); return n; } @@ -1222,14 +1226,14 @@ public: uint256 BuildMerkleTree() const { vMerkleTree.clear(); - foreach(const CTransaction& tx, vtx) + BOOST_FOREACH(const CTransaction& tx, vtx) vMerkleTree.push_back(tx.GetHash()); int j = 0; for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) { for (int i = 0; i < nSize; i += 2) { - int i2 = min(i+1, nSize-1); + int i2 = std::min(i+1, nSize-1); vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); } @@ -1238,15 +1242,15 @@ public: return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); } - vector GetMerkleBranch(int nIndex) const + std::vector GetMerkleBranch(int nIndex) const { if (vMerkleTree.empty()) BuildMerkleTree(); - vector vMerkleBranch; + std::vector vMerkleBranch; int j = 0; for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) { - int i = min(nIndex^1, nSize-1); + int i = std::min(nIndex^1, nSize-1); vMerkleBranch.push_back(vMerkleTree[j+i]); nIndex >>= 1; j += nSize; @@ -1254,11 +1258,11 @@ public: return vMerkleBranch; } - static uint256 CheckMerkleBranch(uint256 hash, const vector& vMerkleBranch, int nIndex) + static uint256 CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) { if (nIndex == -1) return 0; - foreach(const uint256& otherside, vMerkleBranch) + BOOST_FOREACH(const uint256& otherside, vMerkleBranch) { if (nIndex & 1) hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); @@ -1489,7 +1493,7 @@ public: for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev) *(--pbegin) = pindex->GetBlockTime(); - sort(pbegin, pend); + std::sort(pbegin, pend); return pbegin[(pend - pbegin)/2]; } @@ -1507,7 +1511,7 @@ public: - string ToString() const + std::string ToString() const { return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, merkle=%s, hashBlock=%s)", pprev, pnext, nFile, nBlockPos, nHeight, @@ -1576,9 +1580,9 @@ public: } - string ToString() const + std::string ToString() const { - string str = "CDiskBlockIndex("; + std::string str = "CDiskBlockIndex("; str += CBlockIndex::ToString(); str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)", GetBlockHash().ToString().c_str(), @@ -1608,7 +1612,7 @@ public: class CBlockLocator { protected: - vector vHave; + std::vector vHave; public: CBlockLocator() @@ -1622,7 +1626,7 @@ public: explicit CBlockLocator(uint256 hashBlock) { - map::iterator mi = mapBlockIndex.find(hashBlock); + std::map::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end()) Set((*mi).second); } @@ -1666,9 +1670,9 @@ public: // Retrace how far back it was in the sender's branch int nDistance = 0; int nStep = 1; - foreach(const uint256& hash, vHave) + BOOST_FOREACH(const uint256& hash, vHave) { - map::iterator mi = mapBlockIndex.find(hash); + std::map::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { CBlockIndex* pindex = (*mi).second; @@ -1685,9 +1689,9 @@ public: CBlockIndex* GetBlockIndex() { // Find the first block the caller has in the main chain - foreach(const uint256& hash, vHave) + BOOST_FOREACH(const uint256& hash, vHave) { - map::iterator mi = mapBlockIndex.find(hash); + std::map::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { CBlockIndex* pindex = (*mi).second; @@ -1701,9 +1705,9 @@ public: uint256 GetBlockHash() { // Find the first block the caller has in the main chain - foreach(const uint256& hash, vHave) + BOOST_FOREACH(const uint256& hash, vHave) { - map::iterator mi = mapBlockIndex.find(hash); + std::map::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { CBlockIndex* pindex = (*mi).second; @@ -1737,7 +1741,7 @@ public: CPrivKey vchPrivKey; int64 nTimeCreated; int64 nTimeExpires; - string strComment; + std::string strComment; //// todo: add something to note what created it (user, getnewaddress, change) //// maybe should have a map property map @@ -1770,7 +1774,7 @@ public: class CAccount { public: - vector vchPubKey; + std::vector vchPubKey; CAccount() { @@ -1799,11 +1803,11 @@ public: class CAccountingEntry { public: - string strAccount; + std::string strAccount; int64 nCreditDebit; int64 nTime; - string strOtherAccount; - string strComment; + std::string strOtherAccount; + std::string strComment; CAccountingEntry() { @@ -1854,16 +1858,16 @@ public: int64 nExpiration; int nID; int nCancel; - set setCancel; + std::set setCancel; int nMinVer; // lowest version inclusive int nMaxVer; // highest version inclusive - set setSubVer; // empty matches all + std::set setSubVer; // empty matches all int nPriority; // Actions - string strComment; - string strStatusBar; - string strReserved; + std::string strComment; + std::string strStatusBar; + std::string strReserved; IMPLEMENT_SERIALIZE ( @@ -1902,13 +1906,13 @@ public: strReserved.clear(); } - string ToString() const + std::string ToString() const { - string strSetCancel; - foreach(int n, setCancel) + std::string strSetCancel; + BOOST_FOREACH(int n, setCancel) strSetCancel += strprintf("%d ", n); - string strSetSubVer; - foreach(string str, setSubVer) + std::string strSetSubVer; + BOOST_FOREACH(std::string str, setSubVer) strSetSubVer += "\"" + str + "\" "; return strprintf( "CAlert(\n" @@ -1948,8 +1952,8 @@ public: class CAlert : public CUnsignedAlert { public: - vector vchMsg; - vector vchSig; + std::vector vchMsg; + std::vector vchSig; CAlert() { @@ -1991,7 +1995,7 @@ public: return (alert.nID <= nCancel || setCancel.count(alert.nID)); } - bool AppliesTo(int nVersion, string strSubVerIn) const + bool AppliesTo(int nVersion, std::string strSubVerIn) const { return (IsInEffect() && nMinVer <= nVersion && nVersion <= nMaxVer && @@ -2047,12 +2051,12 @@ public: -extern map mapTransactions; -extern map mapWallet; -extern vector vWalletUpdated; +extern std::map mapTransactions; +extern std::map mapWallet; +extern std::vector vWalletUpdated; extern CCriticalSection cs_mapWallet; -extern map, CPrivKey> mapKeys; -extern map > mapPubKeys; +extern std::map, CPrivKey> mapKeys; +extern std::map > mapPubKeys; extern CCriticalSection cs_mapKeys; extern CKey keyUser; diff --git a/core/include/noui.h b/core/include/noui.h new file mode 100644 index 0000000000..afb19526c1 --- /dev/null +++ b/core/include/noui.h @@ -0,0 +1,67 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_NOUI_H +#define BITCOIN_NOUI_H + +#include + +typedef void wxWindow; +#define wxYES 0x00000002 +#define wxOK 0x00000004 +#define wxNO 0x00000008 +#define wxYES_NO (wxYES|wxNO) +#define wxCANCEL 0x00000010 +#define wxAPPLY 0x00000020 +#define wxCLOSE 0x00000040 +#define wxOK_DEFAULT 0x00000000 +#define wxYES_DEFAULT 0x00000000 +#define wxNO_DEFAULT 0x00000080 +#define wxCANCEL_DEFAULT 0x80000000 +#define wxICON_EXCLAMATION 0x00000100 +#define wxICON_HAND 0x00000200 +#define wxICON_WARNING wxICON_EXCLAMATION +#define wxICON_ERROR wxICON_HAND +#define wxICON_QUESTION 0x00000400 +#define wxICON_INFORMATION 0x00000800 +#define wxICON_STOP wxICON_HAND +#define wxICON_ASTERISK wxICON_INFORMATION +#define wxICON_MASK (0x00000100|0x00000200|0x00000400|0x00000800) +#define wxFORWARD 0x00001000 +#define wxBACKWARD 0x00002000 +#define wxRESET 0x00004000 +#define wxHELP 0x00008000 +#define wxMORE 0x00010000 +#define wxSETUP 0x00020000 + +inline int MyMessageBox(const std::string& message, const std::string& caption="Message", int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1) +{ + printf("%s: %s\n", caption.c_str(), message.c_str()); + fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); + return 4; +} +#define wxMessageBox MyMessageBox + +inline int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1) +{ + return MyMessageBox(message, caption, style, parent, x, y); +} + +inline bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent) +{ + return true; +} + +inline void CalledSetStatusBar(const std::string& strText, int nField) +{ +} + +inline void UIThreadCall(boost::function0 fn) +{ +} + +inline void MainFrameRepaint() +{ +} + +#endif diff --git a/core/include/rpc.h b/core/include/rpc.h new file mode 100644 index 0000000000..48a7b8a8a6 --- /dev/null +++ b/core/include/rpc.h @@ -0,0 +1,6 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +void ThreadRPCServer(void* parg); +int CommandLineRPC(int argc, char *argv[]); diff --git a/core/include/script.h b/core/include/script.h new file mode 100644 index 0000000000..22a6020dce --- /dev/null +++ b/core/include/script.h @@ -0,0 +1,718 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef H_BITCOIN_SCRIPT +#define H_BITCOIN_SCRIPT + +#include "base58.h" + +#include +#include + +class CTransaction; + +enum +{ + SIGHASH_ALL = 1, + SIGHASH_NONE = 2, + SIGHASH_SINGLE = 3, + SIGHASH_ANYONECANPAY = 0x80, +}; + + + +enum opcodetype +{ + // push value + OP_0=0, + OP_FALSE=OP_0, + OP_PUSHDATA1=76, + OP_PUSHDATA2, + OP_PUSHDATA4, + OP_1NEGATE, + OP_RESERVED, + OP_1, + OP_TRUE=OP_1, + OP_2, + OP_3, + OP_4, + OP_5, + OP_6, + OP_7, + OP_8, + OP_9, + OP_10, + OP_11, + OP_12, + OP_13, + OP_14, + OP_15, + OP_16, + + // control + OP_NOP, + OP_VER, + OP_IF, + OP_NOTIF, + OP_VERIF, + OP_VERNOTIF, + OP_ELSE, + OP_ENDIF, + OP_VERIFY, + OP_RETURN, + + // stack ops + OP_TOALTSTACK, + OP_FROMALTSTACK, + OP_2DROP, + OP_2DUP, + OP_3DUP, + OP_2OVER, + OP_2ROT, + OP_2SWAP, + OP_IFDUP, + OP_DEPTH, + OP_DROP, + OP_DUP, + OP_NIP, + OP_OVER, + OP_PICK, + OP_ROLL, + OP_ROT, + OP_SWAP, + OP_TUCK, + + // splice ops + OP_CAT, + OP_SUBSTR, + OP_LEFT, + OP_RIGHT, + OP_SIZE, + + // bit logic + OP_INVERT, + OP_AND, + OP_OR, + OP_XOR, + OP_EQUAL, + OP_EQUALVERIFY, + OP_RESERVED1, + OP_RESERVED2, + + // numeric + OP_1ADD, + OP_1SUB, + OP_2MUL, + OP_2DIV, + OP_NEGATE, + OP_ABS, + OP_NOT, + OP_0NOTEQUAL, + + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_MOD, + OP_LSHIFT, + OP_RSHIFT, + + OP_BOOLAND, + OP_BOOLOR, + OP_NUMEQUAL, + OP_NUMEQUALVERIFY, + OP_NUMNOTEQUAL, + OP_LESSTHAN, + OP_GREATERTHAN, + OP_LESSTHANOREQUAL, + OP_GREATERTHANOREQUAL, + OP_MIN, + OP_MAX, + + OP_WITHIN, + + // crypto + OP_RIPEMD160, + OP_SHA1, + OP_SHA256, + OP_HASH160, + OP_HASH256, + OP_CODESEPARATOR, + OP_CHECKSIG, + OP_CHECKSIGVERIFY, + OP_CHECKMULTISIG, + OP_CHECKMULTISIGVERIFY, + + // expansion + OP_NOP1, + OP_NOP2, + OP_NOP3, + OP_NOP4, + OP_NOP5, + OP_NOP6, + OP_NOP7, + OP_NOP8, + OP_NOP9, + OP_NOP10, + + + + // template matching params + OP_PUBKEYHASH = 0xfd, + OP_PUBKEY = 0xfe, + + OP_INVALIDOPCODE = 0xff, +}; + + + + + + + + +inline const char* GetOpName(opcodetype opcode) +{ + switch (opcode) + { + // push value + case OP_0 : return "0"; + case OP_PUSHDATA1 : return "OP_PUSHDATA1"; + case OP_PUSHDATA2 : return "OP_PUSHDATA2"; + case OP_PUSHDATA4 : return "OP_PUSHDATA4"; + case OP_1NEGATE : return "-1"; + case OP_RESERVED : return "OP_RESERVED"; + case OP_1 : return "1"; + case OP_2 : return "2"; + case OP_3 : return "3"; + case OP_4 : return "4"; + case OP_5 : return "5"; + case OP_6 : return "6"; + case OP_7 : return "7"; + case OP_8 : return "8"; + case OP_9 : return "9"; + case OP_10 : return "10"; + case OP_11 : return "11"; + case OP_12 : return "12"; + case OP_13 : return "13"; + case OP_14 : return "14"; + case OP_15 : return "15"; + case OP_16 : return "16"; + + // control + case OP_NOP : return "OP_NOP"; + case OP_VER : return "OP_VER"; + case OP_IF : return "OP_IF"; + case OP_NOTIF : return "OP_NOTIF"; + case OP_VERIF : return "OP_VERIF"; + case OP_VERNOTIF : return "OP_VERNOTIF"; + case OP_ELSE : return "OP_ELSE"; + case OP_ENDIF : return "OP_ENDIF"; + case OP_VERIFY : return "OP_VERIFY"; + case OP_RETURN : return "OP_RETURN"; + + // stack ops + case OP_TOALTSTACK : return "OP_TOALTSTACK"; + case OP_FROMALTSTACK : return "OP_FROMALTSTACK"; + case OP_2DROP : return "OP_2DROP"; + case OP_2DUP : return "OP_2DUP"; + case OP_3DUP : return "OP_3DUP"; + case OP_2OVER : return "OP_2OVER"; + case OP_2ROT : return "OP_2ROT"; + case OP_2SWAP : return "OP_2SWAP"; + case OP_IFDUP : return "OP_IFDUP"; + case OP_DEPTH : return "OP_DEPTH"; + case OP_DROP : return "OP_DROP"; + case OP_DUP : return "OP_DUP"; + case OP_NIP : return "OP_NIP"; + case OP_OVER : return "OP_OVER"; + case OP_PICK : return "OP_PICK"; + case OP_ROLL : return "OP_ROLL"; + case OP_ROT : return "OP_ROT"; + case OP_SWAP : return "OP_SWAP"; + case OP_TUCK : return "OP_TUCK"; + + // splice ops + case OP_CAT : return "OP_CAT"; + case OP_SUBSTR : return "OP_SUBSTR"; + case OP_LEFT : return "OP_LEFT"; + case OP_RIGHT : return "OP_RIGHT"; + case OP_SIZE : return "OP_SIZE"; + + // bit logic + case OP_INVERT : return "OP_INVERT"; + case OP_AND : return "OP_AND"; + case OP_OR : return "OP_OR"; + case OP_XOR : return "OP_XOR"; + case OP_EQUAL : return "OP_EQUAL"; + case OP_EQUALVERIFY : return "OP_EQUALVERIFY"; + case OP_RESERVED1 : return "OP_RESERVED1"; + case OP_RESERVED2 : return "OP_RESERVED2"; + + // numeric + case OP_1ADD : return "OP_1ADD"; + case OP_1SUB : return "OP_1SUB"; + case OP_2MUL : return "OP_2MUL"; + case OP_2DIV : return "OP_2DIV"; + case OP_NEGATE : return "OP_NEGATE"; + case OP_ABS : return "OP_ABS"; + case OP_NOT : return "OP_NOT"; + case OP_0NOTEQUAL : return "OP_0NOTEQUAL"; + case OP_ADD : return "OP_ADD"; + case OP_SUB : return "OP_SUB"; + case OP_MUL : return "OP_MUL"; + case OP_DIV : return "OP_DIV"; + case OP_MOD : return "OP_MOD"; + case OP_LSHIFT : return "OP_LSHIFT"; + case OP_RSHIFT : return "OP_RSHIFT"; + case OP_BOOLAND : return "OP_BOOLAND"; + case OP_BOOLOR : return "OP_BOOLOR"; + case OP_NUMEQUAL : return "OP_NUMEQUAL"; + case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY"; + case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL"; + case OP_LESSTHAN : return "OP_LESSTHAN"; + case OP_GREATERTHAN : return "OP_GREATERTHAN"; + case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL"; + case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL"; + case OP_MIN : return "OP_MIN"; + case OP_MAX : return "OP_MAX"; + case OP_WITHIN : return "OP_WITHIN"; + + // crypto + case OP_RIPEMD160 : return "OP_RIPEMD160"; + case OP_SHA1 : return "OP_SHA1"; + case OP_SHA256 : return "OP_SHA256"; + case OP_HASH160 : return "OP_HASH160"; + case OP_HASH256 : return "OP_HASH256"; + case OP_CODESEPARATOR : return "OP_CODESEPARATOR"; + case OP_CHECKSIG : return "OP_CHECKSIG"; + case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; + case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; + case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; + + // expanson + case OP_NOP1 : return "OP_NOP1"; + case OP_NOP2 : return "OP_NOP2"; + case OP_NOP3 : return "OP_NOP3"; + case OP_NOP4 : return "OP_NOP4"; + case OP_NOP5 : return "OP_NOP5"; + case OP_NOP6 : return "OP_NOP6"; + case OP_NOP7 : return "OP_NOP7"; + case OP_NOP8 : return "OP_NOP8"; + case OP_NOP9 : return "OP_NOP9"; + case OP_NOP10 : return "OP_NOP10"; + + + + // template matching params + case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; + case OP_PUBKEY : return "OP_PUBKEY"; + + case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; + default: + return "OP_UNKNOWN"; + } +}; + + + + +inline std::string ValueString(const std::vector& vch) +{ + if (vch.size() <= 4) + return strprintf("%d", CBigNum(vch).getint()); + else + return HexStr(vch); +} + +inline std::string StackString(const std::vector >& vStack) +{ + std::string str; + BOOST_FOREACH(const std::vector& vch, vStack) + { + if (!str.empty()) + str += " "; + str += ValueString(vch); + } + return str; +} + + + + + + + + + +class CScript : public std::vector +{ +protected: + CScript& push_int64(int64 n) + { + if (n == -1 || (n >= 1 && n <= 16)) + { + push_back(n + (OP_1 - 1)); + } + else + { + CBigNum bn(n); + *this << bn.getvch(); + } + return *this; + } + + CScript& push_uint64(uint64 n) + { + if (n >= 1 && n <= 16) + { + push_back(n + (OP_1 - 1)); + } + else + { + CBigNum bn(n); + *this << bn.getvch(); + } + return *this; + } + +public: + CScript() { } + CScript(const CScript& b) : std::vector(b.begin(), b.end()) { } + CScript(const_iterator pbegin, const_iterator pend) : std::vector(pbegin, pend) { } +#ifndef _MSC_VER + CScript(const unsigned char* pbegin, const unsigned char* pend) : std::vector(pbegin, pend) { } +#endif + + CScript& operator+=(const CScript& b) + { + insert(end(), b.begin(), b.end()); + return *this; + } + + friend CScript operator+(const CScript& a, const CScript& b) + { + CScript ret = a; + ret += b; + return ret; + } + + + explicit CScript(char b) { operator<<(b); } + explicit CScript(short b) { operator<<(b); } + explicit CScript(int b) { operator<<(b); } + explicit CScript(long b) { operator<<(b); } + explicit CScript(int64 b) { operator<<(b); } + explicit CScript(unsigned char b) { operator<<(b); } + explicit CScript(unsigned int b) { operator<<(b); } + explicit CScript(unsigned short b) { operator<<(b); } + explicit CScript(unsigned long b) { operator<<(b); } + explicit CScript(uint64 b) { operator<<(b); } + + explicit CScript(opcodetype b) { operator<<(b); } + explicit CScript(const uint256& b) { operator<<(b); } + explicit CScript(const CBigNum& b) { operator<<(b); } + explicit CScript(const std::vector& b) { operator<<(b); } + + + CScript& operator<<(char b) { return push_int64(b); } + CScript& operator<<(short b) { return push_int64(b); } + CScript& operator<<(int b) { return push_int64(b); } + CScript& operator<<(long b) { return push_int64(b); } + CScript& operator<<(int64 b) { return push_int64(b); } + CScript& operator<<(unsigned char b) { return push_uint64(b); } + CScript& operator<<(unsigned int b) { return push_uint64(b); } + CScript& operator<<(unsigned short b) { return push_uint64(b); } + CScript& operator<<(unsigned long b) { return push_uint64(b); } + CScript& operator<<(uint64 b) { return push_uint64(b); } + + CScript& operator<<(opcodetype opcode) + { + if (opcode < 0 || opcode > 0xff) + throw std::runtime_error("CScript::operator<<() : invalid opcode"); + insert(end(), (unsigned char)opcode); + return *this; + } + + CScript& operator<<(const uint160& b) + { + insert(end(), sizeof(b)); + insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b)); + return *this; + } + + CScript& operator<<(const uint256& b) + { + insert(end(), sizeof(b)); + insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b)); + return *this; + } + + CScript& operator<<(const CBigNum& b) + { + *this << b.getvch(); + return *this; + } + + CScript& operator<<(const std::vector& b) + { + if (b.size() < OP_PUSHDATA1) + { + insert(end(), (unsigned char)b.size()); + } + else if (b.size() <= 0xff) + { + insert(end(), OP_PUSHDATA1); + insert(end(), (unsigned char)b.size()); + } + else if (b.size() <= 0xffff) + { + insert(end(), OP_PUSHDATA2); + unsigned short nSize = b.size(); + insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize)); + } + else + { + insert(end(), OP_PUSHDATA4); + unsigned int nSize = b.size(); + insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize)); + } + insert(end(), b.begin(), b.end()); + return *this; + } + + CScript& operator<<(const CScript& b) + { + // I'm not sure if this should push the script or concatenate scripts. + // If there's ever a use for pushing a script onto a script, delete this member fn + assert(("warning: pushing a CScript onto a CScript with << is probably not intended, use + to concatenate", false)); + return *this; + } + + + bool GetOp(iterator& pc, opcodetype& opcodeRet, std::vector& vchRet) + { + // Wrapper so it can be called with either iterator or const_iterator + const_iterator pc2 = pc; + bool fRet = GetOp2(pc2, opcodeRet, &vchRet); + pc = begin() + (pc2 - begin()); + return fRet; + } + + bool GetOp(iterator& pc, opcodetype& opcodeRet) + { + const_iterator pc2 = pc; + bool fRet = GetOp2(pc2, opcodeRet, NULL); + pc = begin() + (pc2 - begin()); + return fRet; + } + + bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector& vchRet) const + { + return GetOp2(pc, opcodeRet, &vchRet); + } + + bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const + { + return GetOp2(pc, opcodeRet, NULL); + } + + bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector* pvchRet) const + { + opcodeRet = OP_INVALIDOPCODE; + if (pvchRet) + pvchRet->clear(); + if (pc >= end()) + return false; + + // Read instruction + if (end() - pc < 1) + return false; + unsigned int opcode = *pc++; + + // Immediate operand + if (opcode <= OP_PUSHDATA4) + { + unsigned int nSize; + if (opcode < OP_PUSHDATA1) + { + nSize = opcode; + } + else if (opcode == OP_PUSHDATA1) + { + if (end() - pc < 1) + return false; + nSize = *pc++; + } + else if (opcode == OP_PUSHDATA2) + { + if (end() - pc < 2) + return false; + nSize = 0; + memcpy(&nSize, &pc[0], 2); + pc += 2; + } + else if (opcode == OP_PUSHDATA4) + { + if (end() - pc < 4) + return false; + memcpy(&nSize, &pc[0], 4); + pc += 4; + } + if (end() - pc < nSize) + return false; + if (pvchRet) + pvchRet->assign(pc, pc + nSize); + pc += nSize; + } + + opcodeRet = (opcodetype)opcode; + return true; + } + + + void FindAndDelete(const CScript& b) + { + if (b.empty()) + return; + iterator pc = begin(); + opcodetype opcode; + do + { + while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0) + erase(pc, pc + b.size()); + } + while (GetOp(pc, opcode)); + } + + + int GetSigOpCount() const + { + int n = 0; + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + break; + if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY) + n++; + else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY) + n += 20; + } + return n; + } + + + bool IsPushOnly() const + { + if (size() > 200) + return false; + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + return false; + if (opcode > OP_16) + return false; + } + return true; + } + + + uint160 GetBitcoinAddressHash160() const + { + opcodetype opcode; + std::vector vch; + CScript::const_iterator pc = begin(); + if (!GetOp(pc, opcode, vch) || opcode != OP_DUP) return 0; + if (!GetOp(pc, opcode, vch) || opcode != OP_HASH160) return 0; + if (!GetOp(pc, opcode, vch) || vch.size() != sizeof(uint160)) return 0; + uint160 hash160 = uint160(vch); + if (!GetOp(pc, opcode, vch) || opcode != OP_EQUALVERIFY) return 0; + if (!GetOp(pc, opcode, vch) || opcode != OP_CHECKSIG) return 0; + if (pc != end()) return 0; + return hash160; + } + + std::string GetBitcoinAddress() const + { + uint160 hash160 = GetBitcoinAddressHash160(); + if (hash160 == 0) + return ""; + return Hash160ToAddress(hash160); + } + + void SetBitcoinAddress(const uint160& hash160) + { + this->clear(); + *this << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; + } + + void SetBitcoinAddress(const std::vector& vchPubKey) + { + SetBitcoinAddress(Hash160(vchPubKey)); + } + + bool SetBitcoinAddress(const std::string& strAddress) + { + this->clear(); + uint160 hash160; + if (!AddressToHash160(strAddress, hash160)) + return false; + SetBitcoinAddress(hash160); + return true; + } + + + void PrintHex() const + { + printf("CScript(%s)\n", HexStr(begin(), end(), true).c_str()); + } + + std::string ToString() const + { + std::string str; + opcodetype opcode; + std::vector vch; + const_iterator pc = begin(); + while (pc < end()) + { + if (!str.empty()) + str += " "; + if (!GetOp(pc, opcode, vch)) + { + str += "[error]"; + return str; + } + if (0 <= opcode && opcode <= OP_PUSHDATA4) + str += ValueString(vch); + else + str += GetOpName(opcode); + } + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + + + + + +uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +bool IsStandard(const CScript& scriptPubKey); +bool IsMine(const CScript& scriptPubKey); +bool ExtractPubKey(const CScript& scriptPubKey, bool fMineOnly, std::vector& vchPubKeyRet); +bool ExtractHash160(const CScript& scriptPubKey, uint160& hash160Ret); +bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript()); +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType=0); + +#endif diff --git a/core/src/db.cpp b/core/src/db.cpp new file mode 100644 index 0000000000..52c0f5b4c3 --- /dev/null +++ b/core/src/db.cpp @@ -0,0 +1,1026 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" + +using namespace std; +using namespace boost; + +void ThreadFlushWalletDB(void* parg); + + +unsigned int nWalletDBUpdated; +uint64 nAccountingEntryNumber = 0; + + + +// +// CDB +// + +static CCriticalSection cs_db; +static bool fDbEnvInit = false; +DbEnv dbenv(0); +static map mapFileUseCount; +static map mapDb; + +class CDBInit +{ +public: + CDBInit() + { + } + ~CDBInit() + { + if (fDbEnvInit) + { + dbenv.close(0); + fDbEnvInit = false; + } + } +} +instance_of_cdbinit; + + +CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) +{ + int ret; + if (pszFile == NULL) + return; + + fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); + bool fCreate = strchr(pszMode, 'c'); + unsigned int nFlags = DB_THREAD; + if (fCreate) + nFlags |= DB_CREATE; + + CRITICAL_BLOCK(cs_db) + { + if (!fDbEnvInit) + { + if (fShutdown) + return; + string strDataDir = GetDataDir(); + string strLogDir = strDataDir + "/database"; + filesystem::create_directory(strLogDir.c_str()); + string strErrorFile = strDataDir + "/db.log"; + printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str()); + + dbenv.set_lg_dir(strLogDir.c_str()); + dbenv.set_lg_max(10000000); + dbenv.set_lk_max_locks(10000); + dbenv.set_lk_max_objects(10000); + dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug + dbenv.set_flags(DB_AUTO_COMMIT, 1); + ret = dbenv.open(strDataDir.c_str(), + DB_CREATE | + DB_INIT_LOCK | + DB_INIT_LOG | + DB_INIT_MPOOL | + DB_INIT_TXN | + DB_THREAD | + DB_RECOVER, + S_IRUSR | S_IWUSR); + if (ret > 0) + throw runtime_error(strprintf("CDB() : error %d opening database environment", ret)); + fDbEnvInit = true; + } + + strFile = pszFile; + ++mapFileUseCount[strFile]; + pdb = mapDb[strFile]; + if (pdb == NULL) + { + pdb = new Db(&dbenv, 0); + + ret = pdb->open(NULL, // Txn pointer + pszFile, // Filename + "main", // Logical db name + DB_BTREE, // Database type + nFlags, // Flags + 0); + + if (ret > 0) + { + delete pdb; + pdb = NULL; + CRITICAL_BLOCK(cs_db) + --mapFileUseCount[strFile]; + strFile = ""; + throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret)); + } + + if (fCreate && !Exists(string("version"))) + { + bool fTmp = fReadOnly; + fReadOnly = false; + WriteVersion(VERSION); + fReadOnly = fTmp; + } + + mapDb[strFile] = pdb; + } + } +} + +void CDB::Close() +{ + if (!pdb) + return; + if (!vTxn.empty()) + vTxn.front()->abort(); + vTxn.clear(); + pdb = NULL; + + // Flush database activity from memory pool to disk log + unsigned int nMinutes = 0; + if (fReadOnly) + nMinutes = 1; + if (strFile == "addr.dat") + nMinutes = 2; + if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 500 != 0) + nMinutes = 1; + dbenv.txn_checkpoint(0, nMinutes, 0); + + CRITICAL_BLOCK(cs_db) + --mapFileUseCount[strFile]; +} + +void CloseDb(const string& strFile) +{ + CRITICAL_BLOCK(cs_db) + { + if (mapDb[strFile] != NULL) + { + // Close the database handle + Db* pdb = mapDb[strFile]; + pdb->close(0); + delete pdb; + mapDb[strFile] = NULL; + } + } +} + +void DBFlush(bool fShutdown) +{ + // Flush log data to the actual data file + // on all files that are not in use + printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started"); + if (!fDbEnvInit) + return; + CRITICAL_BLOCK(cs_db) + { + map::iterator mi = mapFileUseCount.begin(); + while (mi != mapFileUseCount.end()) + { + string strFile = (*mi).first; + int nRefCount = (*mi).second; + printf("%s refcount=%d\n", strFile.c_str(), nRefCount); + if (nRefCount == 0) + { + // Move log data to the dat file + CloseDb(strFile); + dbenv.txn_checkpoint(0, 0, 0); + printf("%s flush\n", strFile.c_str()); + dbenv.lsn_reset(strFile.c_str(), 0); + mapFileUseCount.erase(mi++); + } + else + mi++; + } + if (fShutdown) + { + char** listp; + if (mapFileUseCount.empty()) + dbenv.log_archive(&listp, DB_ARCH_REMOVE); + dbenv.close(0); + fDbEnvInit = false; + } + } +} + + + + + + +// +// CTxDB +// + +bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex) +{ + assert(!fClient); + txindex.SetNull(); + return Read(make_pair(string("tx"), hash), txindex); +} + +bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex) +{ + assert(!fClient); + return Write(make_pair(string("tx"), hash), txindex); +} + +bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight) +{ + assert(!fClient); + + // Add to tx index + uint256 hash = tx.GetHash(); + CTxIndex txindex(pos, tx.vout.size()); + return Write(make_pair(string("tx"), hash), txindex); +} + +bool CTxDB::EraseTxIndex(const CTransaction& tx) +{ + assert(!fClient); + uint256 hash = tx.GetHash(); + + return Erase(make_pair(string("tx"), hash)); +} + +bool CTxDB::ContainsTx(uint256 hash) +{ + assert(!fClient); + return Exists(make_pair(string("tx"), hash)); +} + +bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector& vtx) +{ + assert(!fClient); + vtx.clear(); + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + unsigned int fFlags = DB_SET_RANGE; + loop + { + // Read next record + CDataStream ssKey; + if (fFlags == DB_SET_RANGE) + ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0); + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); + fFlags = DB_NEXT; + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + { + pcursor->close(); + return false; + } + + // Unserialize + string strType; + uint160 hashItem; + CDiskTxPos pos; + ssKey >> strType >> hashItem >> pos; + int nItemHeight; + ssValue >> nItemHeight; + + // Read transaction + if (strType != "owner" || hashItem != hash160) + break; + if (nItemHeight >= nMinHeight) + { + vtx.resize(vtx.size()+1); + if (!vtx.back().ReadFromDisk(pos)) + { + pcursor->close(); + return false; + } + } + } + + pcursor->close(); + return true; +} + +bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex) +{ + assert(!fClient); + tx.SetNull(); + if (!ReadTxIndex(hash, txindex)) + return false; + return (tx.ReadFromDisk(txindex.pos)); +} + +bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx) +{ + CTxIndex txindex; + return ReadDiskTx(hash, tx, txindex); +} + +bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex) +{ + return ReadDiskTx(outpoint.hash, tx, txindex); +} + +bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx) +{ + CTxIndex txindex; + return ReadDiskTx(outpoint.hash, tx, txindex); +} + +bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) +{ + return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex); +} + +bool CTxDB::EraseBlockIndex(uint256 hash) +{ + return Erase(make_pair(string("blockindex"), hash)); +} + +bool CTxDB::ReadHashBestChain(uint256& hashBestChain) +{ + return Read(string("hashBestChain"), hashBestChain); +} + +bool CTxDB::WriteHashBestChain(uint256 hashBestChain) +{ + return Write(string("hashBestChain"), hashBestChain); +} + +bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork) +{ + return Read(string("bnBestInvalidWork"), bnBestInvalidWork); +} + +bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork) +{ + return Write(string("bnBestInvalidWork"), bnBestInvalidWork); +} + +CBlockIndex* InsertBlockIndex(uint256 hash) +{ + if (hash == 0) + return NULL; + + // Return existing + map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + return (*mi).second; + + // Create new + CBlockIndex* pindexNew = new CBlockIndex(); + if (!pindexNew) + throw runtime_error("LoadBlockIndex() : new CBlockIndex failed"); + mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + pindexNew->phashBlock = &((*mi).first); + + return pindexNew; +} + +bool CTxDB::LoadBlockIndex() +{ + // Get database cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + // Load mapBlockIndex + unsigned int fFlags = DB_SET_RANGE; + loop + { + // Read next record + CDataStream ssKey; + if (fFlags == DB_SET_RANGE) + ssKey << make_pair(string("blockindex"), uint256(0)); + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); + fFlags = DB_NEXT; + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + return false; + + // Unserialize + string strType; + ssKey >> strType; + if (strType == "blockindex") + { + CDiskBlockIndex diskindex; + ssValue >> diskindex; + + // Construct block index object + CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); + pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); + pindexNew->pnext = InsertBlockIndex(diskindex.hashNext); + pindexNew->nFile = diskindex.nFile; + pindexNew->nBlockPos = diskindex.nBlockPos; + pindexNew->nHeight = diskindex.nHeight; + pindexNew->nVersion = diskindex.nVersion; + pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; + pindexNew->nTime = diskindex.nTime; + pindexNew->nBits = diskindex.nBits; + pindexNew->nNonce = diskindex.nNonce; + + // Watch for genesis block + if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock) + pindexGenesisBlock = pindexNew; + + if (!pindexNew->CheckIndex()) + return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight); + } + else + { + break; + } + } + pcursor->close(); + + // Calculate bnChainWork + vector > vSortedByHeight; + vSortedByHeight.reserve(mapBlockIndex.size()); + BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) + { + CBlockIndex* pindex = item.second; + vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); + } + sort(vSortedByHeight.begin(), vSortedByHeight.end()); + BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) + { + CBlockIndex* pindex = item.second; + pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork(); + } + + // Load hashBestChain pointer to end of best chain + if (!ReadHashBestChain(hashBestChain)) + { + if (pindexGenesisBlock == NULL) + return true; + return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded"); + } + if (!mapBlockIndex.count(hashBestChain)) + return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index"); + pindexBest = mapBlockIndex[hashBestChain]; + nBestHeight = pindexBest->nHeight; + bnBestChainWork = pindexBest->bnChainWork; + printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight); + + // Load bnBestInvalidWork, OK if it doesn't exist + ReadBestInvalidWork(bnBestInvalidWork); + + // Verify blocks in the best chain + CBlockIndex* pindexFork = NULL; + for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) + { + if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks")) + break; + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("LoadBlockIndex() : block.ReadFromDisk failed"); + if (!block.CheckBlock()) + { + printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + pindexFork = pindex->pprev; + } + } + if (pindexFork) + { + // Reorg back to the fork + printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight); + CBlock block; + if (!block.ReadFromDisk(pindexFork)) + return error("LoadBlockIndex() : block.ReadFromDisk failed"); + CTxDB txdb; + block.SetBestChain(txdb, pindexFork); + } + + return true; +} + + + + + +// +// CAddrDB +// + +bool CAddrDB::WriteAddress(const CAddress& addr) +{ + return Write(make_pair(string("addr"), addr.GetKey()), addr); +} + +bool CAddrDB::EraseAddress(const CAddress& addr) +{ + return Erase(make_pair(string("addr"), addr.GetKey())); +} + +bool CAddrDB::LoadAddresses() +{ + CRITICAL_BLOCK(cs_mapAddresses) + { + // Load user provided addresses + CAutoFile filein = fopen((GetDataDir() + "/addr.txt").c_str(), "rt"); + if (filein) + { + try + { + char psz[1000]; + while (fgets(psz, sizeof(psz), filein)) + { + CAddress addr(psz, NODE_NETWORK); + addr.nTime = 0; // so it won't relay unless successfully connected + if (addr.IsValid()) + AddAddress(addr); + } + } + catch (...) { } + } + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + loop + { + // Read next record + CDataStream ssKey; + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + return false; + + // Unserialize + string strType; + ssKey >> strType; + if (strType == "addr") + { + CAddress addr; + ssValue >> addr; + mapAddresses.insert(make_pair(addr.GetKey(), addr)); + } + } + pcursor->close(); + + printf("Loaded %d addresses\n", mapAddresses.size()); + } + + return true; +} + +bool LoadAddresses() +{ + return CAddrDB("cr+").LoadAddresses(); +} + + + + +// +// CWalletDB +// + +static set setKeyPool; +static CCriticalSection cs_setKeyPool; + +bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) +{ + account.SetNull(); + return Read(make_pair(string("acc"), strAccount), account); +} + +bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) +{ + return Write(make_pair(string("acc"), strAccount), account); +} + +bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) +{ + return Write(make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry); +} + +int64 CWalletDB::GetAccountCreditDebit(const string& strAccount) +{ + list entries; + ListAccountCreditDebit(strAccount, entries); + + int64 nCreditDebit = 0; + BOOST_FOREACH (const CAccountingEntry& entry, entries) + nCreditDebit += entry.nCreditDebit; + + return nCreditDebit; +} + +void CWalletDB::ListAccountCreditDebit(const string& strAccount, list& entries) +{ + int64 nCreditDebit = 0; + + bool fAllAccounts = (strAccount == "*"); + + Dbc* pcursor = GetCursor(); + if (!pcursor) + throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor"); + unsigned int fFlags = DB_SET_RANGE; + loop + { + // Read next record + CDataStream ssKey; + if (fFlags == DB_SET_RANGE) + ssKey << make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0)); + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); + fFlags = DB_NEXT; + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + { + pcursor->close(); + throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB"); + } + + // Unserialize + string strType; + ssKey >> strType; + if (strType != "acentry") + break; + CAccountingEntry acentry; + ssKey >> acentry.strAccount; + if (!fAllAccounts && acentry.strAccount != strAccount) + break; + + ssValue >> acentry; + entries.push_back(acentry); + } + + pcursor->close(); +} + + +bool CWalletDB::LoadWallet() +{ + vchDefaultKey.clear(); + int nFileVersion = 0; + vector vWalletUpgrade; + + // Modify defaults +#ifndef __WXMSW__ + // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program + fMinimizeToTray = false; + fMinimizeOnClose = false; +#endif + + //// todo: shouldn't we catch exceptions and try to recover and continue? + CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_mapKeys) + { + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + loop + { + // Read next record + CDataStream ssKey; + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + return false; + + // Unserialize + // Taking advantage of the fact that pair serialization + // is just the two items serialized one after the other + string strType; + ssKey >> strType; + if (strType == "name") + { + string strAddress; + ssKey >> strAddress; + ssValue >> mapAddressBook[strAddress]; + } + else if (strType == "tx") + { + uint256 hash; + ssKey >> hash; + CWalletTx& wtx = mapWallet[hash]; + ssValue >> wtx; + + if (wtx.GetHash() != hash) + printf("Error in wallet.dat, hash mismatch\n"); + + // Undo serialize changes in 31600 + if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) + { + if (!ssValue.empty()) + { + char fTmp; + char fUnused; + ssValue >> fTmp >> fUnused >> wtx.strFromAccount; + printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str()); + wtx.fTimeReceivedIsTxTime = fTmp; + } + else + { + printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str()); + wtx.fTimeReceivedIsTxTime = 0; + } + vWalletUpgrade.push_back(hash); + } + + //// debug print + //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); + //printf(" %12I64d %s %s %s\n", + // wtx.vout[0].nValue, + // DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(), + // wtx.hashBlock.ToString().substr(0,20).c_str(), + // wtx.mapValue["message"].c_str()); + } + else if (strType == "acentry") + { + string strAccount; + ssKey >> strAccount; + uint64 nNumber; + ssKey >> nNumber; + if (nNumber > nAccountingEntryNumber) + nAccountingEntryNumber = nNumber; + } + else if (strType == "key" || strType == "wkey") + { + vector vchPubKey; + ssKey >> vchPubKey; + CWalletKey wkey; + if (strType == "key") + ssValue >> wkey.vchPrivKey; + else + ssValue >> wkey; + + mapKeys[vchPubKey] = wkey.vchPrivKey; + mapPubKeys[Hash160(vchPubKey)] = vchPubKey; + } + else if (strType == "defaultkey") + { + ssValue >> vchDefaultKey; + } + else if (strType == "pool") + { + int64 nIndex; + ssKey >> nIndex; + setKeyPool.insert(nIndex); + } + else if (strType == "version") + { + ssValue >> nFileVersion; + if (nFileVersion == 10300) + nFileVersion = 300; + } + else if (strType == "setting") + { + string strKey; + ssKey >> strKey; + + // Options +#ifndef GUI + if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins; +#endif + if (strKey == "nTransactionFee") ssValue >> nTransactionFee; + if (strKey == "addrIncoming") ssValue >> addrIncoming; + if (strKey == "fLimitProcessors") ssValue >> fLimitProcessors; + if (strKey == "nLimitProcessors") ssValue >> nLimitProcessors; + if (strKey == "fMinimizeToTray") ssValue >> fMinimizeToTray; + if (strKey == "fMinimizeOnClose") ssValue >> fMinimizeOnClose; + if (strKey == "fUseProxy") ssValue >> fUseProxy; + if (strKey == "addrProxy") ssValue >> addrProxy; + if (fHaveUPnP && strKey == "fUseUPnP") ssValue >> fUseUPnP; + } + } + pcursor->close(); + } + + BOOST_FOREACH(uint256 hash, vWalletUpgrade) + WriteTx(hash, mapWallet[hash]); + + printf("nFileVersion = %d\n", nFileVersion); + printf("fGenerateBitcoins = %d\n", fGenerateBitcoins); + printf("nTransactionFee = %"PRI64d"\n", nTransactionFee); + printf("addrIncoming = %s\n", addrIncoming.ToString().c_str()); + printf("fMinimizeToTray = %d\n", fMinimizeToTray); + printf("fMinimizeOnClose = %d\n", fMinimizeOnClose); + printf("fUseProxy = %d\n", fUseProxy); + printf("addrProxy = %s\n", addrProxy.ToString().c_str()); + if (fHaveUPnP) + printf("fUseUPnP = %d\n", fUseUPnP); + + + // Upgrade + if (nFileVersion < VERSION) + { + // Get rid of old debug.log file in current directory + if (nFileVersion <= 105 && !pszSetDataDir[0]) + unlink("debug.log"); + + WriteVersion(VERSION); + } + + + return true; +} + +bool LoadWallet(bool& fFirstRunRet) +{ + fFirstRunRet = false; + if (!CWalletDB("cr+").LoadWallet()) + return false; + fFirstRunRet = vchDefaultKey.empty(); + + if (mapKeys.count(vchDefaultKey)) + { + // Set keyUser + keyUser.SetPubKey(vchDefaultKey); + keyUser.SetPrivKey(mapKeys[vchDefaultKey]); + } + else + { + // Create new keyUser and set as default key + RandAddSeedPerfmon(); + keyUser.MakeNewKey(); + if (!AddKey(keyUser)) + return false; + if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "")) + return false; + CWalletDB().WriteDefaultKey(keyUser.GetPubKey()); + } + + CreateThread(ThreadFlushWalletDB, NULL); + return true; +} + +void ThreadFlushWalletDB(void* parg) +{ + static bool fOneThread; + if (fOneThread) + return; + fOneThread = true; + if (mapArgs.count("-noflushwallet")) + return; + + unsigned int nLastSeen = nWalletDBUpdated; + unsigned int nLastFlushed = nWalletDBUpdated; + int64 nLastWalletUpdate = GetTime(); + while (!fShutdown) + { + Sleep(500); + + if (nLastSeen != nWalletDBUpdated) + { + nLastSeen = nWalletDBUpdated; + nLastWalletUpdate = GetTime(); + } + + if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) + { + TRY_CRITICAL_BLOCK(cs_db) + { + // Don't do this if any databases are in use + int nRefCount = 0; + map::iterator mi = mapFileUseCount.begin(); + while (mi != mapFileUseCount.end()) + { + nRefCount += (*mi).second; + mi++; + } + + if (nRefCount == 0 && !fShutdown) + { + string strFile = "wallet.dat"; + map::iterator mi = mapFileUseCount.find(strFile); + if (mi != mapFileUseCount.end()) + { + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); + printf("Flushing wallet.dat\n"); + nLastFlushed = nWalletDBUpdated; + int64 nStart = GetTimeMillis(); + + // Flush wallet.dat so it's self contained + CloseDb(strFile); + dbenv.txn_checkpoint(0, 0, 0); + dbenv.lsn_reset(strFile.c_str(), 0); + + mapFileUseCount.erase(mi++); + printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart); + } + } + } + } + } +} + +void BackupWallet(const string& strDest) +{ + while (!fShutdown) + { + CRITICAL_BLOCK(cs_db) + { + const string strFile = "wallet.dat"; + if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0) + { + // Flush log data to the dat file + CloseDb(strFile); + dbenv.txn_checkpoint(0, 0, 0); + dbenv.lsn_reset(strFile.c_str(), 0); + mapFileUseCount.erase(strFile); + + // Copy wallet.dat + filesystem::path pathSrc(GetDataDir() + "/" + strFile); + filesystem::path pathDest(strDest); + if (filesystem::is_directory(pathDest)) + pathDest = pathDest / strFile; +#if BOOST_VERSION >= 104000 + filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists); +#else + filesystem::copy_file(pathSrc, pathDest); +#endif + printf("copied wallet.dat to %s\n", pathDest.string().c_str()); + + return; + } + } + Sleep(100); + } +} + + +void CWalletDB::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) +{ + nIndex = -1; + keypool.vchPubKey.clear(); + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_setKeyPool) + { + // Top up key pool + int64 nTargetSize = max(GetArg("-keypool", 100), (int64)0); + while (setKeyPool.size() < nTargetSize+1) + { + int64 nEnd = 1; + if (!setKeyPool.empty()) + nEnd = *(--setKeyPool.end()) + 1; + if (!Write(make_pair(string("pool"), nEnd), CKeyPool(GenerateNewKey()))) + throw runtime_error("ReserveKeyFromKeyPool() : writing generated key failed"); + setKeyPool.insert(nEnd); + printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size()); + } + + // Get the oldest key + assert(!setKeyPool.empty()); + nIndex = *(setKeyPool.begin()); + setKeyPool.erase(setKeyPool.begin()); + if (!Read(make_pair(string("pool"), nIndex), keypool)) + throw runtime_error("ReserveKeyFromKeyPool() : read failed"); + if (!mapKeys.count(keypool.vchPubKey)) + throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); + assert(!keypool.vchPubKey.empty()); + printf("keypool reserve %"PRI64d"\n", nIndex); + } +} + +void CWalletDB::KeepKey(int64 nIndex) +{ + // Remove from key pool + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + { + Erase(make_pair(string("pool"), nIndex)); + } + printf("keypool keep %"PRI64d"\n", nIndex); +} + +void CWalletDB::ReturnKey(int64 nIndex) +{ + // Return to key pool + CRITICAL_BLOCK(cs_setKeyPool) + setKeyPool.insert(nIndex); + printf("keypool return %"PRI64d"\n", nIndex); +} + +vector GetKeyFromKeyPool() +{ + CWalletDB walletdb; + int64 nIndex = 0; + CKeyPool keypool; + walletdb.ReserveKeyFromKeyPool(nIndex, keypool); + walletdb.KeepKey(nIndex); + return keypool.vchPubKey; +} + +int64 GetOldestKeyPoolTime() +{ + CWalletDB walletdb; + int64 nIndex = 0; + CKeyPool keypool; + walletdb.ReserveKeyFromKeyPool(nIndex, keypool); + walletdb.ReturnKey(nIndex); + return keypool.nTime; +} diff --git a/core/src/init.cpp b/core/src/init.cpp new file mode 100644 index 0000000000..3126d3489e --- /dev/null +++ b/core/src/init.cpp @@ -0,0 +1,522 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#include "headers.h" + +using namespace std; +using namespace boost; + +////////////////////////////////////////////////////////////////////////////// +// +// Shutdown +// + +void ExitTimeout(void* parg) +{ +#ifdef __WXMSW__ + Sleep(5000); + ExitProcess(0); +#endif +} + +void Shutdown(void* parg) +{ + static CCriticalSection cs_Shutdown; + static bool fTaken; + bool fFirstThread; + CRITICAL_BLOCK(cs_Shutdown) + { + fFirstThread = !fTaken; + fTaken = true; + } + static bool fExit; + if (fFirstThread) + { + fShutdown = true; + nTransactionsUpdated++; + DBFlush(false); + StopNode(); + DBFlush(true); + boost::filesystem::remove(GetPidFile()); + CreateThread(ExitTimeout, NULL); + Sleep(50); + printf("Bitcoin exiting\n\n"); + fExit = true; + exit(0); + } + else + { + while (!fExit) + Sleep(500); + Sleep(100); + ExitThread(0); + } +} + +void HandleSIGTERM(int) +{ + fRequestShutdown = true; +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Start +// +#if 0 +#ifndef GUI +int main(int argc, char* argv[]) +{ + bool fRet = false; + fRet = AppInit(argc, argv); + + if (fRet && fDaemon) + return 0; + + return 1; +} +#endif +#endif + +bool AppInit(int argc, char* argv[]) +{ + bool fRet = false; + try + { + fRet = AppInit2(argc, argv); + } + catch (std::exception& e) { + PrintException(&e, "AppInit()"); + } catch (...) { + PrintException(NULL, "AppInit()"); + } + if (!fRet) + Shutdown(NULL); + return fRet; +} + +bool AppInit2(int argc, char* argv[]) +{ +#ifdef _MSC_VER + // Turn off microsoft heap dump noise + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); +#endif +#if _MSC_VER >= 1400 + // Disable confusing "helpful" text message on abort, ctrl-c + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); +#endif +#ifndef __WXMSW__ + umask(077); +#endif +#ifndef __WXMSW__ + // Clean shutdown on SIGTERM + struct sigaction sa; + sa.sa_handler = HandleSIGTERM; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); +#endif + + // + // Parameters + // + ParseParameters(argc, argv); + + if (mapArgs.count("-datadir")) + { + filesystem::path pathDataDir = filesystem::system_complete(mapArgs["-datadir"]); + strlcpy(pszSetDataDir, pathDataDir.string().c_str(), sizeof(pszSetDataDir)); + } + + ReadConfigFile(mapArgs, mapMultiArgs); // Must be done after processing datadir + + if (mapArgs.count("-?") || mapArgs.count("--help")) + { + string beta = VERSION_IS_BETA ? _(" beta") : ""; + string strUsage = string() + + _("Bitcoin version") + " " + FormatFullVersion() + "\n\n" + + _("Usage:") + "\t\t\t\t\t\t\t\t\t\t\n" + + " bitcoin [options] \t " + "\n" + + " bitcoin [options] [params]\t " + _("Send command to -server or bitcoind\n") + + " bitcoin [options] help \t\t " + _("List commands\n") + + " bitcoin [options] help \t\t " + _("Get help for a command\n") + + _("Options:\n") + + " -conf= \t\t " + _("Specify configuration file (default: bitcoin.conf)\n") + + " -pid= \t\t " + _("Specify pid file (default: bitcoind.pid)\n") + + " -gen \t\t " + _("Generate coins\n") + + " -gen=0 \t\t " + _("Don't generate coins\n") + + " -min \t\t " + _("Start minimized\n") + + " -datadir=

\t\t " + _("Specify data directory\n") + + " -proxy= \t " + _("Connect through socks4 proxy\n") + + " -dns \t " + _("Allow DNS lookups for addnode and connect\n") + + " -addnode= \t " + _("Add a node to connect to\n") + + " -connect= \t\t " + _("Connect only to the specified node\n") + + " -nolisten \t " + _("Don't accept connections from outside\n") + +#ifdef USE_UPNP +#if USE_UPNP + " -noupnp \t " + _("Don't attempt to use UPnP to map the listening port\n") + +#else + " -upnp \t " + _("Attempt to use UPnP to map the listening port\n") + +#endif +#endif + " -paytxfee= \t " + _("Fee per KB to add to transactions you send\n") + +#ifdef GUI + " -server \t\t " + _("Accept command line and JSON-RPC commands\n") + +#endif +#ifndef __WXMSW__ + " -daemon \t\t " + _("Run in the background as a daemon and accept commands\n") + +#endif + " -testnet \t\t " + _("Use the test network\n") + + " -rpcuser= \t " + _("Username for JSON-RPC connections\n") + + " -rpcpassword=\t " + _("Password for JSON-RPC connections\n") + + " -rpcport= \t\t " + _("Listen for JSON-RPC connections on (default: 8332)\n") + + " -rpcallowip= \t\t " + _("Allow JSON-RPC connections from specified IP address\n") + + " -rpcconnect= \t " + _("Send commands to node running on (default: 127.0.0.1)\n") + + " -keypool= \t " + _("Set key pool size to (default: 100)\n") + + " -rescan \t " + _("Rescan the block chain for missing wallet transactions\n"); + +#ifdef USE_SSL + strUsage += string() + + _("\nSSL options: (see the Bitcoin Wiki for SSL setup instructions)\n") + + " -rpcssl \t " + _("Use OpenSSL (https) for JSON-RPC connections\n") + + " -rpcsslcertificatechainfile=\t " + _("Server certificate file (default: server.cert)\n") + + " -rpcsslprivatekeyfile= \t " + _("Server private key (default: server.pem)\n") + + " -rpcsslciphers= \t " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)\n"); +#endif + + strUsage += string() + + " -? \t\t " + _("This help message\n"); + +#if defined(__WXMSW__) && defined(GUI) + // Tabs make the columns line up in the message box + wxMessageBox(strUsage, "Bitcoin", wxOK); +#else + // Remove tabs + strUsage.erase(std::remove(strUsage.begin(), strUsage.end(), '\t'), strUsage.end()); + fprintf(stderr, "%s", strUsage.c_str()); +#endif + return false; + } + + fDebug = GetBoolArg("-debug"); + fAllowDNS = GetBoolArg("-dns"); + +#ifndef __WXMSW__ + fDaemon = GetBoolArg("-daemon"); +#else + fDaemon = false; +#endif + + if (fDaemon) + fServer = true; + else + fServer = GetBoolArg("-server"); + + /* force fServer when running without GUI */ +#ifndef GUI + fServer = true; +#endif + + fPrintToConsole = GetBoolArg("-printtoconsole"); + fPrintToDebugger = GetBoolArg("-printtodebugger"); + + fTestNet = GetBoolArg("-testnet"); + fNoListen = GetBoolArg("-nolisten"); + fLogTimestamps = GetBoolArg("-logtimestamps"); + + for (int i = 1; i < argc; i++) + if (!IsSwitchChar(argv[i][0])) + fCommandLine = true; + + if (fCommandLine) + { + int ret = CommandLineRPC(argc, argv); + exit(ret); + } + +#ifndef __WXMSW__ + if (fDaemon) + { + // Daemonize + pid_t pid = fork(); + if (pid < 0) + { + fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno); + return false; + } + if (pid > 0) + { + CreatePidFile(GetPidFile(), pid); + return true; + } + + pid_t sid = setsid(); + if (sid < 0) + fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno); + } +#endif + + if (!fDebug && !pszSetDataDir[0]) + ShrinkDebugFile(); + printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); + printf("Bitcoin version %s\n", FormatFullVersion().c_str()); +#ifdef GUI + printf("OS version %s\n", ((string)wxGetOsDescription()).c_str()); + printf("System default language is %d %s\n", g_locale.GetSystemLanguage(), ((string)g_locale.GetSysName()).c_str()); + printf("Language file %s (%s)\n", (string("locale/") + (string)g_locale.GetCanonicalName() + "/LC_MESSAGES/bitcoin.mo").c_str(), ((string)g_locale.GetLocale()).c_str()); +#endif + printf("Default data directory %s\n", GetDefaultDataDir().c_str()); + + if (GetBoolArg("-loadblockindextest")) + { + CTxDB txdb("r"); + txdb.LoadBlockIndex(); + PrintBlockTree(); + return false; + } + + // + // Limit to single instance per user + // Required to protect the database files if we're going to keep deleting log.* + // +#if defined(__WXMSW__) && defined(GUI) + // wxSingleInstanceChecker doesn't work on Linux + wxString strMutexName = wxString("bitcoin_running.") + getenv("HOMEPATH"); + for (int i = 0; i < strMutexName.size(); i++) + if (!isalnum(strMutexName[i])) + strMutexName[i] = '.'; + wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName); + if (psingleinstancechecker->IsAnotherRunning()) + { + printf("Existing instance found\n"); + unsigned int nStart = GetTime(); + loop + { + // Show the previous instance and exit + HWND hwndPrev = FindWindowA("wxWindowClassNR", "Bitcoin"); + if (hwndPrev) + { + if (IsIconic(hwndPrev)) + ShowWindow(hwndPrev, SW_RESTORE); + SetForegroundWindow(hwndPrev); + return false; + } + + if (GetTime() > nStart + 60) + return false; + + // Resume this instance if the other exits + delete psingleinstancechecker; + Sleep(1000); + psingleinstancechecker = new wxSingleInstanceChecker(strMutexName); + if (!psingleinstancechecker->IsAnotherRunning()) + break; + } + } +#endif + + // Make sure only a single bitcoin process is using the data directory. + string strLockFile = GetDataDir() + "/.lock"; + FILE* file = fopen(strLockFile.c_str(), "a"); // empty lock file; created if it doesn't exist. + if (file) fclose(file); + static boost::interprocess::file_lock lock(strLockFile.c_str()); + if (!lock.try_lock()) + { + wxMessageBox(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), GetDataDir().c_str()), "Bitcoin"); + return false; + } + + // Bind to the port early so we can tell if another instance is already running. + string strErrors; + if (!fNoListen) + { + if (!BindListenPort(strErrors)) + { + wxMessageBox(strErrors, "Bitcoin"); + return false; + } + } + + // + // Load data files + // + if (fDaemon) + fprintf(stdout, "bitcoin server starting\n"); + strErrors = ""; + int64 nStart; + + printf("Loading addresses...\n"); + nStart = GetTimeMillis(); + if (!LoadAddresses()) + strErrors += _("Error loading addr.dat \n"); + printf(" addresses %15"PRI64d"ms\n", GetTimeMillis() - nStart); + + printf("Loading block index...\n"); + nStart = GetTimeMillis(); + if (!LoadBlockIndex()) + strErrors += _("Error loading blkindex.dat \n"); + printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart); + + printf("Loading wallet...\n"); + nStart = GetTimeMillis(); + bool fFirstRun; + if (!LoadWallet(fFirstRun)) + strErrors += _("Error loading wallet.dat \n"); + printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart); + + CBlockIndex *pindexRescan = pindexBest; + if (GetBoolArg("-rescan")) + pindexRescan = pindexGenesisBlock; + else + { + CWalletDB walletdb; + CBlockLocator locator; + if (walletdb.ReadBestBlock(locator)) + pindexRescan = locator.GetBlockIndex(); + } + if (pindexBest != pindexRescan) + { + printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); + nStart = GetTimeMillis(); + ScanForWalletTransactions(pindexRescan); + printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); + } + + printf("Done loading\n"); + + //// debug print + printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size()); + printf("nBestHeight = %d\n", nBestHeight); + printf("mapKeys.size() = %d\n", mapKeys.size()); + printf("mapPubKeys.size() = %d\n", mapPubKeys.size()); + printf("mapWallet.size() = %d\n", mapWallet.size()); + printf("mapAddressBook.size() = %d\n", mapAddressBook.size()); + + if (!strErrors.empty()) + { + wxMessageBox(strErrors, "Bitcoin", wxOK | wxICON_ERROR); + return false; + } + + // Add wallet transactions that aren't already in a block to mapTransactions + ReacceptWalletTransactions(); + + // + // Parameters + // + if (GetBoolArg("-printblockindex") || GetBoolArg("-printblocktree")) + { + PrintBlockTree(); + return false; + } + + if (mapArgs.count("-printblock")) + { + string strMatch = mapArgs["-printblock"]; + int nFound = 0; + for (map::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) + { + uint256 hash = (*mi).first; + if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0) + { + CBlockIndex* pindex = (*mi).second; + CBlock block; + block.ReadFromDisk(pindex); + block.BuildMerkleTree(); + block.print(); + printf("\n"); + nFound++; + } + } + if (nFound == 0) + printf("No blocks matching %s were found\n", strMatch.c_str()); + return false; + } + + fGenerateBitcoins = GetBoolArg("-gen"); + + if (mapArgs.count("-proxy")) + { + fUseProxy = true; + addrProxy = CAddress(mapArgs["-proxy"]); + if (!addrProxy.IsValid()) + { + wxMessageBox(_("Invalid -proxy address"), "Bitcoin"); + return false; + } + } + + if (mapArgs.count("-addnode")) + { + BOOST_FOREACH(string strAddr, mapMultiArgs["-addnode"]) + { + CAddress addr(strAddr, fAllowDNS); + addr.nTime = 0; // so it won't relay unless successfully connected + if (addr.IsValid()) + AddAddress(addr); + } + } + + if (mapArgs.count("-dnsseed")) + DNSAddressSeed(); + + if (mapArgs.count("-paytxfee")) + { + if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee)) + { + wxMessageBox(_("Invalid amount for -paytxfee="), "Bitcoin"); + return false; + } + if (nTransactionFee > 0.25 * COIN) + wxMessageBox(_("Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction."), "Bitcoin", wxOK | wxICON_EXCLAMATION); + } + + if (fHaveUPnP) + { +#if USE_UPNP + if (GetBoolArg("-noupnp")) + fUseUPnP = false; +#else + if (GetBoolArg("-upnp")) + fUseUPnP = true; +#endif + } + + // + // Create the main window and start the node + // +#ifdef GUI + if (!fDaemon) + CreateMainWindow(); +#endif + + if (!CheckDiskSpace()) + return false; + + RandAddSeedPerfmon(); + + if (!CreateThread(StartNode, NULL)) + wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin"); + + if (fServer) + CreateThread(ThreadRPCServer, NULL); + +#if defined(__WXMSW__) && defined(GUI) + if (fFirstRun) + SetStartOnSystemStartup(true); +#endif + +#ifndef GUI + while (1) + Sleep(5000); +#endif + + return true; +} diff --git a/core/src/irc.cpp b/core/src/irc.cpp new file mode 100644 index 0000000000..099d9e0735 --- /dev/null +++ b/core/src/irc.cpp @@ -0,0 +1,438 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" + +using namespace std; +using namespace boost; + +int nGotIRCAddresses = 0; +bool fGotExternalIP = false; + +void ThreadIRCSeed2(void* parg); + + + + +#pragma pack(push, 1) +struct ircaddr +{ + int ip; + short port; +}; +#pragma pack(pop) + +string EncodeAddress(const CAddress& addr) +{ + struct ircaddr tmp; + tmp.ip = addr.ip; + tmp.port = addr.port; + + vector vch(UBEGIN(tmp), UEND(tmp)); + return string("u") + EncodeBase58Check(vch); +} + +bool DecodeAddress(string str, CAddress& addr) +{ + vector vch; + if (!DecodeBase58Check(str.substr(1), vch)) + return false; + + struct ircaddr tmp; + if (vch.size() != sizeof(tmp)) + return false; + memcpy(&tmp, &vch[0], sizeof(tmp)); + + addr = CAddress(tmp.ip, ntohs(tmp.port), NODE_NETWORK); + return true; +} + + + + + + +static bool Send(SOCKET hSocket, const char* pszSend) +{ + if (strstr(pszSend, "PONG") != pszSend) + printf("IRC SENDING: %s\n", pszSend); + const char* psz = pszSend; + const char* pszEnd = psz + strlen(psz); + while (psz < pszEnd) + { + int ret = send(hSocket, psz, pszEnd - psz, MSG_NOSIGNAL); + if (ret < 0) + return false; + psz += ret; + } + return true; +} + +bool RecvLine(SOCKET hSocket, string& strLine) +{ + strLine = ""; + loop + { + char c; + int nBytes = recv(hSocket, &c, 1, 0); + if (nBytes > 0) + { + if (c == '\n') + continue; + if (c == '\r') + return true; + strLine += c; + if (strLine.size() >= 9000) + return true; + } + else if (nBytes <= 0) + { + if (fShutdown) + return false; + if (nBytes < 0) + { + int nErr = WSAGetLastError(); + if (nErr == WSAEMSGSIZE) + continue; + if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS) + { + Sleep(10); + continue; + } + } + if (!strLine.empty()) + return true; + if (nBytes == 0) + { + // socket closed + printf("IRC socket closed\n"); + return false; + } + else + { + // socket error + int nErr = WSAGetLastError(); + printf("IRC recv failed: %d\n", nErr); + return false; + } + } + } +} + +bool RecvLineIRC(SOCKET hSocket, string& strLine) +{ + loop + { + bool fRet = RecvLine(hSocket, strLine); + if (fRet) + { + if (fShutdown) + return false; + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords.size() >= 1 && vWords[0] == "PING") + { + strLine[1] = 'O'; + strLine += '\r'; + Send(hSocket, strLine.c_str()); + continue; + } + } + return fRet; + } +} + +int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL, const char* psz4=NULL) +{ + loop + { + string strLine; + strLine.reserve(10000); + if (!RecvLineIRC(hSocket, strLine)) + return 0; + printf("IRC %s\n", strLine.c_str()); + if (psz1 && strLine.find(psz1) != -1) + return 1; + if (psz2 && strLine.find(psz2) != -1) + return 2; + if (psz3 && strLine.find(psz3) != -1) + return 3; + if (psz4 && strLine.find(psz4) != -1) + return 4; + } +} + +bool Wait(int nSeconds) +{ + if (fShutdown) + return false; + printf("IRC waiting %d seconds to reconnect\n", nSeconds); + for (int i = 0; i < nSeconds; i++) + { + if (fShutdown) + return false; + Sleep(1000); + } + return true; +} + +bool RecvCodeLine(SOCKET hSocket, const char* psz1, string& strRet) +{ + strRet.clear(); + loop + { + string strLine; + if (!RecvLineIRC(hSocket, strLine)) + return false; + + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords.size() < 2) + continue; + + if (vWords[1] == psz1) + { + printf("IRC %s\n", strLine.c_str()); + strRet = strLine; + return true; + } + } +} + +bool GetIPFromIRC(SOCKET hSocket, string strMyName, unsigned int& ipRet) +{ + Send(hSocket, strprintf("USERHOST %s\r", strMyName.c_str()).c_str()); + + string strLine; + if (!RecvCodeLine(hSocket, "302", strLine)) + return false; + + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords.size() < 4) + return false; + + string str = vWords[3]; + if (str.rfind("@") == string::npos) + return false; + string strHost = str.substr(str.rfind("@")+1); + + // Hybrid IRC used by lfnet always returns IP when you userhost yourself, + // but in case another IRC is ever used this should work. + printf("GetIPFromIRC() got userhost %s\n", strHost.c_str()); + if (fUseProxy) + return false; + CAddress addr(strHost, 0, true); + if (!addr.IsValid()) + return false; + ipRet = addr.ip; + + return true; +} + + + +void ThreadIRCSeed(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadIRCSeed(parg)); + try + { + ThreadIRCSeed2(parg); + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "ThreadIRCSeed()"); + } catch (...) { + PrintExceptionContinue(NULL, "ThreadIRCSeed()"); + } + printf("ThreadIRCSeed exiting\n"); +} + +void ThreadIRCSeed2(void* parg) +{ + /* Dont advertise on IRC if we don't allow incoming connections */ + if (mapArgs.count("-connect") || fNoListen) + return; + + if (GetBoolArg("-noirc")) + return; + printf("ThreadIRCSeed started\n"); + int nErrorWait = 10; + int nRetryWait = 10; + bool fNameInUse = false; + bool fTOR = (fUseProxy && addrProxy.port == htons(9050)); + + while (!fShutdown) + { + //CAddress addrConnect("216.155.130.130:6667"); // chat.freenode.net + CAddress addrConnect("92.243.23.21:6667"); // irc.lfnet.org + if (!fTOR) + { + //struct hostent* phostent = gethostbyname("chat.freenode.net"); + CAddress addrIRC("irc.lfnet.org:6667", 0, true); + if (addrIRC.IsValid()) + addrConnect = addrIRC; + } + + SOCKET hSocket; + if (!ConnectSocket(addrConnect, hSocket)) + { + printf("IRC connect failed\n"); + nErrorWait = nErrorWait * 11 / 10; + if (Wait(nErrorWait += 60)) + continue; + else + return; + } + + if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname", "ignoring hostname")) + { + closesocket(hSocket); + hSocket = INVALID_SOCKET; + nErrorWait = nErrorWait * 11 / 10; + if (Wait(nErrorWait += 60)) + continue; + else + return; + } + + string strMyName; + if (addrLocalHost.IsRoutable() && !fUseProxy && !fNameInUse) + strMyName = EncodeAddress(addrLocalHost); + else + strMyName = strprintf("x%u", GetRand(1000000000)); + + Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str()); + Send(hSocket, strprintf("USER %s 8 * : %s\r", strMyName.c_str(), strMyName.c_str()).c_str()); + + int nRet = RecvUntil(hSocket, " 004 ", " 433 "); + if (nRet != 1) + { + closesocket(hSocket); + hSocket = INVALID_SOCKET; + if (nRet == 2) + { + printf("IRC name already in use\n"); + fNameInUse = true; + Wait(10); + continue; + } + nErrorWait = nErrorWait * 11 / 10; + if (Wait(nErrorWait += 60)) + continue; + else + return; + } + Sleep(500); + + // Get our external IP from the IRC server and re-nick before joining the channel + CAddress addrFromIRC; + if (GetIPFromIRC(hSocket, strMyName, addrFromIRC.ip)) + { + printf("GetIPFromIRC() returned %s\n", addrFromIRC.ToStringIP().c_str()); + if (!fUseProxy && addrFromIRC.IsRoutable()) + { + // IRC lets you to re-nick + fGotExternalIP = true; + addrLocalHost.ip = addrFromIRC.ip; + strMyName = EncodeAddress(addrLocalHost); + Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str()); + } + } + + Send(hSocket, fTestNet ? "JOIN #bitcoinTEST\r" : "JOIN #bitcoin\r"); + Send(hSocket, fTestNet ? "WHO #bitcoinTEST\r" : "WHO #bitcoin\r"); + + int64 nStart = GetTime(); + string strLine; + strLine.reserve(10000); + while (!fShutdown && RecvLineIRC(hSocket, strLine)) + { + if (strLine.empty() || strLine.size() > 900 || strLine[0] != ':') + continue; + + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords.size() < 2) + continue; + + char pszName[10000]; + pszName[0] = '\0'; + + if (vWords[1] == "352" && vWords.size() >= 8) + { + // index 7 is limited to 16 characters + // could get full length name at index 10, but would be different from join messages + strlcpy(pszName, vWords[7].c_str(), sizeof(pszName)); + printf("IRC got who\n"); + } + + if (vWords[1] == "JOIN" && vWords[0].size() > 1) + { + // :username!username@50000007.F000000B.90000002.IP JOIN :#channelname + strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName)); + if (strchr(pszName, '!')) + *strchr(pszName, '!') = '\0'; + printf("IRC got join\n"); + } + + if (pszName[0] == 'u') + { + CAddress addr; + if (DecodeAddress(pszName, addr)) + { + addr.nTime = GetAdjustedTime(); + if (AddAddress(addr, 51 * 60)) + printf("IRC got new address: %s\n", addr.ToString().c_str()); + nGotIRCAddresses++; + } + else + { + printf("IRC decode failed\n"); + } + } + } + closesocket(hSocket); + hSocket = INVALID_SOCKET; + + // IRC usually blocks TOR, so only try once + if (fTOR) + return; + + if (GetTime() - nStart > 20 * 60) + { + nErrorWait /= 3; + nRetryWait /= 3; + } + + nRetryWait = nRetryWait * 11 / 10; + if (!Wait(nRetryWait += 60)) + return; + } +} + + + + + + + + + + +#ifdef TEST +int main(int argc, char *argv[]) +{ + WSADATA wsadata; + if (WSAStartup(MAKEWORD(2,2), &wsadata) != NO_ERROR) + { + printf("Error at WSAStartup()\n"); + return false; + } + + ThreadIRCSeed(NULL); + + WSACleanup(); + return 0; +} +#endif diff --git a/core/src/main.cpp b/core/src/main.cpp new file mode 100644 index 0000000000..68b6b4ee1b --- /dev/null +++ b/core/src/main.cpp @@ -0,0 +1,4024 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#include "headers.h" +#include "cryptopp/sha.h" + +using namespace std; +using namespace boost; + +// +// Global state +// + +CCriticalSection cs_main; + +map mapTransactions; +CCriticalSection cs_mapTransactions; +unsigned int nTransactionsUpdated = 0; +map mapNextTx; + +map mapBlockIndex; +uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); +CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); +CBlockIndex* pindexGenesisBlock = NULL; +int nBestHeight = -1; +CBigNum bnBestChainWork = 0; +CBigNum bnBestInvalidWork = 0; +uint256 hashBestChain = 0; +CBlockIndex* pindexBest = NULL; +int64 nTimeBestReceived = 0; + +map mapOrphanBlocks; +multimap mapOrphanBlocksByPrev; + +map mapOrphanTransactions; +multimap mapOrphanTransactionsByPrev; + +map mapWallet; +vector vWalletUpdated; +CCriticalSection cs_mapWallet; + +map, CPrivKey> mapKeys; +map > mapPubKeys; +CCriticalSection cs_mapKeys; +CKey keyUser; + +map mapRequestCount; +CCriticalSection cs_mapRequestCount; + +map mapAddressBook; +CCriticalSection cs_mapAddressBook; + +vector vchDefaultKey; + +double dHashesPerSec; +int64 nHPSTimerStart; + +// Settings +int fGenerateBitcoins = false; +int64 nTransactionFee = 0; +CAddress addrIncoming; +int fLimitProcessors = false; +int nLimitProcessors = 1; +int fMinimizeToTray = true; +int fMinimizeOnClose = true; +#if USE_UPNP +int fUseUPnP = true; +#else +int fUseUPnP = false; +#endif + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapKeys +// + +bool AddKey(const CKey& key) +{ + CRITICAL_BLOCK(cs_mapKeys) + { + mapKeys[key.GetPubKey()] = key.GetPrivKey(); + mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey(); + } + return CWalletDB().WriteKey(key.GetPubKey(), key.GetPrivKey()); +} + +vector GenerateNewKey() +{ + RandAddSeedPerfmon(); + CKey key; + key.MakeNewKey(); + if (!AddKey(key)) + throw runtime_error("GenerateNewKey() : AddKey failed"); + return key.GetPubKey(); +} + + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapWallet +// + +bool AddToWallet(const CWalletTx& wtxIn) +{ + uint256 hash = wtxIn.GetHash(); + CRITICAL_BLOCK(cs_mapWallet) + { + // Inserts only if not already there, returns tx inserted or tx found + pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); + CWalletTx& wtx = (*ret.first).second; + bool fInsertedNew = ret.second; + if (fInsertedNew) + wtx.nTimeReceived = GetAdjustedTime(); + + bool fUpdated = false; + if (!fInsertedNew) + { + // Merge + if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) + { + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) + { + wtx.vMerkleBranch = wtxIn.vMerkleBranch; + wtx.nIndex = wtxIn.nIndex; + fUpdated = true; + } + if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) + { + wtx.fFromMe = wtxIn.fFromMe; + fUpdated = true; + } + fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent); + } + + //// debug print + printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + + // Write to disk + if (fInsertedNew || fUpdated) + if (!wtx.WriteToDisk()) + return false; + + // If default receiving address gets used, replace it with a new one + CScript scriptDefaultKey; + scriptDefaultKey.SetBitcoinAddress(vchDefaultKey); + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (txout.scriptPubKey == scriptDefaultKey) + { + CWalletDB walletdb; + vchDefaultKey = GetKeyFromKeyPool(); + walletdb.WriteDefaultKey(vchDefaultKey); + walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); + } + } + + // Notify UI + vWalletUpdated.push_back(hash); + } + + // Refresh UI + MainFrameRepaint(); + return true; +} + +bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false) +{ + uint256 hash = tx.GetHash(); + bool fExisted = mapWallet.count(hash); + if (fExisted && !fUpdate) return false; + if (fExisted || tx.IsMine() || tx.IsFromMe()) + { + CWalletTx wtx(tx); + // Get merkle branch if transaction was found in a block + if (pblock) + wtx.SetMerkleBranch(pblock); + return AddToWallet(wtx); + } + return false; +} + +bool EraseFromWallet(uint256 hash) +{ + CRITICAL_BLOCK(cs_mapWallet) + { + if (mapWallet.erase(hash)) + CWalletDB().EraseTx(hash); + } + return true; +} + +void WalletUpdateSpent(const COutPoint& prevout) +{ + // Anytime a signature is successfully verified, it's proof the outpoint is spent. + // Update the wallet spent flag if it doesn't know due to wallet.dat being + // restored from backup or the user making copies of wallet.dat. + CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + CWalletTx& wtx = (*mi).second; + if (!wtx.IsSpent(prevout.n) && wtx.vout[prevout.n].IsMine()) + { + printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + wtx.MarkSpent(prevout.n); + wtx.WriteToDisk(); + vWalletUpdated.push_back(prevout.hash); + } + } + } +} + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapOrphanTransactions +// + +void AddOrphanTx(const CDataStream& vMsg) +{ + CTransaction tx; + CDataStream(vMsg) >> tx; + uint256 hash = tx.GetHash(); + if (mapOrphanTransactions.count(hash)) + return; + CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg); + BOOST_FOREACH(const CTxIn& txin, tx.vin) + mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg)); +} + +void EraseOrphanTx(uint256 hash) +{ + if (!mapOrphanTransactions.count(hash)) + return; + const CDataStream* pvMsg = mapOrphanTransactions[hash]; + CTransaction tx; + CDataStream(*pvMsg) >> tx; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + for (multimap::iterator mi = mapOrphanTransactionsByPrev.lower_bound(txin.prevout.hash); + mi != mapOrphanTransactionsByPrev.upper_bound(txin.prevout.hash);) + { + if ((*mi).second == pvMsg) + mapOrphanTransactionsByPrev.erase(mi++); + else + mi++; + } + } + delete pvMsg; + mapOrphanTransactions.erase(hash); +} + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CTransaction and CTxIndex +// + +bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet) +{ + SetNull(); + if (!txdb.ReadTxIndex(prevout.hash, txindexRet)) + return false; + if (!ReadFromDisk(txindexRet.pos)) + return false; + if (prevout.n >= vout.size()) + { + SetNull(); + return false; + } + return true; +} + +bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout) +{ + CTxIndex txindex; + return ReadFromDisk(txdb, prevout, txindex); +} + +bool CTransaction::ReadFromDisk(COutPoint prevout) +{ + CTxDB txdb("r"); + CTxIndex txindex; + return ReadFromDisk(txdb, prevout, txindex); +} + +bool CTxIn::IsMine() const +{ + CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (prevout.n < prev.vout.size()) + if (prev.vout[prevout.n].IsMine()) + return true; + } + } + return false; +} + +int64 CTxIn::GetDebit() const +{ + CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (prevout.n < prev.vout.size()) + if (prev.vout[prevout.n].IsMine()) + return prev.vout[prevout.n].nValue; + } + } + return 0; +} + +int64 CWalletTx::GetTxTime() const +{ + if (!fTimeReceivedIsTxTime && hashBlock != 0) + { + // If we did not receive the transaction directly, we rely on the block's + // time to figure out when it happened. We use the median over a range + // of blocks to try to filter out inaccurate block times. + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex) + return pindex->GetMedianTime(); + } + } + return nTimeReceived; +} + +int CWalletTx::GetRequestCount() const +{ + // Returns -1 if it wasn't being tracked + int nRequests = -1; + CRITICAL_BLOCK(cs_mapRequestCount) + { + if (IsCoinBase()) + { + // Generated block + if (hashBlock != 0) + { + map::iterator mi = mapRequestCount.find(hashBlock); + if (mi != mapRequestCount.end()) + nRequests = (*mi).second; + } + } + else + { + // Did anyone request this transaction? + map::iterator mi = mapRequestCount.find(GetHash()); + if (mi != mapRequestCount.end()) + { + nRequests = (*mi).second; + + // How about the block it's in? + if (nRequests == 0 && hashBlock != 0) + { + map::iterator mi = mapRequestCount.find(hashBlock); + if (mi != mapRequestCount.end()) + nRequests = (*mi).second; + else + nRequests = 1; // If it's in someone else's block it must have got out + } + } + } + } + return nRequests; +} + +void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list >& listReceived, + list >& listSent, int64& nFee, string& strSentAccount) const +{ + nGeneratedImmature = nGeneratedMature = nFee = 0; + listReceived.clear(); + listSent.clear(); + strSentAccount = strFromAccount; + + if (IsCoinBase()) + { + if (GetBlocksToMaturity() > 0) + nGeneratedImmature = CTransaction::GetCredit(); + else + nGeneratedMature = GetCredit(); + return; + } + + // Compute fee: + int64 nDebit = GetDebit(); + if (nDebit > 0) // debit>0 means we signed/sent this transaction + { + int64 nValueOut = GetValueOut(); + nFee = nDebit - nValueOut; + } + + // Sent/received. Standard client will never generate a send-to-multiple-recipients, + // but non-standard clients might (so return a list of address/amount pairs) + BOOST_FOREACH(const CTxOut& txout, vout) + { + string address; + uint160 hash160; + vector vchPubKey; + if (ExtractHash160(txout.scriptPubKey, hash160)) + address = Hash160ToAddress(hash160); + else if (ExtractPubKey(txout.scriptPubKey, false, vchPubKey)) + address = PubKeyToAddress(vchPubKey); + else + { + printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", + this->GetHash().ToString().c_str()); + address = " unknown "; + } + + // Don't report 'change' txouts + if (nDebit > 0 && txout.IsChange()) + continue; + + if (nDebit > 0) + listSent.push_back(make_pair(address, txout.nValue)); + + if (txout.IsMine()) + listReceived.push_back(make_pair(address, txout.nValue)); + } + +} + +void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, + int64& nSent, int64& nFee) const +{ + nGenerated = nReceived = nSent = nFee = 0; + + int64 allGeneratedImmature, allGeneratedMature, allFee; + allGeneratedImmature = allGeneratedMature = allFee = 0; + string strSentAccount; + list > listReceived; + list > listSent; + GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); + + if (strAccount == "") + nGenerated = allGeneratedMature; + if (strAccount == strSentAccount) + { + BOOST_FOREACH(const PAIRTYPE(string,int64)& s, listSent) + nSent += s.second; + nFee = allFee; + } + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) + { + if (mapAddressBook.count(r.first)) + { + if (mapAddressBook[r.first] == strAccount) + { + nReceived += r.second; + } + } + else if (strAccount.empty()) + { + nReceived += r.second; + } + } + } +} + + + +int CMerkleTx::SetMerkleBranch(const CBlock* pblock) +{ + if (fClient) + { + if (hashBlock == 0) + return 0; + } + else + { + CBlock blockTmp; + if (pblock == NULL) + { + // Load the block this tx is in + CTxIndex txindex; + if (!CTxDB("r").ReadTxIndex(GetHash(), txindex)) + return 0; + if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos)) + return 0; + pblock = &blockTmp; + } + + // Update the tx's hashBlock + hashBlock = pblock->GetHash(); + + // Locate the transaction + for (nIndex = 0; nIndex < pblock->vtx.size(); nIndex++) + if (pblock->vtx[nIndex] == *(CTransaction*)this) + break; + if (nIndex == pblock->vtx.size()) + { + vMerkleBranch.clear(); + nIndex = -1; + printf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); + return 0; + } + + // Fill in merkle branch + vMerkleBranch = pblock->GetMerkleBranch(nIndex); + } + + // Is the tx in a block that's in the main chain + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !pindex->IsInMainChain()) + return 0; + + return pindexBest->nHeight - pindex->nHeight + 1; +} + + + +void CWalletTx::AddSupportingTransactions(CTxDB& txdb) +{ + vtxPrev.clear(); + + const int COPY_DEPTH = 3; + if (SetMerkleBranch() < COPY_DEPTH) + { + vector vWorkQueue; + BOOST_FOREACH(const CTxIn& txin, vin) + vWorkQueue.push_back(txin.prevout.hash); + + // This critsect is OK because txdb is already open + CRITICAL_BLOCK(cs_mapWallet) + { + map mapWalletPrev; + set setAlreadyDone; + for (int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hash = vWorkQueue[i]; + if (setAlreadyDone.count(hash)) + continue; + setAlreadyDone.insert(hash); + + CMerkleTx tx; + if (mapWallet.count(hash)) + { + tx = mapWallet[hash]; + BOOST_FOREACH(const CMerkleTx& txWalletPrev, mapWallet[hash].vtxPrev) + mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; + } + else if (mapWalletPrev.count(hash)) + { + tx = *mapWalletPrev[hash]; + } + else if (!fClient && txdb.ReadDiskTx(hash, tx)) + { + ; + } + else + { + printf("ERROR: AddSupportingTransactions() : unsupported transaction\n"); + continue; + } + + int nDepth = tx.SetMerkleBranch(); + vtxPrev.push_back(tx); + + if (nDepth < COPY_DEPTH) + BOOST_FOREACH(const CTxIn& txin, tx.vin) + vWorkQueue.push_back(txin.prevout.hash); + } + } + } + + reverse(vtxPrev.begin(), vtxPrev.end()); +} + + + + + + + + + + + +bool CTransaction::CheckTransaction() const +{ + // Basic checks that don't depend on any context + if (vin.empty() || vout.empty()) + return error("CTransaction::CheckTransaction() : vin or vout empty"); + + // Size limits + if (::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE) + return error("CTransaction::CheckTransaction() : size limits failed"); + + // Check for negative or overflow output values + int64 nValueOut = 0; + BOOST_FOREACH(const CTxOut& txout, vout) + { + if (txout.nValue < 0) + return error("CTransaction::CheckTransaction() : txout.nValue negative"); + if (txout.nValue > MAX_MONEY) + return error("CTransaction::CheckTransaction() : txout.nValue too high"); + nValueOut += txout.nValue; + if (!MoneyRange(nValueOut)) + return error("CTransaction::CheckTransaction() : txout total out of range"); + } + + if (IsCoinBase()) + { + if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) + return error("CTransaction::CheckTransaction() : coinbase script size"); + } + else + { + BOOST_FOREACH(const CTxIn& txin, vin) + if (txin.prevout.IsNull()) + return error("CTransaction::CheckTransaction() : prevout is null"); + } + + return true; +} + +bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs) +{ + if (pfMissingInputs) + *pfMissingInputs = false; + + if (!CheckTransaction()) + return error("AcceptToMemoryPool() : CheckTransaction failed"); + + // Coinbase is only valid in a block, not as a loose transaction + if (IsCoinBase()) + return error("AcceptToMemoryPool() : coinbase as individual tx"); + + // To help v0.1.5 clients who would see it as a negative number + if ((int64)nLockTime > INT_MAX) + return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet"); + + // Safety limits + unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK); + // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service + // attacks disallow transactions with more than one SigOp per 34 bytes. + // 34 bytes because a TxOut is: + // 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length + if (GetSigOpCount() > nSize / 34 || nSize < 100) + return error("AcceptToMemoryPool() : nonstandard transaction"); + + // Rather not work on nonstandard transactions (unless -testnet) + if (!fTestNet && !IsStandard()) + return error("AcceptToMemoryPool() : nonstandard transaction type"); + + // Do we already have it? + uint256 hash = GetHash(); + CRITICAL_BLOCK(cs_mapTransactions) + if (mapTransactions.count(hash)) + return false; + if (fCheckInputs) + if (txdb.ContainsTx(hash)) + return false; + + // Check for conflicts with in-memory transactions + CTransaction* ptxOld = NULL; + for (int i = 0; i < vin.size(); i++) + { + COutPoint outpoint = vin[i].prevout; + if (mapNextTx.count(outpoint)) + { + // Disable replacement feature for now + return false; + + // Allow replacing with a newer version of the same transaction + if (i != 0) + return false; + ptxOld = mapNextTx[outpoint].ptx; + if (ptxOld->IsFinal()) + return false; + if (!IsNewerThan(*ptxOld)) + return false; + for (int i = 0; i < vin.size(); i++) + { + COutPoint outpoint = vin[i].prevout; + if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld) + return false; + } + break; + } + } + + if (fCheckInputs) + { + // Check against previous transactions + map mapUnused; + int64 nFees = 0; + if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false)) + { + if (pfMissingInputs) + *pfMissingInputs = true; + return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); + } + + // Don't accept it if it can't get into a block + if (nFees < GetMinFee(1000)) + return error("AcceptToMemoryPool() : not enough fees"); + + // Continuously rate-limit free transactions + // This mitigates 'penny-flooding' -- sending thousands of free transactions just to + // be annoying or make other's transactions take longer to confirm. + if (nFees < MIN_TX_FEE) + { + static CCriticalSection cs; + static double dFreeCount; + static int64 nLastTime; + int64 nNow = GetTime(); + + CRITICAL_BLOCK(cs) + { + // Use an exponentially decaying ~10-minute window: + dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); + nLastTime = nNow; + // -limitfreerelay unit is thousand-bytes-per-minute + // At default rate it would take over a month to fill 1GB + if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe()) + return error("AcceptToMemoryPool() : free transaction rejected by rate limiter"); + if (fDebug) + printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + dFreeCount += nSize; + } + } + } + + // Store transaction in memory + CRITICAL_BLOCK(cs_mapTransactions) + { + if (ptxOld) + { + printf("AcceptToMemoryPool() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); + ptxOld->RemoveFromMemoryPool(); + } + AddToMemoryPoolUnchecked(); + } + + ///// are we sure this is ok when loading transactions or restoring block txes + // If updated, erase old tx from wallet + if (ptxOld) + EraseFromWallet(ptxOld->GetHash()); + + printf("AcceptToMemoryPool(): accepted %s\n", hash.ToString().substr(0,10).c_str()); + return true; +} + + +bool CTransaction::AddToMemoryPoolUnchecked() +{ + // Add to memory pool without checking anything. Don't call this directly, + // call AcceptToMemoryPool to properly check the transaction first. + CRITICAL_BLOCK(cs_mapTransactions) + { + uint256 hash = GetHash(); + mapTransactions[hash] = *this; + for (int i = 0; i < vin.size(); i++) + mapNextTx[vin[i].prevout] = CInPoint(&mapTransactions[hash], i); + nTransactionsUpdated++; + } + return true; +} + + +bool CTransaction::RemoveFromMemoryPool() +{ + // Remove transaction from memory pool + CRITICAL_BLOCK(cs_mapTransactions) + { + BOOST_FOREACH(const CTxIn& txin, vin) + mapNextTx.erase(txin.prevout); + mapTransactions.erase(GetHash()); + nTransactionsUpdated++; + } + return true; +} + + + + + + +int CMerkleTx::GetDepthInMainChain(int& nHeightRet) const +{ + if (hashBlock == 0 || nIndex == -1) + return 0; + + // Find the block it claims to be in + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !pindex->IsInMainChain()) + return 0; + + // Make sure the merkle branch connects to this block + if (!fMerkleVerified) + { + if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) + return 0; + fMerkleVerified = true; + } + + nHeightRet = pindex->nHeight; + return pindexBest->nHeight - pindex->nHeight + 1; +} + + +int CMerkleTx::GetBlocksToMaturity() const +{ + if (!IsCoinBase()) + return 0; + return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain()); +} + + +bool CMerkleTx::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs) +{ + if (fClient) + { + if (!IsInMainChain() && !ClientConnectInputs()) + return false; + return CTransaction::AcceptToMemoryPool(txdb, false); + } + else + { + return CTransaction::AcceptToMemoryPool(txdb, fCheckInputs); + } +} + + + +bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) +{ + CRITICAL_BLOCK(cs_mapTransactions) + { + // Add previous supporting transactions first + BOOST_FOREACH(CMerkleTx& tx, vtxPrev) + { + if (!tx.IsCoinBase()) + { + uint256 hash = tx.GetHash(); + if (!mapTransactions.count(hash) && !txdb.ContainsTx(hash)) + tx.AcceptToMemoryPool(txdb, fCheckInputs); + } + } + return AcceptToMemoryPool(txdb, fCheckInputs); + } + return false; +} + +int ScanForWalletTransactions(CBlockIndex* pindexStart) +{ + int ret = 0; + + CBlockIndex* pindex = pindexStart; + CRITICAL_BLOCK(cs_mapWallet) + { + while (pindex) + { + CBlock block; + block.ReadFromDisk(pindex, true); + BOOST_FOREACH(CTransaction& tx, block.vtx) + { + if (AddToWalletIfInvolvingMe(tx, &block)) + ret++; + } + pindex = pindex->pnext; + } + } + return ret; +} + +void ReacceptWalletTransactions() +{ + CTxDB txdb("r"); + bool fRepeat = true; + while (fRepeat) CRITICAL_BLOCK(cs_mapWallet) + { + fRepeat = false; + vector vMissingTx; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + if (wtx.IsCoinBase() && wtx.IsSpent(0)) + continue; + + CTxIndex txindex; + bool fUpdated = false; + if (txdb.ReadTxIndex(wtx.GetHash(), txindex)) + { + // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat + if (txindex.vSpent.size() != wtx.vout.size()) + { + printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size()); + continue; + } + for (int i = 0; i < txindex.vSpent.size(); i++) + { + if (wtx.IsSpent(i)) + continue; + if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine()) + { + wtx.MarkSpent(i); + fUpdated = true; + vMissingTx.push_back(txindex.vSpent[i]); + } + } + if (fUpdated) + { + printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + wtx.MarkDirty(); + wtx.WriteToDisk(); + } + } + else + { + // Reaccept any txes of ours that aren't already in a block + if (!wtx.IsCoinBase()) + wtx.AcceptWalletTransaction(txdb, false); + } + } + if (!vMissingTx.empty()) + { + // TODO: optimize this to scan just part of the block chain? + if (ScanForWalletTransactions(pindexGenesisBlock)) + fRepeat = true; // Found missing transactions: re-do Reaccept. + } + } +} + + +void CWalletTx::RelayWalletTransaction(CTxDB& txdb) +{ + BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) + { + if (!tx.IsCoinBase()) + { + uint256 hash = tx.GetHash(); + if (!txdb.ContainsTx(hash)) + RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); + } + } + if (!IsCoinBase()) + { + uint256 hash = GetHash(); + if (!txdb.ContainsTx(hash)) + { + printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); + RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); + } + } +} + +void ResendWalletTransactions() +{ + // Do this infrequently and randomly to avoid giving away + // that these are our transactions. + static int64 nNextTime; + if (GetTime() < nNextTime) + return; + bool fFirst = (nNextTime == 0); + nNextTime = GetTime() + GetRand(30 * 60); + if (fFirst) + return; + + // Only do it if there's been a new block since last time + static int64 nLastTime; + if (nTimeBestReceived < nLastTime) + return; + nLastTime = GetTime(); + + // Rebroadcast any of our txes that aren't in a block yet + printf("ResendWalletTransactions()\n"); + CTxDB txdb("r"); + CRITICAL_BLOCK(cs_mapWallet) + { + // Sort them in chronological order + multimap mapSorted; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + // Don't rebroadcast until it's had plenty of time that + // it should have gotten in already by now. + if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) + mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); + } + BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) + { + CWalletTx& wtx = *item.second; + wtx.RelayWalletTransaction(txdb); + } + } +} + +int CTxIndex::GetDepthInMainChain() const +{ + // Read block header + CBlock block; + if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false)) + return 0; + // Find the block in the index + map::iterator mi = mapBlockIndex.find(block.GetHash()); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !pindex->IsInMainChain()) + return 0; + return 1 + nBestHeight - pindex->nHeight; +} + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CBlock and CBlockIndex +// + +bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions) +{ + if (!fReadTransactions) + { + *this = pindex->GetBlockHeader(); + return true; + } + if (!ReadFromDisk(pindex->nFile, pindex->nBlockPos, fReadTransactions)) + return false; + if (GetHash() != pindex->GetBlockHash()) + return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); + return true; +} + +uint256 GetOrphanRoot(const CBlock* pblock) +{ + // Work back to the first block in the orphan chain + while (mapOrphanBlocks.count(pblock->hashPrevBlock)) + pblock = mapOrphanBlocks[pblock->hashPrevBlock]; + return pblock->GetHash(); +} + +int64 GetBlockValue(int nHeight, int64 nFees) +{ + int64 nSubsidy = 50 * COIN; + + // Subsidy is cut in half every 4 years + nSubsidy >>= (nHeight / 210000); + + return nSubsidy + nFees; +} + +unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast) +{ + const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks + const int64 nTargetSpacing = 10 * 60; + const int64 nInterval = nTargetTimespan / nTargetSpacing; + + // Genesis block + if (pindexLast == NULL) + return bnProofOfWorkLimit.GetCompact(); + + // Only change once per interval + if ((pindexLast->nHeight+1) % nInterval != 0) + return pindexLast->nBits; + + // Go back by what we want to be 14 days worth of blocks + const CBlockIndex* pindexFirst = pindexLast; + for (int i = 0; pindexFirst && i < nInterval-1; i++) + pindexFirst = pindexFirst->pprev; + assert(pindexFirst); + + // Limit adjustment step + int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime(); + printf(" nActualTimespan = %"PRI64d" before bounds\n", nActualTimespan); + if (nActualTimespan < nTargetTimespan/4) + nActualTimespan = nTargetTimespan/4; + if (nActualTimespan > nTargetTimespan*4) + nActualTimespan = nTargetTimespan*4; + + // Retarget + CBigNum bnNew; + bnNew.SetCompact(pindexLast->nBits); + bnNew *= nActualTimespan; + bnNew /= nTargetTimespan; + + if (bnNew > bnProofOfWorkLimit) + bnNew = bnProofOfWorkLimit; + + /// debug print + printf("GetNextWorkRequired RETARGET\n"); + printf("nTargetTimespan = %"PRI64d" nActualTimespan = %"PRI64d"\n", nTargetTimespan, nActualTimespan); + printf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str()); + printf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str()); + + return bnNew.GetCompact(); +} + +bool CheckProofOfWork(uint256 hash, unsigned int nBits) +{ + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + + // Check range + if (bnTarget <= 0 || bnTarget > bnProofOfWorkLimit) + return error("CheckProofOfWork() : nBits below minimum work"); + + // Check proof of work matches claimed amount + if (hash > bnTarget.getuint256()) + return error("CheckProofOfWork() : hash doesn't match nBits"); + + return true; +} + +bool IsInitialBlockDownload() +{ + if (pindexBest == NULL || (!fTestNet && nBestHeight < 118000)) + return true; + static int64 nLastUpdate; + static CBlockIndex* pindexLastBest; + if (pindexBest != pindexLastBest) + { + pindexLastBest = pindexBest; + nLastUpdate = GetTime(); + } + return (GetTime() - nLastUpdate < 10 && + pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); +} + +void InvalidChainFound(CBlockIndex* pindexNew) +{ + if (pindexNew->bnChainWork > bnBestInvalidWork) + { + bnBestInvalidWork = pindexNew->bnChainWork; + CTxDB().WriteBestInvalidWork(bnBestInvalidWork); + MainFrameRepaint(); + } + printf("InvalidChainFound: invalid block=%s height=%d work=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, pindexNew->bnChainWork.ToString().c_str()); + printf("InvalidChainFound: current best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str()); + if (pindexBest && bnBestInvalidWork > bnBestChainWork + pindexBest->GetBlockWork() * 6) + printf("InvalidChainFound: WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); +} + + + + + + + + + + + +bool CTransaction::DisconnectInputs(CTxDB& txdb) +{ + // Relinquish previous transactions' spent pointers + if (!IsCoinBase()) + { + BOOST_FOREACH(const CTxIn& txin, vin) + { + COutPoint prevout = txin.prevout; + + // Get prev txindex from disk + CTxIndex txindex; + if (!txdb.ReadTxIndex(prevout.hash, txindex)) + return error("DisconnectInputs() : ReadTxIndex failed"); + + if (prevout.n >= txindex.vSpent.size()) + return error("DisconnectInputs() : prevout.n out of range"); + + // Mark outpoint as not spent + txindex.vSpent[prevout.n].SetNull(); + + // Write back + if (!txdb.UpdateTxIndex(prevout.hash, txindex)) + return error("DisconnectInputs() : UpdateTxIndex failed"); + } + } + + // Remove transaction from index + if (!txdb.EraseTxIndex(*this)) + return error("DisconnectInputs() : EraseTxPos failed"); + + return true; +} + + +bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPool, CDiskTxPos posThisTx, + CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee) +{ + // Take over previous transactions' spent pointers + if (!IsCoinBase()) + { + int64 nValueIn = 0; + for (int i = 0; i < vin.size(); i++) + { + COutPoint prevout = vin[i].prevout; + + // Read txindex + CTxIndex txindex; + bool fFound = true; + if (fMiner && mapTestPool.count(prevout.hash)) + { + // Get txindex from current proposed changes + txindex = mapTestPool[prevout.hash]; + } + else + { + // Read txindex from txdb + fFound = txdb.ReadTxIndex(prevout.hash, txindex); + } + if (!fFound && (fBlock || fMiner)) + return fMiner ? false : error("ConnectInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + + // Read txPrev + CTransaction txPrev; + if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) + { + // Get prev tx from single transactions in memory + CRITICAL_BLOCK(cs_mapTransactions) + { + if (!mapTransactions.count(prevout.hash)) + return error("ConnectInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + txPrev = mapTransactions[prevout.hash]; + } + if (!fFound) + txindex.vSpent.resize(txPrev.vout.size()); + } + else + { + // Get prev tx from disk + if (!txPrev.ReadFromDisk(txindex.pos)) + return error("ConnectInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + } + + if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) + return error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()); + + // If prev is coinbase, check that it's matured + if (txPrev.IsCoinBase()) + for (CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev) + if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) + return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight); + + // Verify signature + if (!VerifySignature(txPrev, *this, i)) + return error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); + + // Check for conflicts + if (!txindex.vSpent[prevout.n].IsNull()) + return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str()); + + // Check for negative or overflow input values + nValueIn += txPrev.vout[prevout.n].nValue; + if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) + return error("ConnectInputs() : txin values out of range"); + + // Mark outpoints as spent + txindex.vSpent[prevout.n] = posThisTx; + + // Write back + if (fBlock) + { + if (!txdb.UpdateTxIndex(prevout.hash, txindex)) + return error("ConnectInputs() : UpdateTxIndex failed"); + } + else if (fMiner) + { + mapTestPool[prevout.hash] = txindex; + } + } + + if (nValueIn < GetValueOut()) + return error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()); + + // Tally transaction fees + int64 nTxFee = nValueIn - GetValueOut(); + if (nTxFee < 0) + return error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()); + if (nTxFee < nMinFee) + return false; + nFees += nTxFee; + if (!MoneyRange(nFees)) + return error("ConnectInputs() : nFees out of range"); + } + + if (fBlock) + { + // Add transaction to disk index + if (!txdb.AddTxIndex(*this, posThisTx, pindexBlock->nHeight)) + return error("ConnectInputs() : AddTxPos failed"); + } + else if (fMiner) + { + // Add transaction to test pool + mapTestPool[GetHash()] = CTxIndex(CDiskTxPos(1,1,1), vout.size()); + } + + return true; +} + + +bool CTransaction::ClientConnectInputs() +{ + if (IsCoinBase()) + return false; + + // Take over previous transactions' spent pointers + CRITICAL_BLOCK(cs_mapTransactions) + { + int64 nValueIn = 0; + for (int i = 0; i < vin.size(); i++) + { + // Get prev tx from single transactions in memory + COutPoint prevout = vin[i].prevout; + if (!mapTransactions.count(prevout.hash)) + return false; + CTransaction& txPrev = mapTransactions[prevout.hash]; + + if (prevout.n >= txPrev.vout.size()) + return false; + + // Verify signature + if (!VerifySignature(txPrev, *this, i)) + return error("ConnectInputs() : VerifySignature failed"); + + ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of + ///// this has to go away now that posNext is gone + // // Check for conflicts + // if (!txPrev.vout[prevout.n].posNext.IsNull()) + // return error("ConnectInputs() : prev tx already used"); + // + // // Flag outpoints as used + // txPrev.vout[prevout.n].posNext = posThisTx; + + nValueIn += txPrev.vout[prevout.n].nValue; + + if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) + return error("ClientConnectInputs() : txin values out of range"); + } + if (GetValueOut() > nValueIn) + return false; + } + + return true; +} + + + + +bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) +{ + // Disconnect in reverse order + for (int i = vtx.size()-1; i >= 0; i--) + if (!vtx[i].DisconnectInputs(txdb)) + return false; + + // Update block index on disk without changing it in memory. + // The memory index structure will be changed after the db commits. + if (pindex->pprev) + { + CDiskBlockIndex blockindexPrev(pindex->pprev); + blockindexPrev.hashNext = 0; + if (!txdb.WriteBlockIndex(blockindexPrev)) + return error("DisconnectBlock() : WriteBlockIndex failed"); + } + + return true; +} + +bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) +{ + // Check it again in case a previous version let a bad block in + if (!CheckBlock()) + return false; + + //// issue here: it doesn't know the version + unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size()); + + map mapUnused; + int64 nFees = 0; + BOOST_FOREACH(CTransaction& tx, vtx) + { + CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); + nTxPos += ::GetSerializeSize(tx, SER_DISK); + + if (!tx.ConnectInputs(txdb, mapUnused, posThisTx, pindex, nFees, true, false)) + return false; + } + + if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) + return false; + + // Update block index on disk without changing it in memory. + // The memory index structure will be changed after the db commits. + if (pindex->pprev) + { + CDiskBlockIndex blockindexPrev(pindex->pprev); + blockindexPrev.hashNext = pindex->GetBlockHash(); + if (!txdb.WriteBlockIndex(blockindexPrev)) + return error("ConnectBlock() : WriteBlockIndex failed"); + } + + // Watch for transactions paying to me + BOOST_FOREACH(CTransaction& tx, vtx) + AddToWalletIfInvolvingMe(tx, this, true); + + return true; +} + +bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) +{ + printf("REORGANIZE\n"); + + // Find the fork + CBlockIndex* pfork = pindexBest; + CBlockIndex* plonger = pindexNew; + while (pfork != plonger) + { + while (plonger->nHeight > pfork->nHeight) + if (!(plonger = plonger->pprev)) + return error("Reorganize() : plonger->pprev is null"); + if (pfork == plonger) + break; + if (!(pfork = pfork->pprev)) + return error("Reorganize() : pfork->pprev is null"); + } + + // List of what to disconnect + vector vDisconnect; + for (CBlockIndex* pindex = pindexBest; pindex != pfork; pindex = pindex->pprev) + vDisconnect.push_back(pindex); + + // List of what to connect + vector vConnect; + for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev) + vConnect.push_back(pindex); + reverse(vConnect.begin(), vConnect.end()); + + // Disconnect shorter branch + vector vResurrect; + BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) + { + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("Reorganize() : ReadFromDisk for disconnect failed"); + if (!block.DisconnectBlock(txdb, pindex)) + return error("Reorganize() : DisconnectBlock failed"); + + // Queue memory transactions to resurrect + BOOST_FOREACH(const CTransaction& tx, block.vtx) + if (!tx.IsCoinBase()) + vResurrect.push_back(tx); + } + + // Connect longer branch + vector vDelete; + for (int i = 0; i < vConnect.size(); i++) + { + CBlockIndex* pindex = vConnect[i]; + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("Reorganize() : ReadFromDisk for connect failed"); + if (!block.ConnectBlock(txdb, pindex)) + { + // Invalid block + txdb.TxnAbort(); + return error("Reorganize() : ConnectBlock failed"); + } + + // Queue memory transactions to delete + BOOST_FOREACH(const CTransaction& tx, block.vtx) + vDelete.push_back(tx); + } + if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash())) + return error("Reorganize() : WriteHashBestChain failed"); + + // Make sure it's successfully written to disk before changing memory structure + if (!txdb.TxnCommit()) + return error("Reorganize() : TxnCommit failed"); + + // Disconnect shorter branch + BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) + if (pindex->pprev) + pindex->pprev->pnext = NULL; + + // Connect longer branch + BOOST_FOREACH(CBlockIndex* pindex, vConnect) + if (pindex->pprev) + pindex->pprev->pnext = pindex; + + // Resurrect memory transactions that were in the disconnected branch + BOOST_FOREACH(CTransaction& tx, vResurrect) + tx.AcceptToMemoryPool(txdb, false); + + // Delete redundant memory transactions that are in the connected branch + BOOST_FOREACH(CTransaction& tx, vDelete) + tx.RemoveFromMemoryPool(); + + return true; +} + + +bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) +{ + uint256 hash = GetHash(); + + txdb.TxnBegin(); + if (pindexGenesisBlock == NULL && hash == hashGenesisBlock) + { + txdb.WriteHashBestChain(hash); + if (!txdb.TxnCommit()) + return error("SetBestChain() : TxnCommit failed"); + pindexGenesisBlock = pindexNew; + } + else if (hashPrevBlock == hashBestChain) + { + // Adding to current best branch + if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash)) + { + txdb.TxnAbort(); + InvalidChainFound(pindexNew); + return error("SetBestChain() : ConnectBlock failed"); + } + if (!txdb.TxnCommit()) + return error("SetBestChain() : TxnCommit failed"); + + // Add to current best branch + pindexNew->pprev->pnext = pindexNew; + + // Delete redundant memory transactions + BOOST_FOREACH(CTransaction& tx, vtx) + tx.RemoveFromMemoryPool(); + } + else + { + // New best branch + if (!Reorganize(txdb, pindexNew)) + { + txdb.TxnAbort(); + InvalidChainFound(pindexNew); + return error("SetBestChain() : Reorganize failed"); + } + } + + // Update best block in wallet (so we can detect restored wallets) + if (!IsInitialBlockDownload()) + { + CWalletDB walletdb; + const CBlockLocator locator(pindexNew); + if (!walletdb.WriteBestBlock(locator)) + return error("SetBestChain() : WriteWalletBest failed"); + } + + // New best block + hashBestChain = hash; + pindexBest = pindexNew; + nBestHeight = pindexBest->nHeight; + bnBestChainWork = pindexNew->bnChainWork; + nTimeBestReceived = GetTime(); + nTransactionsUpdated++; + printf("SetBestChain: new best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str()); + + return true; +} + + +bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) +{ + // Check for duplicate + uint256 hash = GetHash(); + if (mapBlockIndex.count(hash)) + return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,20).c_str()); + + // Construct new block index object + CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this); + if (!pindexNew) + return error("AddToBlockIndex() : new CBlockIndex failed"); + map::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + pindexNew->phashBlock = &((*mi).first); + map::iterator miPrev = mapBlockIndex.find(hashPrevBlock); + if (miPrev != mapBlockIndex.end()) + { + pindexNew->pprev = (*miPrev).second; + pindexNew->nHeight = pindexNew->pprev->nHeight + 1; + } + pindexNew->bnChainWork = (pindexNew->pprev ? pindexNew->pprev->bnChainWork : 0) + pindexNew->GetBlockWork(); + + CTxDB txdb; + txdb.TxnBegin(); + txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew)); + if (!txdb.TxnCommit()) + return false; + + // New best + if (pindexNew->bnChainWork > bnBestChainWork) + if (!SetBestChain(txdb, pindexNew)) + return false; + + txdb.Close(); + + if (pindexNew == pindexBest) + { + // Notify UI to display prev block's coinbase if it was ours + static uint256 hashPrevBestCoinBase; + CRITICAL_BLOCK(cs_mapWallet) + vWalletUpdated.push_back(hashPrevBestCoinBase); + hashPrevBestCoinBase = vtx[0].GetHash(); + } + + MainFrameRepaint(); + return true; +} + + + + +bool CBlock::CheckBlock() const +{ + // These are checks that are independent of context + // that can be verified before saving an orphan block. + + // Size limits + if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE) + return error("CheckBlock() : size limits failed"); + + // Check proof of work matches claimed amount + if (!CheckProofOfWork(GetHash(), nBits)) + return error("CheckBlock() : proof of work failed"); + + // Check timestamp + if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) + return error("CheckBlock() : block timestamp too far in the future"); + + // First transaction must be coinbase, the rest must not be + if (vtx.empty() || !vtx[0].IsCoinBase()) + return error("CheckBlock() : first tx is not coinbase"); + for (int i = 1; i < vtx.size(); i++) + if (vtx[i].IsCoinBase()) + return error("CheckBlock() : more than one coinbase"); + + // Check transactions + BOOST_FOREACH(const CTransaction& tx, vtx) + if (!tx.CheckTransaction()) + return error("CheckBlock() : CheckTransaction failed"); + + // Check that it's not full of nonstandard transactions + if (GetSigOpCount() > MAX_BLOCK_SIGOPS) + return error("CheckBlock() : too many nonstandard transactions"); + + // Check merkleroot + if (hashMerkleRoot != BuildMerkleTree()) + return error("CheckBlock() : hashMerkleRoot mismatch"); + + return true; +} + +bool CBlock::AcceptBlock() +{ + // Check for duplicate + uint256 hash = GetHash(); + if (mapBlockIndex.count(hash)) + return error("AcceptBlock() : block already in mapBlockIndex"); + + // Get prev block index + map::iterator mi = mapBlockIndex.find(hashPrevBlock); + if (mi == mapBlockIndex.end()) + return error("AcceptBlock() : prev block not found"); + CBlockIndex* pindexPrev = (*mi).second; + int nHeight = pindexPrev->nHeight+1; + + // Check proof of work + if (nBits != GetNextWorkRequired(pindexPrev)) + return error("AcceptBlock() : incorrect proof of work"); + + // Check timestamp against prev + if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) + return error("AcceptBlock() : block's timestamp is too early"); + + // Check that all transactions are finalized + BOOST_FOREACH(const CTransaction& tx, vtx) + if (!tx.IsFinal(nHeight, GetBlockTime())) + return error("AcceptBlock() : contains a non-final transaction"); + + // Check that the block chain matches the known block chain up to a checkpoint + if (!fTestNet) + if ((nHeight == 11111 && hash != uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")) || + (nHeight == 33333 && hash != uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")) || + (nHeight == 68555 && hash != uint256("0x00000000001e1b4903550a0b96e9a9405c8a95f387162e4944e8d9fbe501cd6a")) || + (nHeight == 70567 && hash != uint256("0x00000000006a49b14bcf27462068f1264c961f11fa2e0eddd2be0791e1d4124a")) || + (nHeight == 74000 && hash != uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")) || + (nHeight == 105000 && hash != uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")) || + (nHeight == 118000 && hash != uint256("0x000000000000774a7f8a7a12dc906ddb9e17e75d684f15e00f8767f9e8f36553"))) + return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight); + + // Write block to history file + if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK))) + return error("AcceptBlock() : out of disk space"); + unsigned int nFile = -1; + unsigned int nBlockPos = 0; + if (!WriteToDisk(nFile, nBlockPos)) + return error("AcceptBlock() : WriteToDisk failed"); + if (!AddToBlockIndex(nFile, nBlockPos)) + return error("AcceptBlock() : AddToBlockIndex failed"); + + // Relay inventory, but don't relay old inventory during initial block download + if (hashBestChain == hash) + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 118000)) + pnode->PushInventory(CInv(MSG_BLOCK, hash)); + + return true; +} + +bool ProcessBlock(CNode* pfrom, CBlock* pblock) +{ + // Check for duplicate + uint256 hash = pblock->GetHash(); + if (mapBlockIndex.count(hash)) + return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().substr(0,20).c_str()); + if (mapOrphanBlocks.count(hash)) + return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str()); + + // Preliminary checks + if (!pblock->CheckBlock()) + return error("ProcessBlock() : CheckBlock FAILED"); + + // If don't already have its previous block, shunt it off to holding area until we get it + if (!mapBlockIndex.count(pblock->hashPrevBlock)) + { + printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str()); + CBlock* pblock2 = new CBlock(*pblock); + mapOrphanBlocks.insert(make_pair(hash, pblock2)); + mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); + + // Ask this guy to fill in what we're missing + if (pfrom) + pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2)); + return true; + } + + // Store to disk + if (!pblock->AcceptBlock()) + return error("ProcessBlock() : AcceptBlock FAILED"); + + // Recursively process any orphan blocks that depended on this one + vector vWorkQueue; + vWorkQueue.push_back(hash); + for (int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hashPrev = vWorkQueue[i]; + for (multimap::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev); + mi != mapOrphanBlocksByPrev.upper_bound(hashPrev); + ++mi) + { + CBlock* pblockOrphan = (*mi).second; + if (pblockOrphan->AcceptBlock()) + vWorkQueue.push_back(pblockOrphan->GetHash()); + mapOrphanBlocks.erase(pblockOrphan->GetHash()); + delete pblockOrphan; + } + mapOrphanBlocksByPrev.erase(hashPrev); + } + + printf("ProcessBlock: ACCEPTED\n"); + return true; +} + + + + + + + + +template +bool ScanMessageStart(Stream& s) +{ + // Scan ahead to the next pchMessageStart, which should normally be immediately + // at the file pointer. Leaves file pointer at end of pchMessageStart. + s.clear(0); + short prevmask = s.exceptions(0); + const char* p = BEGIN(pchMessageStart); + try + { + loop + { + char c; + s.read(&c, 1); + if (s.fail()) + { + s.clear(0); + s.exceptions(prevmask); + return false; + } + if (*p != c) + p = BEGIN(pchMessageStart); + if (*p == c) + { + if (++p == END(pchMessageStart)) + { + s.clear(0); + s.exceptions(prevmask); + return true; + } + } + } + } + catch (...) + { + s.clear(0); + s.exceptions(prevmask); + return false; + } +} + +bool CheckDiskSpace(uint64 nAdditionalBytes) +{ + uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available; + + // Check for 15MB because database could create another 10MB log file at any time + if (nFreeBytesAvailable < (uint64)15000000 + nAdditionalBytes) + { + fShutdown = true; + string strMessage = _("Warning: Disk space is low "); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + ThreadSafeMessageBox(strMessage, "Bitcoin", wxOK | wxICON_EXCLAMATION); + CreateThread(Shutdown, NULL); + return false; + } + return true; +} + +FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode) +{ + if (nFile == -1) + return NULL; + FILE* file = fopen(strprintf("%s/blk%04d.dat", GetDataDir().c_str(), nFile).c_str(), pszMode); + if (!file) + return NULL; + if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w')) + { + if (fseek(file, nBlockPos, SEEK_SET) != 0) + { + fclose(file); + return NULL; + } + } + return file; +} + +static unsigned int nCurrentBlockFile = 1; + +FILE* AppendBlockFile(unsigned int& nFileRet) +{ + nFileRet = 0; + loop + { + FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab"); + if (!file) + return NULL; + if (fseek(file, 0, SEEK_END) != 0) + return NULL; + // FAT32 filesize max 4GB, fseek and ftell max 2GB, so we must stay under 2GB + if (ftell(file) < 0x7F000000 - MAX_SIZE) + { + nFileRet = nCurrentBlockFile; + return file; + } + fclose(file); + nCurrentBlockFile++; + } +} + +bool LoadBlockIndex(bool fAllowNew) +{ + if (fTestNet) + { + hashGenesisBlock = uint256("0x00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"); + bnProofOfWorkLimit = CBigNum(~uint256(0) >> 28); + pchMessageStart[0] = 0xfa; + pchMessageStart[1] = 0xbf; + pchMessageStart[2] = 0xb5; + pchMessageStart[3] = 0xda; + } + + // + // Load block index + // + CTxDB txdb("cr"); + if (!txdb.LoadBlockIndex()) + return false; + txdb.Close(); + + // + // Init with genesis block + // + if (mapBlockIndex.empty()) + { + if (!fAllowNew) + return false; + + // Genesis Block: + // CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1) + // CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) + // CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73) + // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) + // vMerkleTree: 4a5e1e + + // Genesis block + const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"; + CTransaction txNew; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vout[0].nValue = 50 * COIN; + txNew.vout[0].scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; + CBlock block; + block.vtx.push_back(txNew); + block.hashPrevBlock = 0; + block.hashMerkleRoot = block.BuildMerkleTree(); + block.nVersion = 1; + block.nTime = 1231006505; + block.nBits = 0x1d00ffff; + block.nNonce = 2083236893; + + if (fTestNet) + { + block.nTime = 1296688602; + block.nBits = 0x1d07fff8; + block.nNonce = 384568319; + } + + //// debug print + printf("%s\n", block.GetHash().ToString().c_str()); + printf("%s\n", hashGenesisBlock.ToString().c_str()); + printf("%s\n", block.hashMerkleRoot.ToString().c_str()); + assert(block.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); + block.print(); + assert(block.GetHash() == hashGenesisBlock); + + // Start new block file + unsigned int nFile; + unsigned int nBlockPos; + if (!block.WriteToDisk(nFile, nBlockPos)) + return error("LoadBlockIndex() : writing genesis block to disk failed"); + if (!block.AddToBlockIndex(nFile, nBlockPos)) + return error("LoadBlockIndex() : genesis block not accepted"); + } + + return true; +} + + + +void PrintBlockTree() +{ + // precompute tree structure + map > mapNext; + for (map::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) + { + CBlockIndex* pindex = (*mi).second; + mapNext[pindex->pprev].push_back(pindex); + // test + //while (rand() % 3 == 0) + // mapNext[pindex->pprev].push_back(pindex); + } + + vector > vStack; + vStack.push_back(make_pair(0, pindexGenesisBlock)); + + int nPrevCol = 0; + while (!vStack.empty()) + { + int nCol = vStack.back().first; + CBlockIndex* pindex = vStack.back().second; + vStack.pop_back(); + + // print split or gap + if (nCol > nPrevCol) + { + for (int i = 0; i < nCol-1; i++) + printf("| "); + printf("|\\\n"); + } + else if (nCol < nPrevCol) + { + for (int i = 0; i < nCol; i++) + printf("| "); + printf("|\n"); + } + nPrevCol = nCol; + + // print columns + for (int i = 0; i < nCol; i++) + printf("| "); + + // print item + CBlock block; + block.ReadFromDisk(pindex); + printf("%d (%u,%u) %s %s tx %d", + pindex->nHeight, + pindex->nFile, + pindex->nBlockPos, + block.GetHash().ToString().substr(0,20).c_str(), + DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(), + block.vtx.size()); + + CRITICAL_BLOCK(cs_mapWallet) + { + if (mapWallet.count(block.vtx[0].GetHash())) + { + CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; + printf(" mine: %d %d %d", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); + } + } + printf("\n"); + + + // put the main timechain first + vector& vNext = mapNext[pindex]; + for (int i = 0; i < vNext.size(); i++) + { + if (vNext[i]->pnext) + { + swap(vNext[0], vNext[i]); + break; + } + } + + // iterate children + for (int i = 0; i < vNext.size(); i++) + vStack.push_back(make_pair(nCol+i, vNext[i])); + } +} + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CAlert +// + +map mapAlerts; +CCriticalSection cs_mapAlerts; + +string GetWarnings(string strFor) +{ + int nPriority = 0; + string strStatusBar; + string strRPC; + if (GetBoolArg("-testsafemode")) + strRPC = "test"; + + // Misc warnings like out of disk space and clock is wrong + if (strMiscWarning != "") + { + nPriority = 1000; + strStatusBar = strMiscWarning; + } + + // Longer invalid proof-of-work chain + if (pindexBest && bnBestInvalidWork > bnBestChainWork + pindexBest->GetBlockWork() * 6) + { + nPriority = 2000; + strStatusBar = strRPC = "WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade."; + } + + // Alerts + CRITICAL_BLOCK(cs_mapAlerts) + { + BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + { + const CAlert& alert = item.second; + if (alert.AppliesToMe() && alert.nPriority > nPriority) + { + nPriority = alert.nPriority; + strStatusBar = alert.strStatusBar; + } + } + } + + if (strFor == "statusbar") + return strStatusBar; + else if (strFor == "rpc") + return strRPC; + assert(("GetWarnings() : invalid parameter", false)); + return "error"; +} + +bool CAlert::ProcessAlert() +{ + if (!CheckSignature()) + return false; + if (!IsInEffect()) + return false; + + CRITICAL_BLOCK(cs_mapAlerts) + { + // Cancel previous alerts + for (map::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();) + { + const CAlert& alert = (*mi).second; + if (Cancels(alert)) + { + printf("cancelling alert %d\n", alert.nID); + mapAlerts.erase(mi++); + } + else if (!alert.IsInEffect()) + { + printf("expiring alert %d\n", alert.nID); + mapAlerts.erase(mi++); + } + else + mi++; + } + + // Check if this alert has been cancelled + BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + { + const CAlert& alert = item.second; + if (alert.Cancels(*this)) + { + printf("alert already cancelled by %d\n", alert.nID); + return false; + } + } + + // Add to mapAlerts + mapAlerts.insert(make_pair(GetHash(), *this)); + } + + printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe()); + MainFrameRepaint(); + return true; +} + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Messages +// + + +bool AlreadyHave(CTxDB& txdb, const CInv& inv) +{ + switch (inv.type) + { + case MSG_TX: return mapTransactions.count(inv.hash) || mapOrphanTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash); + case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash); + } + // Don't know what it is, just say we already got one + return true; +} + + + + +// The message start string is designed to be unlikely to occur in normal data. +// The characters are rarely used upper ascii, not valid as UTF-8, and produce +// a large 4-byte int at any alignment. +char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; + + +bool ProcessMessages(CNode* pfrom) +{ + CDataStream& vRecv = pfrom->vRecv; + if (vRecv.empty()) + return true; + //if (fDebug) + // printf("ProcessMessages(%u bytes)\n", vRecv.size()); + + // + // Message format + // (4) message start + // (12) command + // (4) size + // (4) checksum + // (x) data + // + + loop + { + // Scan for message start + CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart)); + int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader()); + if (vRecv.end() - pstart < nHeaderSize) + { + if (vRecv.size() > nHeaderSize) + { + printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n"); + vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); + } + break; + } + if (pstart - vRecv.begin() > 0) + printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", pstart - vRecv.begin()); + vRecv.erase(vRecv.begin(), pstart); + + // Read header + vector vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize); + CMessageHeader hdr; + vRecv >> hdr; + if (!hdr.IsValid()) + { + printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); + continue; + } + string strCommand = hdr.GetCommand(); + + // Message size + unsigned int nMessageSize = hdr.nMessageSize; + if (nMessageSize > MAX_SIZE) + { + printf("ProcessMessage(%s, %u bytes) : nMessageSize > MAX_SIZE\n", strCommand.c_str(), nMessageSize); + continue; + } + if (nMessageSize > vRecv.size()) + { + // Rewind and wait for rest of message + vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end()); + break; + } + + // Checksum + if (vRecv.GetVersion() >= 209) + { + uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + if (nChecksum != hdr.nChecksum) + { + printf("ProcessMessage(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", + strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); + continue; + } + } + + // Copy message to its own buffer + CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion); + vRecv.ignore(nMessageSize); + + // Process message + bool fRet = false; + try + { + CRITICAL_BLOCK(cs_main) + fRet = ProcessMessage(pfrom, strCommand, vMsg); + if (fShutdown) + return true; + } + catch (std::ios_base::failure& e) + { + if (strstr(e.what(), "end of data")) + { + // Allow exceptions from underlength message on vRecv + printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what()); + } + else if (strstr(e.what(), "size too large")) + { + // Allow exceptions from overlong size + printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); + } + else + { + PrintExceptionContinue(&e, "ProcessMessage()"); + } + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "ProcessMessage()"); + } catch (...) { + PrintExceptionContinue(NULL, "ProcessMessage()"); + } + + if (!fRet) + printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); + } + + vRecv.Compact(); + return true; +} + + + + +bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) +{ + static map > mapReuseKey; + RandAddSeedPerfmon(); + if (fDebug) + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); + printf("received: %s (%d bytes)\n", strCommand.c_str(), vRecv.size()); + if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) + { + printf("dropmessagestest DROPPING RECV MESSAGE\n"); + return true; + } + + + + + + if (strCommand == "version") + { + // Each connection can only send one version message + if (pfrom->nVersion != 0) + return false; + + int64 nTime; + CAddress addrMe; + CAddress addrFrom; + uint64 nNonce = 1; + vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; + if (pfrom->nVersion == 10300) + pfrom->nVersion = 300; + if (pfrom->nVersion >= 106 && !vRecv.empty()) + vRecv >> addrFrom >> nNonce; + if (pfrom->nVersion >= 106 && !vRecv.empty()) + vRecv >> pfrom->strSubVer; + if (pfrom->nVersion >= 209 && !vRecv.empty()) + vRecv >> pfrom->nStartingHeight; + + if (pfrom->nVersion == 0) + return false; + + // Disconnect if we connected to ourself + if (nNonce == nLocalHostNonce && nNonce > 1) + { + printf("connected to self at %s, disconnecting\n", pfrom->addr.ToString().c_str()); + pfrom->fDisconnect = true; + return true; + } + + // Be shy and don't send version until we hear + if (pfrom->fInbound) + pfrom->PushVersion(); + + pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); + + AddTimeData(pfrom->addr.ip, nTime); + + // Change version + if (pfrom->nVersion >= 209) + pfrom->PushMessage("verack"); + pfrom->vSend.SetVersion(min(pfrom->nVersion, VERSION)); + if (pfrom->nVersion < 209) + pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION)); + + if (!pfrom->fInbound) + { + // Advertise our address + if (addrLocalHost.IsRoutable() && !fUseProxy) + { + CAddress addr(addrLocalHost); + addr.nTime = GetAdjustedTime(); + pfrom->PushAddress(addr); + } + + // Get recent addresses + if (pfrom->nVersion >= 31402 || mapAddresses.size() < 1000) + { + pfrom->PushMessage("getaddr"); + pfrom->fGetAddr = true; + } + } + + // Ask the first connected node for block updates + static int nAskedForBlocks; + if (!pfrom->fClient && (nAskedForBlocks < 1 || vNodes.size() <= 1)) + { + nAskedForBlocks++; + pfrom->PushGetBlocks(pindexBest, uint256(0)); + } + + // Relay alerts + CRITICAL_BLOCK(cs_mapAlerts) + BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + item.second.RelayTo(pfrom); + + pfrom->fSuccessfullyConnected = true; + + printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight); + } + + + else if (pfrom->nVersion == 0) + { + // Must have a version message before anything else + return false; + } + + + else if (strCommand == "verack") + { + pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION)); + } + + + else if (strCommand == "addr") + { + vector vAddr; + vRecv >> vAddr; + + // Don't want addr from older versions unless seeding + if (pfrom->nVersion < 209) + return true; + if (pfrom->nVersion < 31402 && mapAddresses.size() > 1000) + return true; + if (vAddr.size() > 1000) + return error("message addr size() = %d", vAddr.size()); + + // Store the new addresses + int64 nNow = GetAdjustedTime(); + int64 nSince = nNow - 10 * 60; + BOOST_FOREACH(CAddress& addr, vAddr) + { + if (fShutdown) + return true; + // ignore IPv6 for now, since it isn't implemented anyway + if (!addr.IsIPv4()) + continue; + if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) + addr.nTime = nNow - 5 * 24 * 60 * 60; + AddAddress(addr, 2 * 60 * 60); + pfrom->AddAddressKnown(addr); + if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) + { + // Relay to a limited number of other nodes + CRITICAL_BLOCK(cs_vNodes) + { + // Use deterministic randomness to send to the same nodes for 24 hours + // at a time so the setAddrKnowns of the chosen nodes prevent repeats + static uint256 hashSalt; + if (hashSalt == 0) + RAND_bytes((unsigned char*)&hashSalt, sizeof(hashSalt)); + uint256 hashRand = hashSalt ^ (((int64)addr.ip)<<32) ^ ((GetTime()+addr.ip)/(24*60*60)); + hashRand = Hash(BEGIN(hashRand), END(hashRand)); + multimap mapMix; + BOOST_FOREACH(CNode* pnode, vNodes) + { + if (pnode->nVersion < 31402) + continue; + unsigned int nPointer; + memcpy(&nPointer, &pnode, sizeof(nPointer)); + uint256 hashKey = hashRand ^ nPointer; + hashKey = Hash(BEGIN(hashKey), END(hashKey)); + mapMix.insert(make_pair(hashKey, pnode)); + } + int nRelayNodes = 2; + for (multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) + ((*mi).second)->PushAddress(addr); + } + } + } + if (vAddr.size() < 1000) + pfrom->fGetAddr = false; + } + + + else if (strCommand == "inv") + { + vector vInv; + vRecv >> vInv; + if (vInv.size() > 50000) + return error("message inv size() = %d", vInv.size()); + + CTxDB txdb("r"); + BOOST_FOREACH(const CInv& inv, vInv) + { + if (fShutdown) + return true; + pfrom->AddInventoryKnown(inv); + + bool fAlreadyHave = AlreadyHave(txdb, inv); + printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); + + if (!fAlreadyHave) + pfrom->AskFor(inv); + else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) + pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); + + // Track requests for our stuff + CRITICAL_BLOCK(cs_mapRequestCount) + { + map::iterator mi = mapRequestCount.find(inv.hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } + } + } + + + else if (strCommand == "getdata") + { + vector vInv; + vRecv >> vInv; + if (vInv.size() > 50000) + return error("message getdata size() = %d", vInv.size()); + + BOOST_FOREACH(const CInv& inv, vInv) + { + if (fShutdown) + return true; + printf("received getdata for: %s\n", inv.ToString().c_str()); + + if (inv.type == MSG_BLOCK) + { + // Send block from disk + map::iterator mi = mapBlockIndex.find(inv.hash); + if (mi != mapBlockIndex.end()) + { + CBlock block; + block.ReadFromDisk((*mi).second); + pfrom->PushMessage("block", block); + + // Trigger them to send a getblocks request for the next batch of inventory + if (inv.hash == pfrom->hashContinue) + { + // Bypass PushInventory, this must send even if redundant, + // and we want it right after the last block so they don't + // wait for other stuff first. + vector vInv; + vInv.push_back(CInv(MSG_BLOCK, hashBestChain)); + pfrom->PushMessage("inv", vInv); + pfrom->hashContinue = 0; + } + } + } + else if (inv.IsKnownType()) + { + // Send stream from relay memory + CRITICAL_BLOCK(cs_mapRelay) + { + map::iterator mi = mapRelay.find(inv); + if (mi != mapRelay.end()) + pfrom->PushMessage(inv.GetCommand(), (*mi).second); + } + } + + // Track requests for our stuff + CRITICAL_BLOCK(cs_mapRequestCount) + { + map::iterator mi = mapRequestCount.find(inv.hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } + } + } + + + else if (strCommand == "getblocks") + { + CBlockLocator locator; + uint256 hashStop; + vRecv >> locator >> hashStop; + + // Find the last block the caller has in the main chain + CBlockIndex* pindex = locator.GetBlockIndex(); + + // Send the rest of the chain + if (pindex) + pindex = pindex->pnext; + int nLimit = 500 + locator.GetDistanceBack(); + printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit); + for (; pindex; pindex = pindex->pnext) + { + if (pindex->GetBlockHash() == hashStop) + { + printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); + break; + } + pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); + if (--nLimit <= 0) + { + // When this block is requested, we'll send an inv that'll make them + // getblocks the next batch of inventory. + printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); + pfrom->hashContinue = pindex->GetBlockHash(); + break; + } + } + } + + + else if (strCommand == "getheaders") + { + CBlockLocator locator; + uint256 hashStop; + vRecv >> locator >> hashStop; + + CBlockIndex* pindex = NULL; + if (locator.IsNull()) + { + // If locator is null, return the hashStop block + map::iterator mi = mapBlockIndex.find(hashStop); + if (mi == mapBlockIndex.end()) + return true; + pindex = (*mi).second; + } + else + { + // Find the last block the caller has in the main chain + pindex = locator.GetBlockIndex(); + if (pindex) + pindex = pindex->pnext; + } + + vector vHeaders; + int nLimit = 2000 + locator.GetDistanceBack(); + printf("getheaders %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit); + for (; pindex; pindex = pindex->pnext) + { + vHeaders.push_back(pindex->GetBlockHeader()); + if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) + break; + } + pfrom->PushMessage("headers", vHeaders); + } + + + else if (strCommand == "tx") + { + vector vWorkQueue; + CDataStream vMsg(vRecv); + CTransaction tx; + vRecv >> tx; + + CInv inv(MSG_TX, tx.GetHash()); + pfrom->AddInventoryKnown(inv); + + bool fMissingInputs = false; + if (tx.AcceptToMemoryPool(true, &fMissingInputs)) + { + AddToWalletIfInvolvingMe(tx, NULL, true); + RelayMessage(inv, vMsg); + mapAlreadyAskedFor.erase(inv); + vWorkQueue.push_back(inv.hash); + + // Recursively process any orphan transactions that depended on this one + for (int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hashPrev = vWorkQueue[i]; + for (multimap::iterator mi = mapOrphanTransactionsByPrev.lower_bound(hashPrev); + mi != mapOrphanTransactionsByPrev.upper_bound(hashPrev); + ++mi) + { + const CDataStream& vMsg = *((*mi).second); + CTransaction tx; + CDataStream(vMsg) >> tx; + CInv inv(MSG_TX, tx.GetHash()); + + if (tx.AcceptToMemoryPool(true)) + { + printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); + AddToWalletIfInvolvingMe(tx, NULL, true); + RelayMessage(inv, vMsg); + mapAlreadyAskedFor.erase(inv); + vWorkQueue.push_back(inv.hash); + } + } + } + + BOOST_FOREACH(uint256 hash, vWorkQueue) + EraseOrphanTx(hash); + } + else if (fMissingInputs) + { + printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); + AddOrphanTx(vMsg); + } + } + + + else if (strCommand == "block") + { + CBlock block; + vRecv >> block; + + printf("received block %s\n", block.GetHash().ToString().substr(0,20).c_str()); + // block.print(); + + CInv inv(MSG_BLOCK, block.GetHash()); + pfrom->AddInventoryKnown(inv); + + if (ProcessBlock(pfrom, &block)) + mapAlreadyAskedFor.erase(inv); + } + + + else if (strCommand == "getaddr") + { + // Nodes rebroadcast an addr every 24 hours + pfrom->vAddrToSend.clear(); + int64 nSince = GetAdjustedTime() - 3 * 60 * 60; // in the last 3 hours + CRITICAL_BLOCK(cs_mapAddresses) + { + unsigned int nCount = 0; + BOOST_FOREACH(const PAIRTYPE(vector, CAddress)& item, mapAddresses) + { + const CAddress& addr = item.second; + if (addr.nTime > nSince) + nCount++; + } + BOOST_FOREACH(const PAIRTYPE(vector, CAddress)& item, mapAddresses) + { + const CAddress& addr = item.second; + if (addr.nTime > nSince && GetRand(nCount) < 2500) + pfrom->PushAddress(addr); + } + } + } + + + else if (strCommand == "checkorder") + { + uint256 hashReply; + vRecv >> hashReply; + + if (!GetBoolArg("-allowreceivebyip")) + { + pfrom->PushMessage("reply", hashReply, (int)2, string("")); + return true; + } + + CWalletTx order; + vRecv >> order; + + /// we have a chance to check the order here + + // Keep giving the same key to the same ip until they use it + if (!mapReuseKey.count(pfrom->addr.ip)) + mapReuseKey[pfrom->addr.ip] = GetKeyFromKeyPool(); + + // Send back approval of order and pubkey to use + CScript scriptPubKey; + scriptPubKey << mapReuseKey[pfrom->addr.ip] << OP_CHECKSIG; + pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey); + } + + + else if (strCommand == "submitorder") + { + uint256 hashReply; + vRecv >> hashReply; + + if (!GetBoolArg("-allowreceivebyip")) + { + pfrom->PushMessage("reply", hashReply, (int)2); + return true; + } + + CWalletTx wtxNew; + vRecv >> wtxNew; + wtxNew.fFromMe = false; + + // Broadcast + if (!wtxNew.AcceptWalletTransaction()) + { + pfrom->PushMessage("reply", hashReply, (int)1); + return error("submitorder AcceptWalletTransaction() failed, returning error 1"); + } + wtxNew.fTimeReceivedIsTxTime = true; + AddToWallet(wtxNew); + wtxNew.RelayWalletTransaction(); + mapReuseKey.erase(pfrom->addr.ip); + + // Send back confirmation + pfrom->PushMessage("reply", hashReply, (int)0); + } + + + else if (strCommand == "reply") + { + uint256 hashReply; + vRecv >> hashReply; + + CRequestTracker tracker; + CRITICAL_BLOCK(pfrom->cs_mapRequests) + { + map::iterator mi = pfrom->mapRequests.find(hashReply); + if (mi != pfrom->mapRequests.end()) + { + tracker = (*mi).second; + pfrom->mapRequests.erase(mi); + } + } + if (!tracker.IsNull()) + tracker.fn(tracker.param1, vRecv); + } + + + else if (strCommand == "ping") + { + } + + + else if (strCommand == "alert") + { + CAlert alert; + vRecv >> alert; + + if (alert.ProcessAlert()) + { + // Relay + pfrom->setKnown.insert(alert.GetHash()); + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + alert.RelayTo(pnode); + } + } + + + else + { + // Ignore unknown commands for extensibility + } + + + // Update the last seen time for this node's address + if (pfrom->fNetworkNode) + if (strCommand == "version" || strCommand == "addr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping") + AddressCurrentlyConnected(pfrom->addr); + + + return true; +} + + + + + + + + + +bool SendMessages(CNode* pto, bool fSendTrickle) +{ + CRITICAL_BLOCK(cs_main) + { + // Don't send anything until we get their version message + if (pto->nVersion == 0) + return true; + + // Keep-alive ping + if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSend.empty()) + pto->PushMessage("ping"); + + // Resend wallet transactions that haven't gotten in a block yet + ResendWalletTransactions(); + + // Address refresh broadcast + static int64 nLastRebroadcast; + if (GetTime() - nLastRebroadcast > 24 * 60 * 60) + { + nLastRebroadcast = GetTime(); + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodes) + { + // Periodically clear setAddrKnown to allow refresh broadcasts + pnode->setAddrKnown.clear(); + + // Rebroadcast our address + if (addrLocalHost.IsRoutable() && !fUseProxy) + { + CAddress addr(addrLocalHost); + addr.nTime = GetAdjustedTime(); + pnode->PushAddress(addr); + } + } + } + } + + // Clear out old addresses periodically so it's not too much work at once + static int64 nLastClear; + if (nLastClear == 0) + nLastClear = GetTime(); + if (GetTime() - nLastClear > 10 * 60 && vNodes.size() >= 3) + { + nLastClear = GetTime(); + CRITICAL_BLOCK(cs_mapAddresses) + { + CAddrDB addrdb; + int64 nSince = GetAdjustedTime() - 14 * 24 * 60 * 60; + for (map, CAddress>::iterator mi = mapAddresses.begin(); + mi != mapAddresses.end();) + { + const CAddress& addr = (*mi).second; + if (addr.nTime < nSince) + { + if (mapAddresses.size() < 1000 || GetTime() > nLastClear + 20) + break; + addrdb.EraseAddress(addr); + mapAddresses.erase(mi++); + } + else + mi++; + } + } + } + + + // + // Message: addr + // + if (fSendTrickle) + { + vector vAddr; + vAddr.reserve(pto->vAddrToSend.size()); + BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend) + { + // returns true if wasn't already contained in the set + if (pto->setAddrKnown.insert(addr).second) + { + vAddr.push_back(addr); + // receiver rejects addr messages larger than 1000 + if (vAddr.size() >= 1000) + { + pto->PushMessage("addr", vAddr); + vAddr.clear(); + } + } + } + pto->vAddrToSend.clear(); + if (!vAddr.empty()) + pto->PushMessage("addr", vAddr); + } + + + // + // Message: inventory + // + vector vInv; + vector vInvWait; + CRITICAL_BLOCK(pto->cs_inventory) + { + vInv.reserve(pto->vInventoryToSend.size()); + vInvWait.reserve(pto->vInventoryToSend.size()); + BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) + { + if (pto->setInventoryKnown.count(inv)) + continue; + + // trickle out tx inv to protect privacy + if (inv.type == MSG_TX && !fSendTrickle) + { + // 1/4 of tx invs blast to all immediately + static uint256 hashSalt; + if (hashSalt == 0) + RAND_bytes((unsigned char*)&hashSalt, sizeof(hashSalt)); + uint256 hashRand = inv.hash ^ hashSalt; + hashRand = Hash(BEGIN(hashRand), END(hashRand)); + bool fTrickleWait = ((hashRand & 3) != 0); + + // always trickle our own transactions + if (!fTrickleWait) + { + TRY_CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(inv.hash); + if (mi != mapWallet.end()) + { + CWalletTx& wtx = (*mi).second; + if (wtx.fFromMe) + fTrickleWait = true; + } + } + } + + if (fTrickleWait) + { + vInvWait.push_back(inv); + continue; + } + } + + // returns true if wasn't already contained in the set + if (pto->setInventoryKnown.insert(inv).second) + { + vInv.push_back(inv); + if (vInv.size() >= 1000) + { + pto->PushMessage("inv", vInv); + vInv.clear(); + } + } + } + pto->vInventoryToSend = vInvWait; + } + if (!vInv.empty()) + pto->PushMessage("inv", vInv); + + + // + // Message: getdata + // + vector vGetData; + int64 nNow = GetTime() * 1000000; + CTxDB txdb("r"); + while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) + { + const CInv& inv = (*pto->mapAskFor.begin()).second; + if (!AlreadyHave(txdb, inv)) + { + printf("sending getdata: %s\n", inv.ToString().c_str()); + vGetData.push_back(inv); + if (vGetData.size() >= 1000) + { + pto->PushMessage("getdata", vGetData); + vGetData.clear(); + } + } + pto->mapAskFor.erase(pto->mapAskFor.begin()); + } + if (!vGetData.empty()) + pto->PushMessage("getdata", vGetData); + + } + return true; +} + + + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// BitcoinMiner +// + +void GenerateBitcoins(bool fGenerate) +{ + if (fGenerateBitcoins != fGenerate) + { + fGenerateBitcoins = fGenerate; + CWalletDB().WriteSetting("fGenerateBitcoins", fGenerateBitcoins); + MainFrameRepaint(); + } + if (fGenerateBitcoins) + { + int nProcessors = boost::thread::hardware_concurrency(); + printf("%d processors\n", nProcessors); + if (nProcessors < 1) + nProcessors = 1; + if (fLimitProcessors && nProcessors > nLimitProcessors) + nProcessors = nLimitProcessors; + int nAddThreads = nProcessors - vnThreadsRunning[3]; + printf("Starting %d BitcoinMiner threads\n", nAddThreads); + for (int i = 0; i < nAddThreads; i++) + { + if (!CreateThread(ThreadBitcoinMiner, NULL)) + printf("Error: CreateThread(ThreadBitcoinMiner) failed\n"); + Sleep(10); + } + } +} + +void ThreadBitcoinMiner(void* parg) +{ + try + { + vnThreadsRunning[3]++; + BitcoinMiner(); + vnThreadsRunning[3]--; + } + catch (std::exception& e) { + vnThreadsRunning[3]--; + PrintException(&e, "ThreadBitcoinMiner()"); + } catch (...) { + vnThreadsRunning[3]--; + PrintException(NULL, "ThreadBitcoinMiner()"); + } + UIThreadCall(boost::bind(CalledSetStatusBar, "", 0)); + nHPSTimerStart = 0; + if (vnThreadsRunning[3] == 0) + dHashesPerSec = 0; + printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[3]); +} + + +int FormatHashBlocks(void* pbuffer, unsigned int len) +{ + unsigned char* pdata = (unsigned char*)pbuffer; + unsigned int blocks = 1 + ((len + 8) / 64); + unsigned char* pend = pdata + 64 * blocks; + memset(pdata + len, 0, 64 * blocks - len); + pdata[len] = 0x80; + unsigned int bits = len * 8; + pend[-1] = (bits >> 0) & 0xff; + pend[-2] = (bits >> 8) & 0xff; + pend[-3] = (bits >> 16) & 0xff; + pend[-4] = (bits >> 24) & 0xff; + return blocks; +} + +using CryptoPP::ByteReverse; + +static const unsigned int pSHA256InitState[8] = +{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + +inline void SHA256Transform(void* pstate, void* pinput, const void* pinit) +{ + memcpy(pstate, pinit, 32); + CryptoPP::SHA256::Transform((CryptoPP::word32*)pstate, (CryptoPP::word32*)pinput); +} + +// +// ScanHash scans nonces looking for a hash with at least some zero bits. +// It operates on big endian data. Caller does the byte reversing. +// All input buffers are 16-byte aligned. nNonce is usually preserved +// between calls, but periodically or if nNonce is 0xffff0000 or above, +// the block is rebuilt and nNonce starts over at zero. +// +unsigned int ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone) +{ + unsigned int& nNonce = *(unsigned int*)(pdata + 12); + for (;;) + { + // Crypto++ SHA-256 + // Hash pdata using pmidstate as the starting state into + // preformatted buffer phash1, then hash phash1 into phash + nNonce++; + SHA256Transform(phash1, pdata, pmidstate); + SHA256Transform(phash, phash1, pSHA256InitState); + + // Return the nonce if the hash has at least some zero bits, + // caller will check if it has enough to reach the target + if (((unsigned short*)phash)[14] == 0) + return nNonce; + + // If nothing found after trying for a while, return -1 + if ((nNonce & 0xffff) == 0) + { + nHashesDone = 0xffff+1; + return -1; + } + } +} + + +class COrphan +{ +public: + CTransaction* ptx; + set setDependsOn; + double dPriority; + + COrphan(CTransaction* ptxIn) + { + ptx = ptxIn; + dPriority = 0; + } + + void print() const + { + printf("COrphan(hash=%s, dPriority=%.1f)\n", ptx->GetHash().ToString().substr(0,10).c_str(), dPriority); + BOOST_FOREACH(uint256 hash, setDependsOn) + printf(" setDependsOn %s\n", hash.ToString().substr(0,10).c_str()); + } +}; + + +CBlock* CreateNewBlock(CReserveKey& reservekey) +{ + CBlockIndex* pindexPrev = pindexBest; + + // Create new block + auto_ptr pblock(new CBlock()); + if (!pblock.get()) + return NULL; + + // Create coinbase tx + CTransaction txNew; + txNew.vin.resize(1); + txNew.vin[0].prevout.SetNull(); + txNew.vout.resize(1); + txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG; + + // Add our coinbase tx as first transaction + pblock->vtx.push_back(txNew); + + // Collect memory pool transactions into the block + int64 nFees = 0; + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapTransactions) + { + CTxDB txdb("r"); + + // Priority order to process transactions + list vOrphan; // list memory doesn't move + map > mapDependers; + multimap mapPriority; + for (map::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi) + { + CTransaction& tx = (*mi).second; + if (tx.IsCoinBase() || !tx.IsFinal()) + continue; + + COrphan* porphan = NULL; + double dPriority = 0; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + // Read prev transaction + CTransaction txPrev; + CTxIndex txindex; + if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) + { + // Has to wait for dependencies + if (!porphan) + { + // Use list for automatic deletion + vOrphan.push_back(COrphan(&tx)); + porphan = &vOrphan.back(); + } + mapDependers[txin.prevout.hash].push_back(porphan); + porphan->setDependsOn.insert(txin.prevout.hash); + continue; + } + int64 nValueIn = txPrev.vout[txin.prevout.n].nValue; + + // Read block header + int nConf = txindex.GetDepthInMainChain(); + + dPriority += (double)nValueIn * nConf; + + if (fDebug && GetBoolArg("-printpriority")) + printf("priority nValueIn=%-12I64d nConf=%-5d dPriority=%-20.1f\n", nValueIn, nConf, dPriority); + } + + // Priority is sum(valuein * age) / txsize + dPriority /= ::GetSerializeSize(tx, SER_NETWORK); + + if (porphan) + porphan->dPriority = dPriority; + else + mapPriority.insert(make_pair(-dPriority, &(*mi).second)); + + if (fDebug && GetBoolArg("-printpriority")) + { + printf("priority %-20.1f %s\n%s", dPriority, tx.GetHash().ToString().substr(0,10).c_str(), tx.ToString().c_str()); + if (porphan) + porphan->print(); + printf("\n"); + } + } + + // Collect transactions into block + map mapTestPool; + uint64 nBlockSize = 1000; + int nBlockSigOps = 100; + while (!mapPriority.empty()) + { + // Take highest priority transaction off priority queue + double dPriority = -(*mapPriority.begin()).first; + CTransaction& tx = *(*mapPriority.begin()).second; + mapPriority.erase(mapPriority.begin()); + + // Size limits + unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK); + if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN) + continue; + int nTxSigOps = tx.GetSigOpCount(); + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) + continue; + + // Transaction fee required depends on block size + bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority)); + int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree); + + // Connecting shouldn't fail due to dependency on other memory pool transactions + // because we're already processing them in order of dependency + map mapTestPoolTmp(mapTestPool); + if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee)) + continue; + swap(mapTestPool, mapTestPoolTmp); + + // Added + pblock->vtx.push_back(tx); + nBlockSize += nTxSize; + nBlockSigOps += nTxSigOps; + + // Add transactions that depend on this one to the priority queue + uint256 hash = tx.GetHash(); + if (mapDependers.count(hash)) + { + BOOST_FOREACH(COrphan* porphan, mapDependers[hash]) + { + if (!porphan->setDependsOn.empty()) + { + porphan->setDependsOn.erase(hash); + if (porphan->setDependsOn.empty()) + mapPriority.insert(make_pair(-porphan->dPriority, porphan->ptx)); + } + } + } + } + } + pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); + + // Fill in header + pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->nBits = GetNextWorkRequired(pindexPrev); + pblock->nNonce = 0; + + return pblock.release(); +} + + +void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, int64& nPrevTime) +{ + // Update nExtraNonce + int64 nNow = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + if (++nExtraNonce >= 0x7f && nNow > nPrevTime+1) + { + nExtraNonce = 1; + nPrevTime = nNow; + } + pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); +} + + +void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1) +{ + // + // Prebuild hash buffers + // + struct + { + struct unnamed2 + { + int nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + } + block; + unsigned char pchPadding0[64]; + uint256 hash1; + unsigned char pchPadding1[64]; + } + tmp; + memset(&tmp, 0, sizeof(tmp)); + + tmp.block.nVersion = pblock->nVersion; + tmp.block.hashPrevBlock = pblock->hashPrevBlock; + tmp.block.hashMerkleRoot = pblock->hashMerkleRoot; + tmp.block.nTime = pblock->nTime; + tmp.block.nBits = pblock->nBits; + tmp.block.nNonce = pblock->nNonce; + + FormatHashBlocks(&tmp.block, sizeof(tmp.block)); + FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1)); + + // Byte swap all the input buffer + for (int i = 0; i < sizeof(tmp)/4; i++) + ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]); + + // Precalc the first half of the first hash, which stays constant + SHA256Transform(pmidstate, &tmp.block, pSHA256InitState); + + memcpy(pdata, &tmp.block, 128); + memcpy(phash1, &tmp.hash1, 64); +} + + +bool CheckWork(CBlock* pblock, CReserveKey& reservekey) +{ + uint256 hash = pblock->GetHash(); + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + if (hash > hashTarget) + return false; + + //// debug print + printf("BitcoinMiner:\n"); + printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); + pblock->print(); + printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str()); + printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); + + // Found a solution + CRITICAL_BLOCK(cs_main) + { + if (pblock->hashPrevBlock != hashBestChain) + return error("BitcoinMiner : generated block is stale"); + + // Remove key from key pool + reservekey.KeepKey(); + + // Track how many getdata requests this block gets + CRITICAL_BLOCK(cs_mapRequestCount) + mapRequestCount[pblock->GetHash()] = 0; + + // Process this block the same as if we had received it from another node + if (!ProcessBlock(NULL, pblock)) + return error("BitcoinMiner : ProcessBlock, block not accepted"); + } + + Sleep(2000); + return true; +} + + +void BitcoinMiner() +{ + printf("BitcoinMiner started\n"); + SetThreadPriority(THREAD_PRIORITY_LOWEST); + + // Each thread has its own key and counter + CReserveKey reservekey; + unsigned int nExtraNonce = 0; + int64 nPrevTime = 0; + + while (fGenerateBitcoins) + { + if (AffinityBugWorkaround(ThreadBitcoinMiner)) + return; + if (fShutdown) + return; + while (vNodes.empty() || IsInitialBlockDownload()) + { + Sleep(1000); + if (fShutdown) + return; + if (!fGenerateBitcoins) + return; + } + + + // + // Create new block + // + unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; + CBlockIndex* pindexPrev = pindexBest; + + auto_ptr pblock(CreateNewBlock(reservekey)); + if (!pblock.get()) + return; + IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce, nPrevTime); + + printf("Running BitcoinMiner with %d transactions in block\n", pblock->vtx.size()); + + + // + // Prebuild hash buffers + // + char pmidstatebuf[32+16]; char* pmidstate = alignup<16>(pmidstatebuf); + char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf); + char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf); + + FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1); + + unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4); + unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12); + + + // + // Search + // + int64 nStart = GetTime(); + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + uint256 hashbuf[2]; + uint256& hash = *alignup<16>(hashbuf); + loop + { + unsigned int nHashesDone = 0; + unsigned int nNonceFound; + + // Crypto++ SHA-256 + nNonceFound = ScanHash_CryptoPP(pmidstate, pdata + 64, phash1, + (char*)&hash, nHashesDone); + + // Check if something found + if (nNonceFound != -1) + { + for (int i = 0; i < sizeof(hash)/4; i++) + ((unsigned int*)&hash)[i] = ByteReverse(((unsigned int*)&hash)[i]); + + if (hash <= hashTarget) + { + // Found a solution + pblock->nNonce = ByteReverse(nNonceFound); + assert(hash == pblock->GetHash()); + + SetThreadPriority(THREAD_PRIORITY_NORMAL); + CheckWork(pblock.get(), reservekey); + SetThreadPriority(THREAD_PRIORITY_LOWEST); + break; + } + } + + // Meter hashes/sec + static int64 nHashCounter; + if (nHPSTimerStart == 0) + { + nHPSTimerStart = GetTimeMillis(); + nHashCounter = 0; + } + else + nHashCounter += nHashesDone; + if (GetTimeMillis() - nHPSTimerStart > 4000) + { + static CCriticalSection cs; + CRITICAL_BLOCK(cs) + { + if (GetTimeMillis() - nHPSTimerStart > 4000) + { + dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart); + nHPSTimerStart = GetTimeMillis(); + nHashCounter = 0; + string strStatus = strprintf(" %.0f khash/s", dHashesPerSec/1000.0); + UIThreadCall(boost::bind(CalledSetStatusBar, strStatus, 0)); + static int64 nLogTime; + if (GetTime() - nLogTime > 30 * 60) + { + nLogTime = GetTime(); + printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str()); + printf("hashmeter %3d CPUs %6.0f khash/s\n", vnThreadsRunning[3], dHashesPerSec/1000.0); + } + } + } + } + + // Check for stop or if block needs to be rebuilt + if (fShutdown) + return; + if (!fGenerateBitcoins) + return; + if (fLimitProcessors && vnThreadsRunning[3] > nLimitProcessors) + return; + if (vNodes.empty()) + break; + if (nBlockNonce >= 0xffff0000) + break; + if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60) + break; + if (pindexPrev != pindexBest) + break; + + // Update nTime every few seconds + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + nBlockTime = ByteReverse(pblock->nTime); + } + } +} + + + + + + + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Actions +// + + +int64 GetBalance() +{ + int64 nStart = GetTimeMillis(); + + int64 nTotal = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* pcoin = &(*it).second; + if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + continue; + nTotal += pcoin->GetAvailableCredit(); + } + } + + //printf("GetBalance() %"PRI64d"ms\n", GetTimeMillis() - nStart); + return nTotal; +} + + +bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) +{ + setCoinsRet.clear(); + nValueRet = 0; + + // List of values less than target + pair > coinLowestLarger; + coinLowestLarger.first = INT64_MAX; + coinLowestLarger.second.first = NULL; + vector > > vValue; + int64 nTotalLower = 0; + + CRITICAL_BLOCK(cs_mapWallet) + { + vector vCoins; + vCoins.reserve(mapWallet.size()); + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + vCoins.push_back(&(*it).second); + random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + + BOOST_FOREACH(CWalletTx* pcoin, vCoins) + { + if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) + continue; + + for (int i = 0; i < pcoin->vout.size(); i++) + { + if (pcoin->IsSpent(i) || !pcoin->vout[i].IsMine()) + continue; + + int64 n = pcoin->vout[i].nValue; + + if (n <= 0) + continue; + + pair > coin = make_pair(n,make_pair(pcoin,i)); + + if (n == nTargetValue) + { + setCoinsRet.insert(coin.second); + nValueRet += coin.first; + return true; + } + else if (n < nTargetValue + CENT) + { + vValue.push_back(coin); + nTotalLower += n; + } + else if (n < coinLowestLarger.first) + { + coinLowestLarger = coin; + } + } + } + } + + if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT) + { + for (int i = 0; i < vValue.size(); ++i) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + return true; + } + + if (nTotalLower < nTargetValue + (coinLowestLarger.second.first ? CENT : 0)) + { + if (coinLowestLarger.second.first == NULL) + return false; + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + return true; + } + + if (nTotalLower >= nTargetValue + CENT) + nTargetValue += CENT; + + // Solve subset sum by stochastic approximation + sort(vValue.rbegin(), vValue.rend()); + vector vfIncluded; + vector vfBest(vValue.size(), true); + int64 nBest = nTotalLower; + + for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++) + { + vfIncluded.assign(vValue.size(), false); + int64 nTotal = 0; + bool fReachedTarget = false; + for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) + { + for (int i = 0; i < vValue.size(); i++) + { + if (nPass == 0 ? rand() % 2 : !vfIncluded[i]) + { + nTotal += vValue[i].first; + vfIncluded[i] = true; + if (nTotal >= nTargetValue) + { + fReachedTarget = true; + if (nTotal < nBest) + { + nBest = nTotal; + vfBest = vfIncluded; + } + nTotal -= vValue[i].first; + vfIncluded[i] = false; + } + } + } + } + } + + // If the next larger is still closer, return it + if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue) + { + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + } + else { + for (int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + + //// debug print + printf("SelectCoins() best subset: "); + for (int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + printf("%s ", FormatMoney(vValue[i].first).c_str()); + printf("total %s\n", FormatMoney(nBest).c_str()); + } + + return true; +} + +bool SelectCoins(int64 nTargetValue, set >& setCoinsRet, int64& nValueRet) +{ + return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet)); +} + + + + +bool CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +{ + int64 nValue = 0; + BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + { + if (nValue < 0) + return false; + nValue += s.second; + } + if (vecSend.empty() || nValue < 0) + return false; + + CRITICAL_BLOCK(cs_main) + { + // txdb must be opened before the mapWallet lock + CTxDB txdb("r"); + CRITICAL_BLOCK(cs_mapWallet) + { + nFeeRet = nTransactionFee; + loop + { + wtxNew.vin.clear(); + wtxNew.vout.clear(); + wtxNew.fFromMe = true; + + int64 nTotalValue = nValue + nFeeRet; + double dPriority = 0; + // vouts to the payees + BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + wtxNew.vout.push_back(CTxOut(s.second, s.first)); + + // Choose coins to use + set > setCoins; + int64 nValueIn = 0; + if (!SelectCoins(nTotalValue, setCoins, nValueIn)) + return false; + BOOST_FOREACH(PAIRTYPE(CWalletTx*, unsigned int) pcoin, setCoins) + { + int64 nCredit = pcoin.first->vout[pcoin.second].nValue; + dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(); + } + + // Fill a vout back to self with any change + int64 nChange = nValueIn - nTotalValue; + if (nChange >= CENT) + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + vector vchPubKey = reservekey.GetReservedKey(); + assert(mapKeys.count(vchPubKey)); + + // Fill a vout to ourself, using same address type as the payment + CScript scriptChange; + if (vecSend[0].first.GetBitcoinAddressHash160() != 0) + scriptChange.SetBitcoinAddress(vchPubKey); + else + scriptChange << vchPubKey << OP_CHECKSIG; + + // Insert change txn at random position: + vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); + wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); + } + else + reservekey.ReturnKey(); + + // Fill vin + BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) + wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); + + // Sign + int nIn = 0; + BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) + if (!SignSignature(*coin.first, wtxNew, nIn++)) + return false; + + // Limit size + unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK); + if (nBytes >= MAX_BLOCK_SIZE_GEN/5) + return false; + dPriority /= nBytes; + + // Check that enough fee is included + int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); + bool fAllowFree = CTransaction::AllowFree(dPriority); + int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree); + if (nFeeRet < max(nPayFee, nMinFee)) + { + nFeeRet = max(nPayFee, nMinFee); + continue; + } + + // Fill vtxPrev by copying from previous transactions vtxPrev + wtxNew.AddSupportingTransactions(txdb); + wtxNew.fTimeReceivedIsTxTime = true; + + break; + } + } + } + return true; +} + +bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +{ + vector< pair > vecSend; + vecSend.push_back(make_pair(scriptPubKey, nValue)); + return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); +} + +// Call after CreateTransaction unless you want to abort +bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) +{ + CRITICAL_BLOCK(cs_main) + { + printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); + CRITICAL_BLOCK(cs_mapWallet) + { + // This is only to keep the database open to defeat the auto-flush for the + // duration of this scope. This is the only place where this optimization + // maybe makes sense; please don't do it anywhere else. + CWalletDB walletdb("r"); + + // Take key pair from key pool so it won't be used again + reservekey.KeepKey(); + + // Add tx to wallet, because if it has change it's also ours, + // otherwise just for transaction history. + AddToWallet(wtxNew); + + // Mark old coins as spent + set setCoins; + BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) + { + CWalletTx &pcoin = mapWallet[txin.prevout.hash]; + pcoin.MarkSpent(txin.prevout.n); + pcoin.WriteToDisk(); + vWalletUpdated.push_back(pcoin.GetHash()); + } + } + + // Track how many getdata requests our transaction gets + CRITICAL_BLOCK(cs_mapRequestCount) + mapRequestCount[wtxNew.GetHash()] = 0; + + // Broadcast + if (!wtxNew.AcceptToMemoryPool()) + { + // This must not fail. The transaction has already been signed and recorded. + printf("CommitTransaction() : Error: Transaction not valid"); + return false; + } + wtxNew.RelayWalletTransaction(); + } + MainFrameRepaint(); + return true; +} + + + + +// requires cs_main lock +string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +{ + CReserveKey reservekey; + int64 nFeeRequired; + if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired)) + { + string strError; + if (nValue + nFeeRequired > 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("SendMoney() : %s", strError.c_str()); + return strError; + } + + if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL)) + return "ABORTED"; + + if (!CommitTransaction(wtxNew, reservekey)) + return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); + + MainFrameRepaint(); + return ""; +} + + + +// requires cs_main lock +string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +{ + // Check amount + if (nValue <= 0) + return _("Invalid amount"); + if (nValue + nTransactionFee > GetBalance()) + return _("Insufficient funds"); + + // Parse bitcoin address + CScript scriptPubKey; + if (!scriptPubKey.SetBitcoinAddress(strAddress)) + return _("Invalid bitcoin address"); + + return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); +} diff --git a/core/src/net.cpp b/core/src/net.cpp new file mode 100644 index 0000000000..4f489fdb57 --- /dev/null +++ b/core/src/net.cpp @@ -0,0 +1,1665 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" + +#ifdef USE_UPNP +#include +#include +#include +#include +#endif + +using namespace std; +using namespace boost; + +static const int MAX_OUTBOUND_CONNECTIONS = 8; + +void ThreadMessageHandler2(void* parg); +void ThreadSocketHandler2(void* parg); +void ThreadOpenConnections2(void* parg); +#ifdef USE_UPNP +void ThreadMapPort2(void* parg); +#endif +bool OpenNetworkConnection(const CAddress& addrConnect); + + + + + +// +// Global state variables +// +bool fClient = false; +bool fAllowDNS = false; +uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); +CAddress addrLocalHost("0.0.0.0", 0, false, nLocalServices); +CNode* pnodeLocalHost = NULL; +uint64 nLocalHostNonce = 0; +array vnThreadsRunning; +SOCKET hListenSocket = INVALID_SOCKET; + +vector vNodes; +CCriticalSection cs_vNodes; +map, CAddress> mapAddresses; +CCriticalSection cs_mapAddresses; +map mapRelay; +deque > vRelayExpiration; +CCriticalSection cs_mapRelay; +map mapAlreadyAskedFor; + +// Settings +int fUseProxy = false; +CAddress addrProxy("127.0.0.1",9050); + + + + + +void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) +{ + // Filter out duplicate requests + if (pindexBegin == pindexLastGetBlocksBegin && hashEnd == hashLastGetBlocksEnd) + return; + pindexLastGetBlocksBegin = pindexBegin; + hashLastGetBlocksEnd = hashEnd; + + PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); +} + + + + + +bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) +{ + hSocketRet = INVALID_SOCKET; + + SOCKET hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (hSocket == INVALID_SOCKET) + return false; +#ifdef BSD + int set = 1; + setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)); +#endif + + bool fRoutable = !(addrConnect.GetByte(3) == 10 || (addrConnect.GetByte(3) == 192 && addrConnect.GetByte(2) == 168)); + bool fProxy = (fUseProxy && fRoutable); + struct sockaddr_in sockaddr = (fProxy ? addrProxy.GetSockAddr() : addrConnect.GetSockAddr()); + + if (connect(hSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) + { + closesocket(hSocket); + return false; + } + + if (fProxy) + { + printf("proxy connecting %s\n", addrConnect.ToString().c_str()); + char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; + memcpy(pszSocks4IP + 2, &addrConnect.port, 2); + memcpy(pszSocks4IP + 4, &addrConnect.ip, 4); + char* pszSocks4 = pszSocks4IP; + int nSize = sizeof(pszSocks4IP); + + int ret = send(hSocket, pszSocks4, nSize, MSG_NOSIGNAL); + if (ret != nSize) + { + closesocket(hSocket); + return error("Error sending to proxy"); + } + char pchRet[8]; + if (recv(hSocket, pchRet, 8, 0) != 8) + { + closesocket(hSocket); + return error("Error reading proxy response"); + } + if (pchRet[1] != 0x5a) + { + closesocket(hSocket); + if (pchRet[1] != 0x5b) + printf("ERROR: Proxy returned error %d\n", pchRet[1]); + return false; + } + printf("proxy connected %s\n", addrConnect.ToString().c_str()); + } + + hSocketRet = hSocket; + return true; +} + +// portDefault is in host order +bool Lookup(const char *pszName, vector& vaddr, int nServices, int nMaxSolutions, bool fAllowLookup, int portDefault, bool fAllowPort) +{ + vaddr.clear(); + int port = portDefault; + char psz[256]; + char *pszHost = psz; + strlcpy(psz, pszName, sizeof(psz)); + if (fAllowPort) + { + char* pszColon = strrchr(psz+1,':'); + char *pszPortEnd = NULL; + int portParsed = pszColon ? strtoul(pszColon+1, &pszPortEnd, 10) : 0; + if (pszColon && pszPortEnd && pszPortEnd[0] == 0) + { + if (psz[0] == '[' && pszColon[-1] == ']') + { + // Future: enable IPv6 colon-notation inside [] + pszHost = psz+1; + pszColon[-1] = 0; + } + else + pszColon[0] = 0; + port = portParsed; + if (port < 0 || port > USHRT_MAX) + port = USHRT_MAX; + } + } + + struct in_addr addrIP; + if (inet_aton(pszHost, &addrIP)) + { + // valid IP address passed + vaddr.push_back(CAddress(addrIP.s_addr, port, nServices)); + return true; + } + + if (!fAllowLookup) + return false; + + struct hostent* phostent = gethostbyname(pszHost); + if (!phostent) + return false; + + if (phostent->h_addrtype != AF_INET) + return false; + + char** ppAddr = phostent->h_addr_list; + while (*ppAddr != NULL && vaddr.size() != nMaxSolutions) + { + CAddress addr(((struct in_addr*)ppAddr[0])->s_addr, port, nServices); + if (addr.IsValid()) + vaddr.push_back(addr); + ppAddr++; + } + + return (vaddr.size() > 0); +} + +// portDefault is in host order +bool Lookup(const char *pszName, CAddress& addr, int nServices, bool fAllowLookup, int portDefault, bool fAllowPort) +{ + vector vaddr; + bool fRet = Lookup(pszName, vaddr, nServices, 1, fAllowLookup, portDefault, fAllowPort); + if (fRet) + addr = vaddr[0]; + return fRet; +} + +bool GetMyExternalIP2(const CAddress& addrConnect, const char* pszGet, const char* pszKeyword, unsigned int& ipRet) +{ + SOCKET hSocket; + if (!ConnectSocket(addrConnect, hSocket)) + return error("GetMyExternalIP() : connection to %s failed", addrConnect.ToString().c_str()); + + send(hSocket, pszGet, strlen(pszGet), MSG_NOSIGNAL); + + string strLine; + while (RecvLine(hSocket, strLine)) + { + if (strLine.empty()) // HTTP response is separated from headers by blank line + { + loop + { + if (!RecvLine(hSocket, strLine)) + { + closesocket(hSocket); + return false; + } + if (pszKeyword == NULL) + break; + if (strLine.find(pszKeyword) != -1) + { + strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword)); + break; + } + } + closesocket(hSocket); + if (strLine.find("<") != -1) + strLine = strLine.substr(0, strLine.find("<")); + strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r")); + while (strLine.size() > 0 && isspace(strLine[strLine.size()-1])) + strLine.resize(strLine.size()-1); + CAddress addr(strLine,0,true); + printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); + if (addr.ip == 0 || addr.ip == INADDR_NONE || !addr.IsRoutable()) + return false; + ipRet = addr.ip; + return true; + } + } + closesocket(hSocket); + return error("GetMyExternalIP() : connection closed"); +} + +// We now get our external IP from the IRC server first and only use this as a backup +bool GetMyExternalIP(unsigned int& ipRet) +{ + CAddress addrConnect; + const char* pszGet; + const char* pszKeyword; + + if (fUseProxy) + return false; + + for (int nLookup = 0; nLookup <= 1; nLookup++) + for (int nHost = 1; nHost <= 2; nHost++) + { + // We should be phasing out our use of sites like these. If we need + // replacements, we should ask for volunteers to put this simple + // php file on their webserver that prints the client IP: + // + if (nHost == 1) + { + addrConnect = CAddress("91.198.22.70",80); // checkip.dyndns.org + + if (nLookup == 1) + { + CAddress addrIP("checkip.dyndns.org", 80, true); + if (addrIP.IsValid()) + addrConnect = addrIP; + } + + pszGet = "GET / HTTP/1.1\r\n" + "Host: checkip.dyndns.org\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" + "Connection: close\r\n" + "\r\n"; + + pszKeyword = "Address:"; + } + else if (nHost == 2) + { + addrConnect = CAddress("74.208.43.192", 80); // www.showmyip.com + + if (nLookup == 1) + { + CAddress addrIP("www.showmyip.com", 80, true); + if (addrIP.IsValid()) + addrConnect = addrIP; + } + + pszGet = "GET /simple/ HTTP/1.1\r\n" + "Host: www.showmyip.com\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" + "Connection: close\r\n" + "\r\n"; + + pszKeyword = NULL; // Returns just IP address + } + + if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet)) + return true; + } + + return false; +} + +void ThreadGetMyExternalIP(void* parg) +{ + // Wait for IRC to get it first + if (!GetBoolArg("-noirc")) + { + for (int i = 0; i < 2 * 60; i++) + { + Sleep(1000); + if (fGotExternalIP || fShutdown) + return; + } + } + + // Fallback in case IRC fails to get it + if (GetMyExternalIP(addrLocalHost.ip)) + { + printf("GetMyExternalIP() returned %s\n", addrLocalHost.ToStringIP().c_str()); + if (addrLocalHost.IsRoutable()) + { + // If we already connected to a few before we had our IP, go back and addr them. + // setAddrKnown automatically filters any duplicate sends. + CAddress addr(addrLocalHost); + addr.nTime = GetAdjustedTime(); + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + pnode->PushAddress(addr); + } + } +} + + + + + +bool AddAddress(CAddress addr, int64 nTimePenalty) +{ + if (!addr.IsRoutable()) + return false; + if (addr.ip == addrLocalHost.ip) + return false; + addr.nTime = max((int64)0, (int64)addr.nTime - nTimePenalty); + CRITICAL_BLOCK(cs_mapAddresses) + { + map, CAddress>::iterator it = mapAddresses.find(addr.GetKey()); + if (it == mapAddresses.end()) + { + // New address + printf("AddAddress(%s)\n", addr.ToString().c_str()); + mapAddresses.insert(make_pair(addr.GetKey(), addr)); + CAddrDB().WriteAddress(addr); + return true; + } + else + { + bool fUpdated = false; + CAddress& addrFound = (*it).second; + if ((addrFound.nServices | addr.nServices) != addrFound.nServices) + { + // Services have been added + addrFound.nServices |= addr.nServices; + fUpdated = true; + } + bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60); + int64 nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); + if (addrFound.nTime < addr.nTime - nUpdateInterval) + { + // Periodically update most recently seen time + addrFound.nTime = addr.nTime; + fUpdated = true; + } + if (fUpdated) + CAddrDB().WriteAddress(addrFound); + } + } + return false; +} + +void AddressCurrentlyConnected(const CAddress& addr) +{ + CRITICAL_BLOCK(cs_mapAddresses) + { + // Only if it's been published already + map, CAddress>::iterator it = mapAddresses.find(addr.GetKey()); + if (it != mapAddresses.end()) + { + CAddress& addrFound = (*it).second; + int64 nUpdateInterval = 20 * 60; + if (addrFound.nTime < GetAdjustedTime() - nUpdateInterval) + { + // Periodically update most recently seen time + addrFound.nTime = GetAdjustedTime(); + CAddrDB addrdb; + addrdb.WriteAddress(addrFound); + } + } + } +} + + + + + +void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) +{ + // If the dialog might get closed before the reply comes back, + // call this in the destructor so it doesn't get called after it's deleted. + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodes) + { + CRITICAL_BLOCK(pnode->cs_mapRequests) + { + for (map::iterator mi = pnode->mapRequests.begin(); mi != pnode->mapRequests.end();) + { + CRequestTracker& tracker = (*mi).second; + if (tracker.fn == fn && tracker.param1 == param1) + pnode->mapRequests.erase(mi++); + else + mi++; + } + } + } + } +} + + + + + + + +// +// Subscription methods for the broadcast and subscription system. +// Channel numbers are message numbers, i.e. MSG_TABLE and MSG_PRODUCT. +// +// The subscription system uses a meet-in-the-middle strategy. +// With 100,000 nodes, if senders broadcast to 1000 random nodes and receivers +// subscribe to 1000 random nodes, 99.995% (1 - 0.99^1000) of messages will get through. +// + +bool AnySubscribed(unsigned int nChannel) +{ + if (pnodeLocalHost->IsSubscribed(nChannel)) + return true; + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->IsSubscribed(nChannel)) + return true; + return false; +} + +bool CNode::IsSubscribed(unsigned int nChannel) +{ + if (nChannel >= vfSubscribe.size()) + return false; + return vfSubscribe[nChannel]; +} + +void CNode::Subscribe(unsigned int nChannel, unsigned int nHops) +{ + if (nChannel >= vfSubscribe.size()) + return; + + if (!AnySubscribed(nChannel)) + { + // Relay subscribe + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode != this) + pnode->PushMessage("subscribe", nChannel, nHops); + } + + vfSubscribe[nChannel] = true; +} + +void CNode::CancelSubscribe(unsigned int nChannel) +{ + if (nChannel >= vfSubscribe.size()) + return; + + // Prevent from relaying cancel if wasn't subscribed + if (!vfSubscribe[nChannel]) + return; + vfSubscribe[nChannel] = false; + + if (!AnySubscribed(nChannel)) + { + // Relay subscription cancel + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode != this) + pnode->PushMessage("sub-cancel", nChannel); + } +} + + + + + + + + + +CNode* FindNode(unsigned int ip) +{ + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->addr.ip == ip) + return (pnode); + } + return NULL; +} + +CNode* FindNode(CAddress addr) +{ + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->addr == addr) + return (pnode); + } + return NULL; +} + +CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) +{ + if (addrConnect.ip == addrLocalHost.ip) + return NULL; + + // Look for an existing connection + CNode* pnode = FindNode(addrConnect.ip); + if (pnode) + { + if (nTimeout != 0) + pnode->AddRef(nTimeout); + else + pnode->AddRef(); + return pnode; + } + + /// debug print + printf("trying connection %s lastseen=%.1fhrs lasttry=%.1fhrs\n", + addrConnect.ToString().c_str(), + (double)(addrConnect.nTime - GetAdjustedTime())/3600.0, + (double)(addrConnect.nLastTry - GetAdjustedTime())/3600.0); + + CRITICAL_BLOCK(cs_mapAddresses) + mapAddresses[addrConnect.GetKey()].nLastTry = GetAdjustedTime(); + + // Connect + SOCKET hSocket; + if (ConnectSocket(addrConnect, hSocket)) + { + /// debug print + printf("connected %s\n", addrConnect.ToString().c_str()); + + // Set to nonblocking +#ifdef __WXMSW__ + u_long nOne = 1; + if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) + printf("ConnectSocket() : ioctlsocket nonblocking setting failed, error %d\n", WSAGetLastError()); +#else + if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) + printf("ConnectSocket() : fcntl nonblocking setting failed, error %d\n", errno); +#endif + + // Add node + CNode* pnode = new CNode(hSocket, addrConnect, false); + if (nTimeout != 0) + pnode->AddRef(nTimeout); + else + pnode->AddRef(); + CRITICAL_BLOCK(cs_vNodes) + vNodes.push_back(pnode); + + pnode->nTimeConnected = GetTime(); + return pnode; + } + else + { + return NULL; + } +} + +void CNode::CloseSocketDisconnect() +{ + fDisconnect = true; + if (hSocket != INVALID_SOCKET) + { + if (fDebug) + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); + printf("disconnecting node %s\n", addr.ToString().c_str()); + closesocket(hSocket); + hSocket = INVALID_SOCKET; + } +} + +void CNode::Cleanup() +{ + // All of a nodes broadcasts and subscriptions are automatically torn down + // when it goes down, so a node has to stay up to keep its broadcast going. + + // Cancel subscriptions + for (unsigned int nChannel = 0; nChannel < vfSubscribe.size(); nChannel++) + if (vfSubscribe[nChannel]) + CancelSubscribe(nChannel); +} + + + + + + + + + + + + + +void ThreadSocketHandler(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadSocketHandler(parg)); + try + { + vnThreadsRunning[0]++; + ThreadSocketHandler2(parg); + vnThreadsRunning[0]--; + } + catch (std::exception& e) { + vnThreadsRunning[0]--; + PrintException(&e, "ThreadSocketHandler()"); + } catch (...) { + vnThreadsRunning[0]--; + throw; // support pthread_cancel() + } + printf("ThreadSocketHandler exiting\n"); +} + +void ThreadSocketHandler2(void* parg) +{ + printf("ThreadSocketHandler started\n"); + list vNodesDisconnected; + int nPrevNodeCount = 0; + + loop + { + // + // Disconnect nodes + // + CRITICAL_BLOCK(cs_vNodes) + { + // Disconnect unused nodes + vector vNodesCopy = vNodes; + BOOST_FOREACH(CNode* pnode, vNodesCopy) + { + if (pnode->fDisconnect || + (pnode->GetRefCount() <= 0 && pnode->vRecv.empty() && pnode->vSend.empty())) + { + // remove from vNodes + vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); + + // close socket and cleanup + pnode->CloseSocketDisconnect(); + pnode->Cleanup(); + + // hold in disconnected pool until all refs are released + pnode->nReleaseTime = max(pnode->nReleaseTime, GetTime() + 15 * 60); + if (pnode->fNetworkNode || pnode->fInbound) + pnode->Release(); + vNodesDisconnected.push_back(pnode); + } + } + + // Delete disconnected nodes + list vNodesDisconnectedCopy = vNodesDisconnected; + BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy) + { + // wait until threads are done using it + if (pnode->GetRefCount() <= 0) + { + bool fDelete = false; + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + TRY_CRITICAL_BLOCK(pnode->cs_mapRequests) + TRY_CRITICAL_BLOCK(pnode->cs_inventory) + fDelete = true; + if (fDelete) + { + vNodesDisconnected.remove(pnode); + delete pnode; + } + } + } + } + if (vNodes.size() != nPrevNodeCount) + { + nPrevNodeCount = vNodes.size(); + MainFrameRepaint(); + } + + + // + // Find which sockets have data to receive + // + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 50000; // frequency to poll pnode->vSend + + fd_set fdsetRecv; + fd_set fdsetSend; + fd_set fdsetError; + FD_ZERO(&fdsetRecv); + FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); + SOCKET hSocketMax = 0; + + if(hListenSocket != INVALID_SOCKET) + FD_SET(hListenSocket, &fdsetRecv); + hSocketMax = max(hSocketMax, hListenSocket); + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodes) + { + if (pnode->hSocket == INVALID_SOCKET || pnode->hSocket < 0) + continue; + FD_SET(pnode->hSocket, &fdsetRecv); + FD_SET(pnode->hSocket, &fdsetError); + hSocketMax = max(hSocketMax, pnode->hSocket); + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + if (!pnode->vSend.empty()) + FD_SET(pnode->hSocket, &fdsetSend); + } + } + + vnThreadsRunning[0]--; + int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); + vnThreadsRunning[0]++; + if (fShutdown) + return; + if (nSelect == SOCKET_ERROR) + { + int nErr = WSAGetLastError(); + printf("socket select error %d\n", nErr); + for (int i = 0; i <= hSocketMax; i++) + FD_SET(i, &fdsetRecv); + FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); + Sleep(timeout.tv_usec/1000); + } + + + // + // Accept new connections + // + if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) + { + struct sockaddr_in sockaddr; + socklen_t len = sizeof(sockaddr); + SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); + CAddress addr(sockaddr); + int nInbound = 0; + + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->fInbound) + nInbound++; + if (hSocket == INVALID_SOCKET) + { + if (WSAGetLastError() != WSAEWOULDBLOCK) + printf("socket error accept failed: %d\n", WSAGetLastError()); + } + else if (nInbound >= GetArg("-maxconnections", 125) - MAX_OUTBOUND_CONNECTIONS) + { + closesocket(hSocket); + } + else + { + printf("accepted connection %s\n", addr.ToString().c_str()); + CNode* pnode = new CNode(hSocket, addr, true); + pnode->AddRef(); + CRITICAL_BLOCK(cs_vNodes) + vNodes.push_back(pnode); + } + } + + + // + // Service each socket + // + vector vNodesCopy; + CRITICAL_BLOCK(cs_vNodes) + { + vNodesCopy = vNodes; + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->AddRef(); + } + BOOST_FOREACH(CNode* pnode, vNodesCopy) + { + if (fShutdown) + return; + + // + // Receive + // + if (pnode->hSocket == INVALID_SOCKET) + continue; + if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) + { + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + { + CDataStream& vRecv = pnode->vRecv; + unsigned int nPos = vRecv.size(); + + if (nPos > 1000*GetArg("-maxreceivebuffer", 10*1000)) { + if (!pnode->fDisconnect) + printf("socket recv flood control disconnect (%d bytes)\n", vRecv.size()); + pnode->CloseSocketDisconnect(); + } + else { + // typical socket buffer is 8K-64K + char pchBuf[0x10000]; + int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); + if (nBytes > 0) + { + vRecv.resize(nPos + nBytes); + memcpy(&vRecv[nPos], pchBuf, nBytes); + pnode->nLastRecv = GetTime(); + } + else if (nBytes == 0) + { + // socket closed gracefully + if (!pnode->fDisconnect) + printf("socket closed\n"); + pnode->CloseSocketDisconnect(); + } + else if (nBytes < 0) + { + // error + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) + { + if (!pnode->fDisconnect) + printf("socket recv error %d\n", nErr); + pnode->CloseSocketDisconnect(); + } + } + } + } + } + + // + // Send + // + if (pnode->hSocket == INVALID_SOCKET) + continue; + if (FD_ISSET(pnode->hSocket, &fdsetSend)) + { + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + { + CDataStream& vSend = pnode->vSend; + if (!vSend.empty()) + { + int nBytes = send(pnode->hSocket, &vSend[0], vSend.size(), MSG_NOSIGNAL | MSG_DONTWAIT); + if (nBytes > 0) + { + vSend.erase(vSend.begin(), vSend.begin() + nBytes); + pnode->nLastSend = GetTime(); + } + else if (nBytes < 0) + { + // error + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) + { + printf("socket send error %d\n", nErr); + pnode->CloseSocketDisconnect(); + } + } + if (vSend.size() > 1000*GetArg("-maxsendbuffer", 10*1000)) { + if (!pnode->fDisconnect) + printf("socket send flood control disconnect (%d bytes)\n", vSend.size()); + pnode->CloseSocketDisconnect(); + } + } + } + } + + // + // Inactivity checking + // + if (pnode->vSend.empty()) + pnode->nLastSendEmpty = GetTime(); + if (GetTime() - pnode->nTimeConnected > 60) + { + if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) + { + printf("socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0); + pnode->fDisconnect = true; + } + else if (GetTime() - pnode->nLastSend > 90*60 && GetTime() - pnode->nLastSendEmpty > 90*60) + { + printf("socket not sending\n"); + pnode->fDisconnect = true; + } + else if (GetTime() - pnode->nLastRecv > 90*60) + { + printf("socket inactivity timeout\n"); + pnode->fDisconnect = true; + } + } + } + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->Release(); + } + + Sleep(10); + } +} + + + + + + + + + +#ifdef USE_UPNP +void ThreadMapPort(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadMapPort(parg)); + try + { + vnThreadsRunning[5]++; + ThreadMapPort2(parg); + vnThreadsRunning[5]--; + } + catch (std::exception& e) { + vnThreadsRunning[5]--; + PrintException(&e, "ThreadMapPort()"); + } catch (...) { + vnThreadsRunning[5]--; + PrintException(NULL, "ThreadMapPort()"); + } + printf("ThreadMapPort exiting\n"); +} + +void ThreadMapPort2(void* parg) +{ + printf("ThreadMapPort started\n"); + + char port[6]; + sprintf(port, "%d", GetDefaultPort()); + + const char * rootdescurl = 0; + const char * multicastif = 0; + const char * minissdpdpath = 0; + struct UPNPDev * devlist = 0; + char lanaddr[64]; + + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); + + struct UPNPUrls urls; + struct IGDdatas data; + int r; + + r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); + if (r == 1) + { + char intClient[16]; + char intPort[6]; + +#ifndef __WXMSW__ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port, port, lanaddr, 0, "TCP", 0); +#else + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port, port, lanaddr, 0, "TCP", 0, "0"); +#endif + if(r!=UPNPCOMMAND_SUCCESS) + printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + port, port, lanaddr, r, strupnperror(r)); + else + printf("UPnP Port Mapping successful.\n"); + loop { + if (fShutdown || !fUseUPnP) + { + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port, "TCP", 0); + printf("UPNP_DeletePortMapping() returned : %d\n", r); + freeUPNPDevlist(devlist); devlist = 0; + FreeUPNPUrls(&urls); + return; + } + Sleep(2000); + } + } else { + printf("No valid UPnP IGDs found\n"); + freeUPNPDevlist(devlist); devlist = 0; + if (r != 0) + FreeUPNPUrls(&urls); + loop { + if (fShutdown || !fUseUPnP) + return; + Sleep(2000); + } + } +} + +void MapPort(bool fMapPort) +{ + if (fUseUPnP != fMapPort) + { + fUseUPnP = fMapPort; + CWalletDB().WriteSetting("fUseUPnP", fUseUPnP); + } + if (fUseUPnP && vnThreadsRunning[5] < 1) + { + if (!CreateThread(ThreadMapPort, NULL)) + printf("Error: ThreadMapPort(ThreadMapPort) failed\n"); + } +} +#endif + + + + + + + + + + +static const char *strDNSSeed[] = { + "bitseed.xf2.org", + "bitseed.bitcoin.org.uk", +}; + +void DNSAddressSeed() +{ + int found = 0; + + printf("Loading addresses from DNS seeds (could take a while)\n"); + + for (int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) { + vector vaddr; + if (Lookup(strDNSSeed[seed_idx], vaddr, NODE_NETWORK, true)) + { + BOOST_FOREACH (CAddress& addr, vaddr) + { + if (addr.GetByte(3) != 127) + { + addr.nTime = 0; + AddAddress(addr); + found++; + } + } + } + } + + printf("%d addresses found from DNS seeds\n", found); +} + + + +unsigned int pnSeed[] = +{ + 0x1ddb1032, 0x6242ce40, 0x52d6a445, 0x2dd7a445, 0x8a53cd47, 0x73263750, 0xda23c257, 0xecd4ed57, + 0x0a40ec59, 0x75dce160, 0x7df76791, 0x89370bad, 0xa4f214ad, 0x767700ae, 0x638b0418, 0x868a1018, + 0xcd9f332e, 0x0129653e, 0xcc92dc3e, 0x96671640, 0x56487e40, 0x5b66f440, 0xb1d01f41, 0xf1dc6041, + 0xc1d12b42, 0x86ba1243, 0x6be4df43, 0x6d4cef43, 0xd18e0644, 0x1ab0b344, 0x6584a345, 0xe7c1a445, + 0x58cea445, 0xc5daa445, 0x21dda445, 0x3d3b5346, 0x13e55347, 0x1080d24a, 0x8e611e4b, 0x81518e4b, + 0x6c839e4b, 0xe2ad0a4c, 0xfbbc0a4c, 0x7f5b6e4c, 0x7244224e, 0x1300554e, 0x20690652, 0x5a48b652, + 0x75c5c752, 0x4335cc54, 0x340fd154, 0x87c07455, 0x087b2b56, 0x8a133a57, 0xac23c257, 0x70374959, + 0xfb63d45b, 0xb9a1685c, 0x180d765c, 0x674f645d, 0x04d3495e, 0x1de44b5e, 0x4ee8a362, 0x0ded1b63, + 0xc1b04b6d, 0x8d921581, 0x97b7ea82, 0x1cf83a8e, 0x91490bad, 0x09dc75ae, 0x9a6d79ae, 0xa26d79ae, + 0x0fd08fae, 0x0f3e3fb2, 0x4f944fb2, 0xcca448b8, 0x3ecd6ab8, 0xa9d5a5bc, 0x8d0119c1, 0x045997d5, + 0xca019dd9, 0x0d526c4d, 0xabf1ba44, 0x66b1ab55, 0x1165f462, 0x3ed7cbad, 0xa38fae6e, 0x3bd2cbad, + 0xd36f0547, 0x20df7840, 0x7a337742, 0x549f8e4b, 0x9062365c, 0xd399f562, 0x2b5274a1, 0x8edfa153, + 0x3bffb347, 0x7074bf58, 0xb74fcbad, 0x5b5a795b, 0x02fa29ce, 0x5a6738d4, 0xe8a1d23e, 0xef98c445, + 0x4b0f494c, 0xa2bc1e56, 0x7694ad63, 0xa4a800c3, 0x05fda6cd, 0x9f22175e, 0x364a795b, 0x536285d5, + 0xac44c9d4, 0x0b06254d, 0x150c2fd4, 0x32a50dcc, 0xfd79ce48, 0xf15cfa53, 0x66c01e60, 0x6bc26661, + 0xc03b47ae, 0x4dda1b81, 0x3285a4c1, 0x883ca96d, 0x35d60a4c, 0xdae09744, 0x2e314d61, 0x84e247cf, + 0x6c814552, 0x3a1cc658, 0x98d8f382, 0xe584cb5b, 0x15e86057, 0x7b01504e, 0xd852dd48, 0x56382f56, + 0x0a5df454, 0xa0d18d18, 0x2e89b148, 0xa79c114c, 0xcbdcd054, 0x5523bc43, 0xa9832640, 0x8a066144, + 0x3894c3bc, 0xab76bf58, 0x6a018ac1, 0xfebf4f43, 0x2f26c658, 0x31102f4e, 0x85e929d5, 0x2a1c175e, + 0xfc6c2cd1, 0x27b04b6d, 0xdf024650, 0x161748b8, 0x28be6580, 0x57be6580, 0x1cee677a, 0xaa6bb742, + 0x9a53964b, 0x0a5a2d4d, 0x2434c658, 0x9a494f57, 0x1ebb0e48, 0xf610b85d, 0x077ecf44, 0x085128bc, + 0x5ba17a18, 0x27ca1b42, 0xf8a00b56, 0xfcd4c257, 0xcf2fc15e, 0xd897e052, 0x4cada04f, 0x2f35f6d5, + 0x382ce8c9, 0xe523984b, 0x3f946846, 0x60c8be43, 0x41da6257, 0xde0be142, 0xae8a544b, 0xeff0c254, + 0x1e0f795b, 0xaeb28890, 0xca16acd9, 0x1e47ddd8, 0x8c8c4829, 0xd27dc747, 0xd53b1663, 0x4096b163, + 0x9c8dd958, 0xcb12f860, 0x9e79305c, 0x40c1a445, 0x4a90c2bc, 0x2c3a464d, 0x2727f23c, 0x30b04b6d, + 0x59024cb8, 0xa091e6ad, 0x31b04b6d, 0xc29d46a6, 0x63934fb2, 0xd9224dbe, 0x9f5910d8, 0x7f530a6b, + 0x752e9c95, 0x65453548, 0xa484be46, 0xce5a1b59, 0x710e0718, 0x46a13d18, 0xdaaf5318, 0xc4a8ff53, + 0x87abaa52, 0xb764cf51, 0xb2025d4a, 0x6d351e41, 0xc035c33e, 0xa432c162, 0x61ef34ae, 0xd16fddbc, + 0x0870e8c1, 0x3070e8c1, 0x9c71e8c1, 0xa4992363, 0x85a1f663, 0x4184e559, 0x18d96ed8, 0x17b8dbd5, + 0x60e7cd18, 0xe5ee104c, 0xab17ac62, 0x1e786e1b, 0x5d23b762, 0xf2388fae, 0x88270360, 0x9e5b3d80, + 0x7da518b2, 0xb5613b45, 0x1ad41f3e, 0xd550854a, 0x8617e9a9, 0x925b229c, 0xf2e92542, 0x47af0544, + 0x73b5a843, 0xb9b7a0ad, 0x03a748d0, 0x0a6ff862, 0x6694df62, 0x3bfac948, 0x8e098f4f, 0x746916c3, + 0x02f38e4f, 0x40bb1243, 0x6a54d162, 0x6008414b, 0xa513794c, 0x514aa343, 0x63781747, 0xdbb6795b, + 0xed065058, 0x42d24b46, 0x1518794c, 0x9b271681, 0x73e4ffad, 0x0654784f, 0x438dc945, 0x641846a6, + 0x2d1b0944, 0x94b59148, 0x8d369558, 0xa5a97662, 0x8b705b42, 0xce9204ae, 0x8d584450, 0x2df61555, + 0xeebff943, 0x2e75fb4d, 0x3ef8fc57, 0x9921135e, 0x8e31042e, 0xb5afad43, 0x89ecedd1, 0x9cfcc047, + 0x8fcd0f4c, 0xbe49f5ad, 0x146a8d45, 0x98669ab8, 0x98d9175e, 0xd1a8e46d, 0x839a3ab8, 0x40a0016c, + 0x6d27c257, 0x977fffad, 0x7baa5d5d, 0x1213be43, 0xb167e5a9, 0x640fe8ca, 0xbc9ea655, 0x0f820a4c, + 0x0f097059, 0x69ac957c, 0x366d8453, 0xb1ba2844, 0x8857f081, 0x70b5be63, 0xc545454b, 0xaf36ded1, + 0xb5a4b052, 0x21f062d1, 0x72ab89b2, 0x74a45318, 0x8312e6bc, 0xb916965f, 0x8aa7c858, 0xfe7effad, +}; + + + +void ThreadOpenConnections(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadOpenConnections(parg)); + try + { + vnThreadsRunning[1]++; + ThreadOpenConnections2(parg); + vnThreadsRunning[1]--; + } + catch (std::exception& e) { + vnThreadsRunning[1]--; + PrintException(&e, "ThreadOpenConnections()"); + } catch (...) { + vnThreadsRunning[1]--; + PrintException(NULL, "ThreadOpenConnections()"); + } + printf("ThreadOpenConnections exiting\n"); +} + +void ThreadOpenConnections2(void* parg) +{ + printf("ThreadOpenConnections started\n"); + + // Connect to specific addresses + if (mapArgs.count("-connect")) + { + for (int64 nLoop = 0;; nLoop++) + { + BOOST_FOREACH(string strAddr, mapMultiArgs["-connect"]) + { + CAddress addr(strAddr, fAllowDNS); + if (addr.IsValid()) + OpenNetworkConnection(addr); + for (int i = 0; i < 10 && i < nLoop; i++) + { + Sleep(500); + if (fShutdown) + return; + } + } + } + } + + // Connect to manually added nodes first + if (mapArgs.count("-addnode")) + { + BOOST_FOREACH(string strAddr, mapMultiArgs["-addnode"]) + { + CAddress addr(strAddr, fAllowDNS); + if (addr.IsValid()) + { + OpenNetworkConnection(addr); + Sleep(500); + if (fShutdown) + return; + } + } + } + + // Initiate network connections + int64 nStart = GetTime(); + loop + { + // Limit outbound connections + vnThreadsRunning[1]--; + Sleep(500); + loop + { + int nOutbound = 0; + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (!pnode->fInbound) + nOutbound++; + int nMaxOutboundConnections = MAX_OUTBOUND_CONNECTIONS; + nMaxOutboundConnections = min(nMaxOutboundConnections, (int)GetArg("-maxconnections", 125)); + if (nOutbound < nMaxOutboundConnections) + break; + Sleep(2000); + if (fShutdown) + return; + } + vnThreadsRunning[1]++; + if (fShutdown) + return; + + CRITICAL_BLOCK(cs_mapAddresses) + { + // Add seed nodes if IRC isn't working + static bool fSeedUsed; + bool fTOR = (fUseProxy && addrProxy.port == htons(9050)); + if (mapAddresses.empty() && (GetTime() - nStart > 60 || fTOR) && !fTestNet) + { + for (int i = 0; i < ARRAYLEN(pnSeed); i++) + { + // It'll only connect to one or two seed nodes because once it connects, + // it'll get a pile of addresses with newer timestamps. + CAddress addr; + addr.ip = pnSeed[i]; + addr.nTime = 0; + AddAddress(addr); + } + fSeedUsed = true; + } + + if (fSeedUsed && mapAddresses.size() > ARRAYLEN(pnSeed) + 100) + { + // Disconnect seed nodes + set setSeed(pnSeed, pnSeed + ARRAYLEN(pnSeed)); + static int64 nSeedDisconnected; + if (nSeedDisconnected == 0) + { + nSeedDisconnected = GetTime(); + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (setSeed.count(pnode->addr.ip)) + pnode->fDisconnect = true; + } + + // Keep setting timestamps to 0 so they won't reconnect + if (GetTime() - nSeedDisconnected < 60 * 60) + { + BOOST_FOREACH(PAIRTYPE(const vector, CAddress)& item, mapAddresses) + { + if (setSeed.count(item.second.ip) && item.second.nTime != 0) + { + item.second.nTime = 0; + CAddrDB().WriteAddress(item.second); + } + } + } + } + } + + + // + // Choose an address to connect to based on most recently seen + // + CAddress addrConnect; + int64 nBest = INT64_MIN; + + // Only connect to one address per a.b.?.? range. + // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. + set setConnected; + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + setConnected.insert(pnode->addr.ip & 0x0000ffff); + + CRITICAL_BLOCK(cs_mapAddresses) + { + BOOST_FOREACH(const PAIRTYPE(vector, CAddress)& item, mapAddresses) + { + const CAddress& addr = item.second; + if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.ip & 0x0000ffff)) + continue; + int64 nSinceLastSeen = GetAdjustedTime() - addr.nTime; + int64 nSinceLastTry = GetAdjustedTime() - addr.nLastTry; + + // Randomize the order in a deterministic way, putting the standard port first + int64 nRandomizer = (uint64)(nStart * 4951 + addr.nLastTry * 9567851 + addr.ip * 7789) % (2 * 60 * 60); + if (addr.port != htons(GetDefaultPort())) + nRandomizer += 2 * 60 * 60; + + // Last seen Base retry frequency + // <1 hour 10 min + // 1 hour 1 hour + // 4 hours 2 hours + // 24 hours 5 hours + // 48 hours 7 hours + // 7 days 13 hours + // 30 days 27 hours + // 90 days 46 hours + // 365 days 93 hours + int64 nDelay = (int64)(3600.0 * sqrt(fabs((double)nSinceLastSeen) / 3600.0) + nRandomizer); + + // Fast reconnect for one hour after last seen + if (nSinceLastSeen < 60 * 60) + nDelay = 10 * 60; + + // Limit retry frequency + if (nSinceLastTry < nDelay) + continue; + + // If we have IRC, we'll be notified when they first come online, + // and again every 24 hours by the refresh broadcast. + if (nGotIRCAddresses > 0 && vNodes.size() >= 2 && nSinceLastSeen > 24 * 60 * 60) + continue; + + // Only try the old stuff if we don't have enough connections + if (vNodes.size() >= 8 && nSinceLastSeen > 24 * 60 * 60) + continue; + + // If multiple addresses are ready, prioritize by time since + // last seen and time since last tried. + int64 nScore = min(nSinceLastTry, (int64)24 * 60 * 60) - nSinceLastSeen - nRandomizer; + if (nScore > nBest) + { + nBest = nScore; + addrConnect = addr; + } + } + } + + if (addrConnect.IsValid()) + OpenNetworkConnection(addrConnect); + } +} + +bool OpenNetworkConnection(const CAddress& addrConnect) +{ + // + // Initiate outbound network connection + // + if (fShutdown) + return false; + if (addrConnect.ip == addrLocalHost.ip || !addrConnect.IsIPv4() || FindNode(addrConnect.ip)) + return false; + + vnThreadsRunning[1]--; + CNode* pnode = ConnectNode(addrConnect); + vnThreadsRunning[1]++; + if (fShutdown) + return false; + if (!pnode) + return false; + pnode->fNetworkNode = true; + + return true; +} + + + + + + + + +void ThreadMessageHandler(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadMessageHandler(parg)); + try + { + vnThreadsRunning[2]++; + ThreadMessageHandler2(parg); + vnThreadsRunning[2]--; + } + catch (std::exception& e) { + vnThreadsRunning[2]--; + PrintException(&e, "ThreadMessageHandler()"); + } catch (...) { + vnThreadsRunning[2]--; + PrintException(NULL, "ThreadMessageHandler()"); + } + printf("ThreadMessageHandler exiting\n"); +} + +void ThreadMessageHandler2(void* parg) +{ + printf("ThreadMessageHandler started\n"); + SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); + while (!fShutdown) + { + vector vNodesCopy; + CRITICAL_BLOCK(cs_vNodes) + { + vNodesCopy = vNodes; + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->AddRef(); + } + + // Poll the connected nodes for messages + CNode* pnodeTrickle = NULL; + if (!vNodesCopy.empty()) + pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())]; + BOOST_FOREACH(CNode* pnode, vNodesCopy) + { + // Receive messages + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + ProcessMessages(pnode); + if (fShutdown) + return; + + // Send messages + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + SendMessages(pnode, pnode == pnodeTrickle); + if (fShutdown) + return; + } + + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->Release(); + } + + // Wait and allow messages to bunch up. + // Reduce vnThreadsRunning so StopNode has permission to exit while + // we're sleeping, but we must always check fShutdown after doing this. + vnThreadsRunning[2]--; + Sleep(100); + if (fRequestShutdown) + Shutdown(NULL); + vnThreadsRunning[2]++; + if (fShutdown) + return; + } +} + + + + + + + + + +bool BindListenPort(string& strError) +{ + strError = ""; + int nOne = 1; + addrLocalHost.port = htons(GetDefaultPort()); + +#ifdef __WXMSW__ + // Initialize Windows Sockets + WSADATA wsadata; + int ret = WSAStartup(MAKEWORD(2,2), &wsadata); + if (ret != NO_ERROR) + { + strError = strprintf("Error: TCP/IP socket library failed to start (WSAStartup returned error %d)", ret); + printf("%s\n", strError.c_str()); + return false; + } +#endif + + // Create socket for listening for incoming connections + hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (hListenSocket == INVALID_SOCKET) + { + strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + +#ifdef BSD + // Different way of disabling SIGPIPE on BSD + setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int)); +#endif + +#ifndef __WXMSW__ + // Allow binding if the port is still in TIME_WAIT state after + // the program was closed and restarted. Not an issue on windows. + setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); +#endif + +#ifdef __WXMSW__ + // Set to nonblocking, incoming connections will also inherit this + if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR) +#else + if (fcntl(hListenSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) +#endif + { + strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + + // The sockaddr_in structure specifies the address family, + // IP address, and port for the socket that is being bound + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = INADDR_ANY; // bind to all IPs on this computer + sockaddr.sin_port = htons(GetDefaultPort()); + if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) + { + int nErr = WSAGetLastError(); + if (nErr == WSAEADDRINUSE) + strError = strprintf(_("Unable to bind to port %d on this computer. Bitcoin is probably already running."), ntohs(sockaddr.sin_port)); + else + strError = strprintf("Error: Unable to bind to port %d on this computer (bind returned error %d)", ntohs(sockaddr.sin_port), nErr); + printf("%s\n", strError.c_str()); + return false; + } + printf("Bound to port %d\n", ntohs(sockaddr.sin_port)); + + // Listen for incoming connections + if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) + { + strError = strprintf("Error: Listening for incoming connections failed (listen returned error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + + return true; +} + +void StartNode(void* parg) +{ + if (pnodeLocalHost == NULL) + pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress("127.0.0.1", 0, false, nLocalServices)); + +#ifdef __WXMSW__ + // Get local host ip + char pszHostName[1000] = ""; + if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) + { + vector vaddr; + if (NameLookup(pszHostName, vaddr, nLocalServices)) + BOOST_FOREACH (const CAddress &addr, vaddr) + if (addr.GetByte(3) != 127) + { + addrLocalHost = addr; + break; + } + } +#else + // Get local host ip + struct ifaddrs* myaddrs; + if (getifaddrs(&myaddrs) == 0) + { + for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == NULL) continue; + if ((ifa->ifa_flags & IFF_UP) == 0) continue; + if (strcmp(ifa->ifa_name, "lo") == 0) continue; + if (strcmp(ifa->ifa_name, "lo0") == 0) continue; + char pszIP[100]; + if (ifa->ifa_addr->sa_family == AF_INET) + { + struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr); + if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s4->sin_addr), pszIP, sizeof(pszIP)) != NULL) + printf("ipv4 %s: %s\n", ifa->ifa_name, pszIP); + + // Take the first IP that isn't loopback 127.x.x.x + CAddress addr(*(unsigned int*)&s4->sin_addr, 0, nLocalServices); + if (addr.IsValid() && addr.GetByte(3) != 127) + { + addrLocalHost = addr; + break; + } + } + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr); + if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s6->sin6_addr), pszIP, sizeof(pszIP)) != NULL) + printf("ipv6 %s: %s\n", ifa->ifa_name, pszIP); + } + } + freeifaddrs(myaddrs); + } +#endif + printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); + + if (fUseProxy || mapArgs.count("-connect") || fNoListen) + { + // Proxies can't take incoming connections + addrLocalHost.ip = CAddress("0.0.0.0").ip; + printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); + } + else + { + CreateThread(ThreadGetMyExternalIP, NULL); + } + + // + // Start threads + // + + // Map ports with UPnP + if (fHaveUPnP) + MapPort(fUseUPnP); + + // Get addresses from IRC and advertise ours + if (!CreateThread(ThreadIRCSeed, NULL)) + printf("Error: CreateThread(ThreadIRCSeed) failed\n"); + + // Send and receive from sockets, accept connections + pthread_t hThreadSocketHandler = CreateThread(ThreadSocketHandler, NULL, true); + + // Initiate outbound connections + if (!CreateThread(ThreadOpenConnections, NULL)) + printf("Error: CreateThread(ThreadOpenConnections) failed\n"); + + // Process messages + if (!CreateThread(ThreadMessageHandler, NULL)) + printf("Error: CreateThread(ThreadMessageHandler) failed\n"); + + // Generate coins in the background + GenerateBitcoins(fGenerateBitcoins); +} + +bool StopNode() +{ + printf("StopNode()\n"); + fShutdown = true; + nTransactionsUpdated++; + int64 nStart = GetTime(); + while (vnThreadsRunning[0] > 0 || vnThreadsRunning[2] > 0 || vnThreadsRunning[3] > 0 || vnThreadsRunning[4] > 0 +#ifdef USE_UPNP + || vnThreadsRunning[5] > 0 +#endif + ) + { + if (GetTime() - nStart > 20) + break; + Sleep(20); + } + if (vnThreadsRunning[0] > 0) printf("ThreadSocketHandler still running\n"); + if (vnThreadsRunning[1] > 0) printf("ThreadOpenConnections still running\n"); + if (vnThreadsRunning[2] > 0) printf("ThreadMessageHandler still running\n"); + if (vnThreadsRunning[3] > 0) printf("ThreadBitcoinMiner still running\n"); + if (vnThreadsRunning[4] > 0) printf("ThreadRPCServer still running\n"); + if (fHaveUPnP && vnThreadsRunning[5] > 0) printf("ThreadMapPort still running\n"); + while (vnThreadsRunning[2] > 0 || vnThreadsRunning[4] > 0) + Sleep(20); + Sleep(50); + + return true; +} + +class CNetCleanup +{ +public: + CNetCleanup() + { + } + ~CNetCleanup() + { + // Close sockets + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->hSocket != INVALID_SOCKET) + closesocket(pnode->hSocket); + if (hListenSocket != INVALID_SOCKET) + if (closesocket(hListenSocket) == SOCKET_ERROR) + printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError()); + +#ifdef __WXMSW__ + // Shutdown Windows Sockets + WSACleanup(); +#endif + } +} +instance_of_cnetcleanup; diff --git a/core/src/rpc.cpp b/core/src/rpc.cpp new file mode 100644 index 0000000000..9efcbbb15a --- /dev/null +++ b/core/src/rpc.cpp @@ -0,0 +1,2195 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" +#include "cryptopp/sha.h" +#undef printf +#include +#include +#include +#ifdef USE_SSL +#include +typedef boost::asio::ssl::stream SSLStream; +#endif +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_writer_template.h" +#include "json/json_spirit_utils.h" +#define printf OutputDebugStringF +// MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are +// precompiled in headers.h. The problem might be when the pch file goes over +// a certain size around 145MB. If we need access to json_spirit outside this +// file, we could use the compiled json_spirit option. + +using namespace std; +using namespace boost; +using namespace boost::asio; +using namespace json_spirit; + +void ThreadRPCServer2(void* parg); +typedef Value(*rpcfn_type)(const Array& params, bool fHelp); +extern map mapCallTable; + + +Object JSONRPCError(int code, const string& message) +{ + Object error; + error.push_back(Pair("code", code)); + error.push_back(Pair("message", message)); + return error; +} + + +void PrintConsole(const char* format, ...) +{ + char buffer[50000]; + int limit = sizeof(buffer); + va_list arg_ptr; + va_start(arg_ptr, format); + int ret = _vsnprintf(buffer, limit, format, arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + ret = limit - 1; + buffer[limit-1] = 0; + } + printf("%s", buffer); +#if defined(__WXMSW__) && defined(GUI) + MyMessageBox(buffer, "Bitcoin", wxOK | wxICON_EXCLAMATION); +#else + fprintf(stdout, "%s", buffer); +#endif +} + + +int64 AmountFromValue(const Value& value) +{ + double dAmount = value.get_real(); + if (dAmount <= 0.0 || dAmount > 21000000.0) + throw JSONRPCError(-3, "Invalid amount"); + int64 nAmount = roundint64(dAmount * COIN); + if (!MoneyRange(nAmount)) + throw JSONRPCError(-3, "Invalid amount"); + return nAmount; +} + +Value ValueFromAmount(int64 amount) +{ + return (double)amount / (double)COIN; +} + +void WalletTxToJSON(const CWalletTx& wtx, Object& entry) +{ + entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain())); + entry.push_back(Pair("txid", wtx.GetHash().GetHex())); + entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); + BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) + entry.push_back(Pair(item.first, item.second)); +} + +string AccountFromValue(const Value& value) +{ + string strAccount = value.get_str(); + if (strAccount == "*") + throw JSONRPCError(-11, "Invalid account name"); + return strAccount; +} + + + +/// +/// Note: This interface may still be subject to change. +/// + + +Value help(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "help [command]\n" + "List commands, or get help for a command."); + + string strCommand; + if (params.size() > 0) + strCommand = params[0].get_str(); + + string strRet; + set setDone; + for (map::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi) + { + string strMethod = (*mi).first; + // We already filter duplicates, but these deprecated screw up the sort order + if (strMethod == "getamountreceived" || + strMethod == "getallreceived" || + (strMethod.find("label") != string::npos)) + continue; + if (strCommand != "" && strMethod != strCommand) + continue; + try + { + Array params; + rpcfn_type pfn = (*mi).second; + if (setDone.insert(pfn).second) + (*pfn)(params, true); + } + catch (std::exception& e) + { + // Help text is returned in an exception + string strHelp = string(e.what()); + if (strCommand == "") + if (strHelp.find('\n') != -1) + strHelp = strHelp.substr(0, strHelp.find('\n')); + strRet += strHelp + "\n"; + } + } + if (strRet == "") + strRet = strprintf("help: unknown command: %s\n", strCommand.c_str()); + strRet = strRet.substr(0,strRet.size()-1); + return strRet; +} + + +Value stop(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "stop\n" + "Stop bitcoin server."); + + // Shutdown will take long enough that the response should get back + CreateThread(Shutdown, NULL); + return "bitcoin server stopping"; +} + + +Value getblockcount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getblockcount\n" + "Returns the number of blocks in the longest block chain."); + + return nBestHeight; +} + + +Value getblocknumber(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getblocknumber\n" + "Returns the block number of the latest block in the longest block chain."); + + return nBestHeight; +} + + +Value getconnectioncount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getconnectioncount\n" + "Returns the number of connections to other nodes."); + + return (int)vNodes.size(); +} + + +double GetDifficulty() +{ + // Floating point number that is a multiple of the minimum difficulty, + // minimum difficulty = 1.0. + if (pindexBest == NULL) + return 1.0; + int nShift = 256 - 32 - 31; // to fit in a uint + double dMinimum = (CBigNum().SetCompact(bnProofOfWorkLimit.GetCompact()) >> nShift).getuint(); + double dCurrently = (CBigNum().SetCompact(pindexBest->nBits) >> nShift).getuint(); + return dMinimum / dCurrently; +} + +Value getdifficulty(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getdifficulty\n" + "Returns the proof-of-work difficulty as a multiple of the minimum difficulty."); + + return GetDifficulty(); +} + + +Value getgenerate(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getgenerate\n" + "Returns true or false."); + + return (bool)fGenerateBitcoins; +} + + +Value setgenerate(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "setgenerate [genproclimit]\n" + " is true or false to turn generation on or off.\n" + "Generation is limited to [genproclimit] processors, -1 is unlimited."); + + bool fGenerate = true; + if (params.size() > 0) + fGenerate = params[0].get_bool(); + + if (params.size() > 1) + { + int nGenProcLimit = params[1].get_int(); + fLimitProcessors = (nGenProcLimit != -1); + CWalletDB().WriteSetting("fLimitProcessors", fLimitProcessors); + if (nGenProcLimit != -1) + CWalletDB().WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit); + if (nGenProcLimit == 0) + fGenerate = false; + } + + GenerateBitcoins(fGenerate); + return Value::null; +} + + +Value gethashespersec(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "gethashespersec\n" + "Returns a recent hashes per second performance measurement while generating."); + + if (GetTimeMillis() - nHPSTimerStart > 8000) + return (boost::int64_t)0; + return (boost::int64_t)dHashesPerSec; +} + + +Value getinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getinfo\n" + "Returns an object containing various state info."); + + Object obj; + obj.push_back(Pair("version", (int)VERSION)); + obj.push_back(Pair("balance", ValueFromAmount(GetBalance()))); + obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("connections", (int)vNodes.size())); + obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string()))); + obj.push_back(Pair("generate", (bool)fGenerateBitcoins)); + obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1))); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("hashespersec", gethashespersec(params, false))); + obj.push_back(Pair("testnet", fTestNet)); + obj.push_back(Pair("keypoololdest", (boost::int64_t)GetOldestKeyPoolTime())); + obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + return obj; +} + + +Value getnewaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getnewaddress [account]\n" + "Returns a new bitcoin address for receiving payments. " + "If [account] is specified (recommended), it is added to the address book " + "so payments received with the address will be credited to [account]."); + + // Parse the account first so we don't generate a key if there's an error + string strAccount; + if (params.size() > 0) + strAccount = AccountFromValue(params[0]); + + // Generate a new key that is added to wallet + string strAddress = PubKeyToAddress(GetKeyFromKeyPool()); + + SetAddressBookName(strAddress, strAccount); + return strAddress; +} + + +// requires cs_main, cs_mapWallet locks +string GetAccountAddress(string strAccount, bool bForceNew=false) +{ + string strAddress; + + CWalletDB walletdb; + walletdb.TxnBegin(); + + CAccount account; + walletdb.ReadAccount(strAccount, account); + + // Check if the current key has been used + if (!account.vchPubKey.empty()) + { + CScript scriptPubKey; + scriptPubKey.SetBitcoinAddress(account.vchPubKey); + for (map::iterator it = mapWallet.begin(); + it != mapWallet.end() && !account.vchPubKey.empty(); + ++it) + { + const CWalletTx& wtx = (*it).second; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + account.vchPubKey.clear(); + } + } + + // Generate a new key + if (account.vchPubKey.empty() || bForceNew) + { + account.vchPubKey = GetKeyFromKeyPool(); + string strAddress = PubKeyToAddress(account.vchPubKey); + SetAddressBookName(strAddress, strAccount); + walletdb.WriteAccount(strAccount, account); + } + + walletdb.TxnCommit(); + strAddress = PubKeyToAddress(account.vchPubKey); + + return strAddress; +} + +Value getaccountaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaccountaddress \n" + "Returns the current bitcoin address for receiving payments to this account."); + + // Parse the account first so we don't generate a key if there's an error + string strAccount = AccountFromValue(params[0]); + + Value ret; + + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + { + ret = GetAccountAddress(strAccount); + } + + return ret; +} + + + +Value setaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "setaccount \n" + "Sets the account associated with the given address."); + + string strAddress = params[0].get_str(); + uint160 hash160; + bool isValid = AddressToHash160(strAddress, hash160); + if (!isValid) + throw JSONRPCError(-5, "Invalid bitcoin address"); + + + string strAccount; + if (params.size() > 1) + strAccount = AccountFromValue(params[1]); + + // Detect when changing the account of an address that is the 'unused current key' of another account: + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_mapAddressBook) + { + if (mapAddressBook.count(strAddress)) + { + string strOldAccount = mapAddressBook[strAddress]; + if (strAddress == GetAccountAddress(strOldAccount)) + GetAccountAddress(strOldAccount, true); + } + } + + SetAddressBookName(strAddress, strAccount); + return Value::null; +} + + +Value getaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaccount \n" + "Returns the account associated with the given address."); + + string strAddress = params[0].get_str(); + + string strAccount; + CRITICAL_BLOCK(cs_mapAddressBook) + { + map::iterator mi = mapAddressBook.find(strAddress); + if (mi != mapAddressBook.end() && !(*mi).second.empty()) + strAccount = (*mi).second; + } + return strAccount; +} + + +Value getaddressesbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaddressesbyaccount \n" + "Returns the list of addresses for the given account."); + + string strAccount = AccountFromValue(params[0]); + + // Find all addresses that have the given account + Array ret; + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook) + { + const string& strAddress = item.first; + const string& strName = item.second; + if (strName == strAccount) + { + // We're only adding valid bitcoin addresses and not ip addresses + CScript scriptPubKey; + if (scriptPubKey.SetBitcoinAddress(strAddress)) + ret.push_back(strAddress); + } + } + } + return ret; +} + +Value settxfee(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "settxfee \n" + " is a real and is rounded to the nearest 0.00000001"); + + // Amount + int64 nAmount = 0; + if (params[0].get_real() != 0.0) + nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts + + nTransactionFee = nAmount; + return true; +} + +Value sendtoaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "sendtoaddress [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.00000001"); + + string strAddress = params[0].get_str(); + + // Amount + int64 nAmount = AmountFromValue(params[1]); + + // Wallet comments + CWalletTx wtx; + if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty()) + wtx.mapValue["comment"] = params[2].get_str(); + if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) + wtx.mapValue["to"] = params[3].get_str(); + + CRITICAL_BLOCK(cs_main) + { + string strError = SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); + if (strError != "") + throw JSONRPCError(-4, strError); + } + + return wtx.GetHash().GetHex(); +} + + +Value getreceivedbyaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getreceivedbyaddress [minconf=1]\n" + "Returns the total amount received by in transactions with at least [minconf] confirmations."); + + // Bitcoin address + string strAddress = params[0].get_str(); + CScript scriptPubKey; + if (!scriptPubKey.SetBitcoinAddress(strAddress)) + throw JSONRPCError(-5, "Invalid bitcoin address"); + if (!IsMine(scriptPubKey)) + return (double)0.0; + + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + // Tally + int64 nAmount = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + } + + return ValueFromAmount(nAmount); +} + + +void GetAccountPubKeys(string strAccount, set& setPubKey) +{ + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook) + { + const string& strAddress = item.first; + const string& strName = item.second; + if (strName == strAccount) + { + // We're only counting our own valid bitcoin addresses and not ip addresses + CScript scriptPubKey; + if (scriptPubKey.SetBitcoinAddress(strAddress)) + if (IsMine(scriptPubKey)) + setPubKey.insert(scriptPubKey); + } + } + } +} + + +Value getreceivedbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getreceivedbyaccount [minconf=1]\n" + "Returns the total amount received by addresses with in transactions with at least [minconf] confirmations."); + + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + // Get the set of pub keys that have the label + string strAccount = AccountFromValue(params[0]); + set setPubKey; + GetAccountPubKeys(strAccount, setPubKey); + + // Tally + int64 nAmount = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (setPubKey.count(txout.scriptPubKey)) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + } + + return (double)nAmount / (double)COIN; +} + + +int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) +{ + int64 nBalance = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + // Tally wallet transactions + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!wtx.IsFinal()) + continue; + + int64 nGenerated, nReceived, nSent, nFee; + wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee); + + if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) + nBalance += nReceived; + nBalance += nGenerated - nSent - nFee; + } + + // Tally internal accounting entries + nBalance += walletdb.GetAccountCreditDebit(strAccount); + } + + return nBalance; +} + +int64 GetAccountBalance(const string& strAccount, int nMinDepth) +{ + CWalletDB walletdb; + return GetAccountBalance(walletdb, strAccount, nMinDepth); +} + + +Value getbalance(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 0 || params.size() > 2) + throw runtime_error( + "getbalance [account] [minconf=1]\n" + "If [account] is not specified, returns the server's total available balance.\n" + "If [account] is specified, returns the balance in the account."); + + if (params.size() == 0) + return ValueFromAmount(GetBalance()); + + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + if (params[0].get_str() == "*") { + // Calculate total balance a different way from GetBalance() + // (GetBalance() sums up all unspent TxOuts) + // getbalance and getbalance '*' should always return the same number. + int64 nBalance = 0; + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!wtx.IsFinal()) + continue; + + int64 allGeneratedImmature, allGeneratedMature, allFee; + allGeneratedImmature = allGeneratedMature = allFee = 0; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); + if (wtx.GetDepthInMainChain() >= nMinDepth) + BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) + nBalance += r.second; + BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listSent) + nBalance -= r.second; + nBalance -= allFee; + nBalance += allGeneratedMature; + } + return ValueFromAmount(nBalance); + } + + string strAccount = AccountFromValue(params[0]); + + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + + return ValueFromAmount(nBalance); +} + + +Value movecmd(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 5) + throw runtime_error( + "move [minconf=1] [comment]\n" + "Move from one account in your wallet to another."); + + string strFrom = AccountFromValue(params[0]); + string strTo = AccountFromValue(params[1]); + int64 nAmount = AmountFromValue(params[2]); + int nMinDepth = 1; + if (params.size() > 3) + nMinDepth = params[3].get_int(); + string strComment; + if (params.size() > 4) + strComment = params[4].get_str(); + + CRITICAL_BLOCK(cs_mapWallet) + { + CWalletDB walletdb; + walletdb.TxnBegin(); + + int64 nNow = GetAdjustedTime(); + + // Debit + CAccountingEntry debit; + debit.strAccount = strFrom; + debit.nCreditDebit = -nAmount; + debit.nTime = nNow; + debit.strOtherAccount = strTo; + debit.strComment = strComment; + walletdb.WriteAccountingEntry(debit); + + // Credit + CAccountingEntry credit; + credit.strAccount = strTo; + credit.nCreditDebit = nAmount; + credit.nTime = nNow; + credit.strOtherAccount = strFrom; + credit.strComment = strComment; + walletdb.WriteAccountingEntry(credit); + + walletdb.TxnCommit(); + } + return true; +} + + +Value sendfrom(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 6) + throw runtime_error( + "sendfrom [minconf=1] [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.00000001"); + + string strAccount = AccountFromValue(params[0]); + string strAddress = params[1].get_str(); + int64 nAmount = AmountFromValue(params[2]); + int nMinDepth = 1; + if (params.size() > 3) + nMinDepth = params[3].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) + wtx.mapValue["comment"] = params[4].get_str(); + if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) + wtx.mapValue["to"] = params[5].get_str(); + + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + { + // Check funds + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + if (nAmount > nBalance) + throw JSONRPCError(-6, "Account has insufficient funds"); + + // Send + string strError = SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); + if (strError != "") + throw JSONRPCError(-4, strError); + } + + return wtx.GetHash().GetHex(); +} + +Value sendmany(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "sendmany {address:amount,...} [minconf=1] [comment]\n" + "amounts are double-precision floating point numbers"); + + string strAccount = AccountFromValue(params[0]); + Object sendTo = params[1].get_obj(); + int nMinDepth = 1; + if (params.size() > 2) + nMinDepth = params[2].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) + wtx.mapValue["comment"] = params[3].get_str(); + + set setAddress; + vector > vecSend; + + int64 totalAmount = 0; + BOOST_FOREACH(const Pair& s, sendTo) + { + uint160 hash160; + string strAddress = s.name_; + + if (setAddress.count(strAddress)) + throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+strAddress); + setAddress.insert(strAddress); + + CScript scriptPubKey; + if (!scriptPubKey.SetBitcoinAddress(strAddress)) + throw JSONRPCError(-5, string("Invalid bitcoin address:")+strAddress); + int64 nAmount = AmountFromValue(s.value_); + totalAmount += nAmount; + + vecSend.push_back(make_pair(scriptPubKey, nAmount)); + } + + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + { + // Check funds + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + if (totalAmount > nBalance) + throw JSONRPCError(-6, "Account has insufficient funds"); + + // Send + CReserveKey keyChange; + int64 nFeeRequired = 0; + bool fCreated = CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); + if (!fCreated) + { + if (totalAmount + nFeeRequired > GetBalance()) + throw JSONRPCError(-6, "Insufficient funds"); + throw JSONRPCError(-4, "Transaction creation failed"); + } + if (!CommitTransaction(wtx, keyChange)) + throw JSONRPCError(-4, "Transaction commit failed"); + } + + return wtx.GetHash().GetHex(); +} + + +struct tallyitem +{ + int64 nAmount; + int nConf; + tallyitem() + { + nAmount = 0; + nConf = INT_MAX; + } +}; + +Value ListReceived(const Array& params, bool fByAccounts) +{ + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + // Whether to include empty accounts + bool fIncludeEmpty = false; + if (params.size() > 1) + fIncludeEmpty = params[1].get_bool(); + + // Tally + map mapTally; + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + int nDepth = wtx.GetDepthInMainChain(); + if (nDepth < nMinDepth) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + // Only counting our own bitcoin addresses and not ip addresses + uint160 hash160 = txout.scriptPubKey.GetBitcoinAddressHash160(); + if (hash160 == 0 || !mapPubKeys.count(hash160)) // IsMine + continue; + + tallyitem& item = mapTally[hash160]; + item.nAmount += txout.nValue; + item.nConf = min(item.nConf, nDepth); + } + } + } + + // Reply + Array ret; + map mapAccountTally; + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook) + { + const string& strAddress = item.first; + const string& strAccount = item.second; + uint160 hash160; + if (!AddressToHash160(strAddress, hash160)) + continue; + map::iterator it = mapTally.find(hash160); + if (it == mapTally.end() && !fIncludeEmpty) + continue; + + int64 nAmount = 0; + int nConf = INT_MAX; + if (it != mapTally.end()) + { + nAmount = (*it).second.nAmount; + nConf = (*it).second.nConf; + } + + if (fByAccounts) + { + tallyitem& item = mapAccountTally[strAccount]; + item.nAmount += nAmount; + item.nConf = min(item.nConf, nConf); + } + else + { + Object obj; + obj.push_back(Pair("address", strAddress)); + obj.push_back(Pair("account", strAccount)); + obj.push_back(Pair("label", strAccount)); // deprecated + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); + ret.push_back(obj); + } + } + } + + if (fByAccounts) + { + for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) + { + int64 nAmount = (*it).second.nAmount; + int nConf = (*it).second.nConf; + Object obj; + obj.push_back(Pair("account", (*it).first)); + obj.push_back(Pair("label", (*it).first)); // deprecated + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); + ret.push_back(obj); + } + } + + return ret; +} + +Value listreceivedbyaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "listreceivedbyaddress [minconf=1] [includeempty=false]\n" + "[minconf] is the minimum number of confirmations before payments are included.\n" + "[includeempty] whether to include addresses that haven't received any payments.\n" + "Returns an array of objects containing:\n" + " \"address\" : receiving address\n" + " \"account\" : the account of the receiving address\n" + " \"amount\" : total amount received by the address\n" + " \"confirmations\" : number of confirmations of the most recent transaction included"); + + return ListReceived(params, false); +} + +Value listreceivedbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "listreceivedbyaccount [minconf=1] [includeempty=false]\n" + "[minconf] is the minimum number of confirmations before payments are included.\n" + "[includeempty] whether to include accounts that haven't received any payments.\n" + "Returns an array of objects containing:\n" + " \"account\" : the account of the receiving addresses\n" + " \"amount\" : total amount received by addresses with this account\n" + " \"confirmations\" : number of confirmations of the most recent transaction included"); + + return ListReceived(params, true); +} + +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret) +{ + int64 nGeneratedImmature, nGeneratedMature, nFee; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); + + bool fAllAccounts = (strAccount == string("*")); + + // Generated blocks assigned to account "" + if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == "")) + { + Object entry; + entry.push_back(Pair("account", string(""))); + if (nGeneratedImmature) + { + entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan")); + entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature))); + } + else + { + entry.push_back(Pair("category", "generate")); + entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature))); + } + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + + // Sent + if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) + { + BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) + { + Object entry; + entry.push_back(Pair("account", strSentAccount)); + entry.push_back(Pair("address", s.first)); + entry.push_back(Pair("category", "send")); + entry.push_back(Pair("amount", ValueFromAmount(-s.second))); + entry.push_back(Pair("fee", ValueFromAmount(-nFee))); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + + // Received + if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) + { + string account; + if (mapAddressBook.count(r.first)) + account = mapAddressBook[r.first]; + if (fAllAccounts || (account == strAccount)) + { + Object entry; + entry.push_back(Pair("account", account)); + entry.push_back(Pair("address", r.first)); + entry.push_back(Pair("category", "receive")); + entry.push_back(Pair("amount", ValueFromAmount(r.second))); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + } + +} + +void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) +{ + bool fAllAccounts = (strAccount == string("*")); + + if (fAllAccounts || acentry.strAccount == strAccount) + { + Object entry; + entry.push_back(Pair("account", acentry.strAccount)); + entry.push_back(Pair("category", "move")); + entry.push_back(Pair("time", (boost::int64_t)acentry.nTime)); + entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); + entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); + entry.push_back(Pair("comment", acentry.strComment)); + ret.push_back(entry); + } +} + +Value listtransactions(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 3) + throw runtime_error( + "listtransactions [account] [count=10] [from=0]\n" + "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account]."); + + string strAccount = "*"; + if (params.size() > 0) + strAccount = params[0].get_str(); + int nCount = 10; + if (params.size() > 1) + nCount = params[1].get_int(); + int nFrom = 0; + if (params.size() > 2) + nFrom = params[2].get_int(); + + Array ret; + CWalletDB walletdb; + + CRITICAL_BLOCK(cs_mapWallet) + { + // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap: + typedef pair TxPair; + typedef multimap TxItems; + TxItems txByTime; + + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0))); + } + list acentries; + walletdb.ListAccountCreditDebit(strAccount, acentries); + BOOST_FOREACH(CAccountingEntry& entry, acentries) + { + txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + } + + // Now: iterate backwards until we have nCount items to return: + TxItems::reverse_iterator it = txByTime.rbegin(); + for (std::advance(it, nFrom); it != txByTime.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx != 0) + ListTransactions(*pwtx, strAccount, 0, true, ret); + CAccountingEntry *const pacentry = (*it).second.second; + if (pacentry != 0) + AcentryToJSON(*pacentry, strAccount, ret); + + if (ret.size() >= nCount) break; + } + // ret is now newest to oldest + } + + // Make sure we return only last nCount items (sends-to-self might give us an extra): + if (ret.size() > nCount) + { + Array::iterator last = ret.begin(); + std::advance(last, nCount); + ret.erase(last, ret.end()); + } + std::reverse(ret.begin(), ret.end()); // oldest to newest + + return ret; +} + +Value listaccounts(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "listaccounts [minconf=1]\n" + "Returns Object that has account names as keys, account balances as values."); + + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + map mapAccountBalances; + CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& entry, mapAddressBook) { + uint160 hash160; + if(AddressToHash160(entry.first, hash160) && mapPubKeys.count(hash160)) // This address belongs to me + mapAccountBalances[entry.second] = 0; + } + + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + int64 nGeneratedImmature, nGeneratedMature, nFee; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); + mapAccountBalances[strSentAccount] -= nFee; + BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) + mapAccountBalances[strSentAccount] -= s.second; + if (wtx.GetDepthInMainChain() >= nMinDepth) + { + mapAccountBalances[""] += nGeneratedMature; + BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) + if (mapAddressBook.count(r.first)) + mapAccountBalances[mapAddressBook[r.first]] += r.second; + else + mapAccountBalances[""] += r.second; + } + } + } + + list acentries; + CWalletDB().ListAccountCreditDebit("*", acentries); + BOOST_FOREACH(const CAccountingEntry& entry, acentries) + mapAccountBalances[entry.strAccount] += entry.nCreditDebit; + + Object ret; + BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) { + ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); + } + return ret; +} + +Value gettransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "gettransaction \n" + "Get detailed information about "); + + uint256 hash; + hash.SetHex(params[0].get_str()); + + Object entry; + CRITICAL_BLOCK(cs_mapWallet) + { + if (!mapWallet.count(hash)) + throw JSONRPCError(-5, "Invalid or non-wallet transaction id"); + const CWalletTx& wtx = mapWallet[hash]; + + int64 nCredit = wtx.GetCredit(); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); + + entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); + if (wtx.IsFromMe()) + entry.push_back(Pair("fee", ValueFromAmount(nFee))); + + WalletTxToJSON(mapWallet[hash], entry); + + Array details; + ListTransactions(mapWallet[hash], "*", 0, false, details); + entry.push_back(Pair("details", details)); + } + + return entry; +} + + +Value backupwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "backupwallet \n" + "Safely copies wallet.dat to destination, which can be a directory or a path with filename."); + + string strDest = params[0].get_str(); + BackupWallet(strDest); + + return Value::null; +} + + +Value validateaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "validateaddress \n" + "Return information about ."); + + string strAddress = params[0].get_str(); + uint160 hash160; + bool isValid = AddressToHash160(strAddress, hash160); + + Object ret; + ret.push_back(Pair("isvalid", isValid)); + if (isValid) + { + // Call Hash160ToAddress() so we always return current ADDRESSVERSION + // version of the address: + string currentAddress = Hash160ToAddress(hash160); + ret.push_back(Pair("address", currentAddress)); + ret.push_back(Pair("ismine", (mapPubKeys.count(hash160) > 0))); + CRITICAL_BLOCK(cs_mapAddressBook) + { + if (mapAddressBook.count(currentAddress)) + ret.push_back(Pair("account", mapAddressBook[currentAddress])); + } + } + return ret; +} + + +Value getwork(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getwork [data]\n" + "If [data] is not specified, returns formatted hash data to work on:\n" + " \"midstate\" : precomputed hash state after hashing the first half of the data\n" + " \"data\" : block data\n" + " \"hash1\" : formatted hash buffer for second hash\n" + " \"target\" : little endian hash target\n" + "If [data] is specified, tries to solve the block and returns true if it was successful."); + + if (vNodes.empty()) + throw JSONRPCError(-9, "Bitcoin is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(-10, "Bitcoin is downloading blocks..."); + + static map > mapNewBlock; + static vector vNewBlock; + static CReserveKey reservekey; + + if (params.size() == 0) + { + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64 nStart; + static CBlock* pblock; + if (pindexPrev != pindexBest || + (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) + { + if (pindexPrev != pindexBest) + { + // Deallocate old blocks since they're obsolete now + mapNewBlock.clear(); + BOOST_FOREACH(CBlock* pblock, vNewBlock) + delete pblock; + vNewBlock.clear(); + } + nTransactionsUpdatedLast = nTransactionsUpdated; + pindexPrev = pindexBest; + nStart = GetTime(); + + // Create new block + pblock = CreateNewBlock(reservekey); + if (!pblock) + throw JSONRPCError(-7, "Out of memory"); + vNewBlock.push_back(pblock); + } + + // Update nTime + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->nNonce = 0; + + // Update nExtraNonce + static unsigned int nExtraNonce = 0; + static int64 nPrevTime = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, nPrevTime); + + // Save + mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, nExtraNonce); + + // Prebuild hash buffers + char pmidstate[32]; + char pdata[128]; + char phash1[64]; + FormatHashBuffers(pblock, pmidstate, pdata, phash1); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + Object result; + result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); + result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); + result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); + result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); + return result; + } + else + { + // Parse parameters + vector vchData = ParseHex(params[0].get_str()); + if (vchData.size() != 128) + throw JSONRPCError(-8, "Invalid parameter"); + CBlock* pdata = (CBlock*)&vchData[0]; + + // Byte reverse + for (int i = 0; i < 128/4; i++) + ((unsigned int*)pdata)[i] = CryptoPP::ByteReverse(((unsigned int*)pdata)[i]); + + // Get saved block + if (!mapNewBlock.count(pdata->hashMerkleRoot)) + return false; + CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; + unsigned int nExtraNonce = mapNewBlock[pdata->hashMerkleRoot].second; + + pblock->nTime = pdata->nTime; + pblock->nNonce = pdata->nNonce; + pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + + return CheckWork(pblock, reservekey); + } +} + + + + + + + + + + + +// +// Call Table +// + +pair pCallTable[] = +{ + make_pair("help", &help), + make_pair("stop", &stop), + make_pair("getblockcount", &getblockcount), + make_pair("getblocknumber", &getblocknumber), + make_pair("getconnectioncount", &getconnectioncount), + make_pair("getdifficulty", &getdifficulty), + make_pair("getgenerate", &getgenerate), + make_pair("setgenerate", &setgenerate), + make_pair("gethashespersec", &gethashespersec), + make_pair("getinfo", &getinfo), + make_pair("getnewaddress", &getnewaddress), + make_pair("getaccountaddress", &getaccountaddress), + make_pair("setaccount", &setaccount), + make_pair("setlabel", &setaccount), // deprecated + make_pair("getaccount", &getaccount), + make_pair("getlabel", &getaccount), // deprecated + make_pair("getaddressesbyaccount", &getaddressesbyaccount), + make_pair("getaddressesbylabel", &getaddressesbyaccount), // deprecated + make_pair("sendtoaddress", &sendtoaddress), + make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress + make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress + make_pair("getreceivedbyaddress", &getreceivedbyaddress), + make_pair("getreceivedbyaccount", &getreceivedbyaccount), + make_pair("getreceivedbylabel", &getreceivedbyaccount), // deprecated + make_pair("listreceivedbyaddress", &listreceivedbyaddress), + make_pair("listreceivedbyaccount", &listreceivedbyaccount), + make_pair("listreceivedbylabel", &listreceivedbyaccount), // deprecated + make_pair("backupwallet", &backupwallet), + make_pair("validateaddress", &validateaddress), + make_pair("getbalance", &getbalance), + make_pair("move", &movecmd), + make_pair("sendfrom", &sendfrom), + make_pair("sendmany", &sendmany), + make_pair("gettransaction", &gettransaction), + make_pair("listtransactions", &listtransactions), + make_pair("getwork", &getwork), + make_pair("listaccounts", &listaccounts), + make_pair("settxfee", &settxfee), +}; +map mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0])); + +string pAllowInSafeMode[] = +{ + "help", + "stop", + "getblockcount", + "getblocknumber", + "getconnectioncount", + "getdifficulty", + "getgenerate", + "setgenerate", + "gethashespersec", + "getinfo", + "getnewaddress", + "getaccountaddress", + "setlabel", + "getaccount", + "getlabel", // deprecated + "getaddressesbyaccount", + "getaddressesbylabel", // deprecated + "backupwallet", + "validateaddress", + "getwork", +}; +set setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0])); + + + + +// +// HTTP protocol +// +// This ain't Apache. We're just using HTTP header for the length field +// and to be compatible with other JSON-RPC implementations. +// + +string HTTPPost(const string& strMsg, const map& mapRequestHeaders) +{ + ostringstream s; + s << "POST / HTTP/1.1\r\n" + << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n" + << "Host: 127.0.0.1\r\n" + << "Content-Type: application/json\r\n" + << "Content-Length: " << strMsg.size() << "\r\n" + << "Accept: application/json\r\n"; + BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders) + s << item.first << ": " << item.second << "\r\n"; + s << "\r\n" << strMsg; + + return s.str(); +} + +string rfc1123Time() +{ + char buffer[64]; + time_t now; + time(&now); + struct tm* now_gmt = gmtime(&now); + string locale(setlocale(LC_TIME, NULL)); + setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings + strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); + setlocale(LC_TIME, locale.c_str()); + return string(buffer); +} + +string HTTPReply(int nStatus, const string& strMsg) +{ + if (nStatus == 401) + return strprintf("HTTP/1.0 401 Authorization Required\r\n" + "Date: %s\r\n" + "Server: bitcoin-json-rpc/%s\r\n" + "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 296\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Error\r\n" + "\r\n" + "\r\n" + "

401 Unauthorized.

\r\n" + "\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); + string strStatus; + if (nStatus == 200) strStatus = "OK"; + else if (nStatus == 400) strStatus = "Bad Request"; + else if (nStatus == 404) strStatus = "Not Found"; + else if (nStatus == 500) strStatus = "Internal Server Error"; + return strprintf( + "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Connection: close\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/json\r\n" + "Server: bitcoin-json-rpc/%s\r\n" + "\r\n" + "%s", + nStatus, + strStatus.c_str(), + rfc1123Time().c_str(), + strMsg.size(), + FormatFullVersion().c_str(), + strMsg.c_str()); +} + +int ReadHTTPStatus(std::basic_istream& stream) +{ + string str; + getline(stream, str); + vector vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return 500; + return atoi(vWords[1].c_str()); +} + +int ReadHTTPHeader(std::basic_istream& stream, map& mapHeadersRet) +{ + int nLen = 0; + loop + { + string str; + std::getline(stream, str); + if (str.empty() || str == "\r") + break; + string::size_type nColon = str.find(":"); + if (nColon != string::npos) + { + string strHeader = str.substr(0, nColon); + boost::trim(strHeader); + boost::to_lower(strHeader); + string strValue = str.substr(nColon+1); + boost::trim(strValue); + mapHeadersRet[strHeader] = strValue; + if (strHeader == "content-length") + nLen = atoi(strValue.c_str()); + } + } + return nLen; +} + +int ReadHTTP(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet) +{ + mapHeadersRet.clear(); + strMessageRet = ""; + + // Read status + int nStatus = ReadHTTPStatus(stream); + + // Read header + int nLen = ReadHTTPHeader(stream, mapHeadersRet); + if (nLen < 0 || nLen > MAX_SIZE) + return 500; + + // Read message + if (nLen > 0) + { + vector vch(nLen); + stream.read(&vch[0], nLen); + strMessageRet = string(vch.begin(), vch.end()); + } + + return nStatus; +} + +string EncodeBase64(string s) +{ + BIO *b64, *bmem; + BUF_MEM *bptr; + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_new(BIO_s_mem()); + b64 = BIO_push(b64, bmem); + BIO_write(b64, s.c_str(), s.size()); + BIO_flush(b64); + BIO_get_mem_ptr(b64, &bptr); + + string result(bptr->data, bptr->length); + BIO_free_all(b64); + + return result; +} + +string DecodeBase64(string s) +{ + BIO *b64, *bmem; + + char* buffer = static_cast(calloc(s.size(), sizeof(char))); + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_new_mem_buf(const_cast(s.c_str()), s.size()); + bmem = BIO_push(b64, bmem); + BIO_read(bmem, buffer, s.size()); + BIO_free_all(bmem); + + string result(buffer); + free(buffer); + return result; +} + +bool HTTPAuthorized(map& mapHeaders) +{ + string strAuth = mapHeaders["authorization"]; + if (strAuth.substr(0,6) != "Basic ") + return false; + string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); + string strUserPass = DecodeBase64(strUserPass64); + string::size_type nColon = strUserPass.find(":"); + if (nColon == string::npos) + return false; + string strUser = strUserPass.substr(0, nColon); + string strPassword = strUserPass.substr(nColon+1); + return (strUser == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]); +} + +// +// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, +// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were +// unspecified (HTTP errors and contents of 'error'). +// +// 1.0 spec: http://json-rpc.org/wiki/specification +// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http +// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx +// + +string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) +{ + Object request; + request.push_back(Pair("method", strMethod)); + request.push_back(Pair("params", params)); + request.push_back(Pair("id", id)); + return write_string(Value(request), false) + "\n"; +} + +string JSONRPCReply(const Value& result, const Value& error, const Value& id) +{ + Object reply; + if (error.type() != null_type) + reply.push_back(Pair("result", Value::null)); + else + reply.push_back(Pair("result", result)); + reply.push_back(Pair("error", error)); + reply.push_back(Pair("id", id)); + return write_string(Value(reply), false) + "\n"; +} + +void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) +{ + // Send error reply from json-rpc error object + int nStatus = 500; + int code = find_value(objError, "code").get_int(); + if (code == -32600) nStatus = 400; + else if (code == -32601) nStatus = 404; + string strReply = JSONRPCReply(Value::null, objError, id); + stream << HTTPReply(nStatus, strReply) << std::flush; +} + +bool ClientAllowed(const string& strAddress) +{ + if (strAddress == asio::ip::address_v4::loopback().to_string()) + return true; + const vector& vAllow = mapMultiArgs["-rpcallowip"]; + BOOST_FOREACH(string strAllow, vAllow) + if (WildcardMatch(strAddress, strAllow)) + return true; + return false; +} + +#ifdef USE_SSL +// +// IOStream device that speaks SSL but can also speak non-SSL +// +class SSLIOStreamDevice : public iostreams::device { +public: + SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn) + { + fUseSSL = fUseSSLIn; + fNeedHandshake = fUseSSLIn; + } + + void handshake(ssl::stream_base::handshake_type role) + { + if (!fNeedHandshake) return; + fNeedHandshake = false; + stream.handshake(role); + } + std::streamsize read(char* s, std::streamsize n) + { + handshake(ssl::stream_base::server); // HTTPS servers read first + if (fUseSSL) return stream.read_some(asio::buffer(s, n)); + return stream.next_layer().read_some(asio::buffer(s, n)); + } + std::streamsize write(const char* s, std::streamsize n) + { + handshake(ssl::stream_base::client); // HTTPS clients write first + if (fUseSSL) return asio::write(stream, asio::buffer(s, n)); + return asio::write(stream.next_layer(), asio::buffer(s, n)); + } + bool connect(const std::string& server, const std::string& port) + { + ip::tcp::resolver resolver(stream.get_io_service()); + ip::tcp::resolver::query query(server.c_str(), port.c_str()); + ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); + ip::tcp::resolver::iterator end; + boost::system::error_code error = asio::error::host_not_found; + while (error && endpoint_iterator != end) + { + stream.lowest_layer().close(); + stream.lowest_layer().connect(*endpoint_iterator++, error); + } + if (error) + return false; + return true; + } + +private: + bool fNeedHandshake; + bool fUseSSL; + SSLStream& stream; +}; +#endif + +void ThreadRPCServer(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg)); + try + { + vnThreadsRunning[4]++; + ThreadRPCServer2(parg); + vnThreadsRunning[4]--; + } + catch (std::exception& e) { + vnThreadsRunning[4]--; + PrintException(&e, "ThreadRPCServer()"); + } catch (...) { + vnThreadsRunning[4]--; + PrintException(NULL, "ThreadRPCServer()"); + } + printf("ThreadRPCServer exiting\n"); +} + +void ThreadRPCServer2(void* parg) +{ + printf("ThreadRPCServer started\n"); + + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + { + string strWhatAmI = "To use bitcoind"; + if (mapArgs.count("-server")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); + else if (mapArgs.count("-daemon")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); + PrintConsole( + _("Warning: %s, you must set rpcpassword=\nin the configuration file: %s\n" + "If the file does not exist, create it with owner-readable-only file permissions.\n"), + strWhatAmI.c_str(), + GetConfigFile().c_str()); + CreateThread(Shutdown, NULL); + return; + } + + bool fUseSSL = GetBoolArg("-rpcssl"); + asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); + + asio::io_service io_service; + ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332)); + ip::tcp::acceptor acceptor(io_service, endpoint); + + acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + +#ifdef USE_SSL + ssl::context context(io_service, ssl::context::sslv23); + if (fUseSSL) + { + context.set_options(ssl::context::no_sslv2); + filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert"); + if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile; + if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str()); + else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str()); + filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem"); + if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile; + if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem); + else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str()); + + string ciphers = GetArg("-rpcsslciphers", + "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); + SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str()); + } +#else + if (fUseSSL) + throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); +#endif + + loop + { + // Accept connection +#ifdef USE_SSL + SSLStream sslStream(io_service, context); + SSLIOStreamDevice d(sslStream, fUseSSL); + iostreams::stream stream(d); +#else + ip::tcp::iostream stream; +#endif + + ip::tcp::endpoint peer; + vnThreadsRunning[4]--; +#ifdef USE_SSL + acceptor.accept(sslStream.lowest_layer(), peer); +#else + acceptor.accept(*stream.rdbuf(), peer); +#endif + vnThreadsRunning[4]++; + if (fShutdown) + return; + + // Restrict callers by IP + if (!ClientAllowed(peer.address().to_string())) + continue; + + map mapHeaders; + string strRequest; + + boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest)); + if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30)))) + { // Timed out: + acceptor.cancel(); + printf("ThreadRPCServer ReadHTTP timeout\n"); + continue; + } + + // Check authorization + if (mapHeaders.count("authorization") == 0) + { + stream << HTTPReply(401, "") << std::flush; + continue; + } + if (!HTTPAuthorized(mapHeaders)) + { + // Deter brute-forcing short passwords + if (mapArgs["-rpcpassword"].size() < 15) + Sleep(50); + + stream << HTTPReply(401, "") << std::flush; + printf("ThreadRPCServer incorrect password attempt\n"); + continue; + } + + Value id = Value::null; + try + { + // Parse request + Value valRequest; + if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type) + throw JSONRPCError(-32700, "Parse error"); + const Object& request = valRequest.get_obj(); + + // Parse id now so errors from here on will have the id + id = find_value(request, "id"); + + // Parse method + Value valMethod = find_value(request, "method"); + if (valMethod.type() == null_type) + throw JSONRPCError(-32600, "Missing method"); + if (valMethod.type() != str_type) + throw JSONRPCError(-32600, "Method must be a string"); + string strMethod = valMethod.get_str(); + if (strMethod != "getwork") + printf("ThreadRPCServer method=%s\n", strMethod.c_str()); + + // Parse params + Value valParams = find_value(request, "params"); + Array params; + if (valParams.type() == array_type) + params = valParams.get_array(); + else if (valParams.type() == null_type) + params = Array(); + else + throw JSONRPCError(-32600, "Params must be an array"); + + // Find method + map::iterator mi = mapCallTable.find(strMethod); + if (mi == mapCallTable.end()) + throw JSONRPCError(-32601, "Method not found"); + + // Observe safe mode + string strWarning = GetWarnings("rpc"); + if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod)) + throw JSONRPCError(-2, string("Safe mode: ") + strWarning); + + try + { + // Execute + Value result = (*(*mi).second)(params, false); + + // Send reply + string strReply = JSONRPCReply(result, Value::null, id); + stream << HTTPReply(200, strReply) << std::flush; + } + catch (std::exception& e) + { + ErrorReply(stream, JSONRPCError(-1, e.what()), id); + } + } + catch (Object& objError) + { + ErrorReply(stream, objError, id); + } + catch (std::exception& e) + { + ErrorReply(stream, JSONRPCError(-32700, e.what()), id); + } + } +} + + + + +Object CallRPC(const string& strMethod, const Array& params) +{ + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + throw runtime_error(strprintf( + _("You must set rpcpassword= in the configuration file:\n%s\n" + "If the file does not exist, create it with owner-readable-only file permissions."), + GetConfigFile().c_str())); + + // Connect to localhost + bool fUseSSL = GetBoolArg("-rpcssl"); +#ifdef USE_SSL + asio::io_service io_service; + ssl::context context(io_service, ssl::context::sslv23); + context.set_options(ssl::context::no_sslv2); + SSLStream sslStream(io_service, context); + SSLIOStreamDevice d(sslStream, fUseSSL); + iostreams::stream stream(d); + if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"))) + throw runtime_error("couldn't connect to server"); +#else + if (fUseSSL) + throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); + + ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")); + if (stream.fail()) + throw runtime_error("couldn't connect to server"); +#endif + + + // HTTP basic authentication + string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); + map mapRequestHeaders; + mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; + + // Send request + string strRequest = JSONRPCRequest(strMethod, params, 1); + string strPost = HTTPPost(strRequest, mapRequestHeaders); + stream << strPost << std::flush; + + // Receive reply + map mapHeaders; + string strReply; + int nStatus = ReadHTTP(stream, mapHeaders, strReply); + if (nStatus == 401) + throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500) + throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); + else if (strReply.empty()) + throw runtime_error("no response from server"); + + // Parse reply + Value valReply; + if (!read_string(strReply, valReply)) + throw runtime_error("couldn't parse reply from server"); + const Object& reply = valReply.get_obj(); + if (reply.empty()) + throw runtime_error("expected reply to have result, error and id properties"); + + return reply; +} + + + + +template +void ConvertTo(Value& value) +{ + if (value.type() == str_type) + { + // reinterpret string as unquoted json value + Value value2; + if (!read_string(value.get_str(), value2)) + throw runtime_error("type mismatch"); + value = value2.get_value(); + } + else + { + value = value.get_value(); + } +} + +int CommandLineRPC(int argc, char *argv[]) +{ + string strPrint; + int nRet = 0; + try + { + // Skip switches + while (argc > 1 && IsSwitchChar(argv[1][0])) + { + argc--; + argv++; + } + + // Method + if (argc < 2) + throw runtime_error("too few parameters"); + string strMethod = argv[1]; + + // Parameters default to strings + Array params; + for (int i = 2; i < argc; i++) + params.push_back(argv[i]); + int n = params.size(); + + // + // Special case non-string parameter types + // + if (strMethod == "setgenerate" && n > 0) ConvertTo(params[0]); + if (strMethod == "setgenerate" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); + if (strMethod == "getamountreceived" && n > 1) ConvertTo(params[1]); // deprecated + if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo(params[1]); + if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated + if (strMethod == "getallreceived" && n > 0) ConvertTo(params[0]); // deprecated + if (strMethod == "getallreceived" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo(params[0]); + if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo(params[0]); // deprecated + if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated + if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); + if (strMethod == "move" && n > 2) ConvertTo(params[2]); + if (strMethod == "move" && n > 3) ConvertTo(params[3]); + if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]); + if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); + if (strMethod == "listtransactions" && n > 1) ConvertTo(params[1]); + if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); + if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); + if (strMethod == "sendmany" && n > 1) + { + string s = params[1].get_str(); + Value v; + if (!read_string(s, v) || v.type() != obj_type) + throw runtime_error("type mismatch"); + params[1] = v.get_obj(); + } + if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + + // Execute + Object reply = CallRPC(strMethod, params); + + // Parse reply + const Value& result = find_value(reply, "result"); + const Value& error = find_value(reply, "error"); + const Value& id = find_value(reply, "id"); + + if (error.type() != null_type) + { + // Error + strPrint = "error: " + write_string(error, false); + int code = find_value(error.get_obj(), "code").get_int(); + nRet = abs(code); + } + else + { + // Result + if (result.type() == null_type) + strPrint = ""; + else if (result.type() == str_type) + strPrint = result.get_str(); + else + strPrint = write_string(result, true); + } + } + catch (std::exception& e) + { + strPrint = string("error: ") + e.what(); + nRet = 87; + } + catch (...) + { + PrintException(NULL, "CommandLineRPC()"); + } + + if (strPrint != "") + { +#if defined(__WXMSW__) && defined(GUI) + // Windows GUI apps can't print to command line, + // so settle for a message box yuck + MyMessageBox(strPrint, "Bitcoin", wxOK); +#else + fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); +#endif + } + return nRet; +} + + + + +#ifdef TEST +int main(int argc, char *argv[]) +{ +#ifdef _MSC_VER + // Turn off microsoft heap dump noise + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); +#endif + setbuf(stdin, NULL); + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + try + { + if (argc >= 2 && string(argv[1]) == "-server") + { + printf("server ready\n"); + ThreadRPCServer(NULL); + } + else + { + return CommandLineRPC(argc, argv); + } + } + catch (std::exception& e) { + PrintException(&e, "main()"); + } catch (...) { + PrintException(NULL, "main()"); + } + return 0; +} +#endif diff --git a/core/src/script.cpp b/core/src/script.cpp new file mode 100644 index 0000000000..97334ca0a0 --- /dev/null +++ b/core/src/script.cpp @@ -0,0 +1,1208 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#include "headers.h" + +using namespace std; +using namespace boost; + +bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); + + + +typedef vector valtype; +static const valtype vchFalse(0); +static const valtype vchZero(0); +static const valtype vchTrue(1, 1); +static const CBigNum bnZero(0); +static const CBigNum bnOne(1); +static const CBigNum bnFalse(0); +static const CBigNum bnTrue(1); +static const size_t nMaxNumSize = 4; + + +CBigNum CastToBigNum(const valtype& vch) +{ + if (vch.size() > nMaxNumSize) + throw runtime_error("CastToBigNum() : overflow"); + // Get rid of extra leading zeros + return CBigNum(CBigNum(vch).getvch()); +} + +bool CastToBool(const valtype& vch) +{ + for (int i = 0; i < vch.size(); i++) + { + if (vch[i] != 0) + { + // Can be negative zero + if (i == vch.size()-1 && vch[i] == 0x80) + return false; + return true; + } + } + return false; +} + +void MakeSameSize(valtype& vch1, valtype& vch2) +{ + // Lengthen the shorter one + if (vch1.size() < vch2.size()) + vch1.resize(vch2.size(), 0); + if (vch2.size() < vch1.size()) + vch2.resize(vch1.size(), 0); +} + + + +// +// Script is a stack machine (like Forth) that evaluates a predicate +// returning a bool indicating valid or not. There are no loops. +// +#define stacktop(i) (stack.at(stack.size()+(i))) +#define altstacktop(i) (altstack.at(altstack.size()+(i))) +static inline void popstack(vector& stack) +{ + if (stack.empty()) + throw runtime_error("popstack() : stack empty"); + stack.pop_back(); +} + + +bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + CAutoBN_CTX pctx; + CScript::const_iterator pc = script.begin(); + CScript::const_iterator pend = script.end(); + CScript::const_iterator pbegincodehash = script.begin(); + opcodetype opcode; + valtype vchPushValue; + vector vfExec; + vector altstack; + if (script.size() > 10000) + return false; + int nOpCount = 0; + + + try + { + while (pc < pend) + { + bool fExec = !count(vfExec.begin(), vfExec.end(), false); + + // + // Read instruction + // + if (!script.GetOp(pc, opcode, vchPushValue)) + return false; + if (vchPushValue.size() > 520) + return false; + if (opcode > OP_16 && ++nOpCount > 201) + return false; + + if (opcode == OP_CAT || + opcode == OP_SUBSTR || + opcode == OP_LEFT || + opcode == OP_RIGHT || + opcode == OP_INVERT || + opcode == OP_AND || + opcode == OP_OR || + opcode == OP_XOR || + opcode == OP_2MUL || + opcode == OP_2DIV || + opcode == OP_MUL || + opcode == OP_DIV || + opcode == OP_MOD || + opcode == OP_LSHIFT || + opcode == OP_RSHIFT) + return false; + + if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) + stack.push_back(vchPushValue); + else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) + switch (opcode) + { + // + // Push value + // + case OP_1NEGATE: + case OP_1: + case OP_2: + case OP_3: + case OP_4: + case OP_5: + case OP_6: + case OP_7: + case OP_8: + case OP_9: + case OP_10: + case OP_11: + case OP_12: + case OP_13: + case OP_14: + case OP_15: + case OP_16: + { + // ( -- value) + CBigNum bn((int)opcode - (int)(OP_1 - 1)); + stack.push_back(bn.getvch()); + } + break; + + + // + // Control + // + case OP_NOP: + case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: + case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: + break; + + case OP_IF: + case OP_NOTIF: + { + // if [statements] [else [statements]] endif + bool fValue = false; + if (fExec) + { + if (stack.size() < 1) + return false; + valtype& vch = stacktop(-1); + fValue = CastToBool(vch); + if (opcode == OP_NOTIF) + fValue = !fValue; + popstack(stack); + } + vfExec.push_back(fValue); + } + break; + + case OP_ELSE: + { + if (vfExec.empty()) + return false; + vfExec.back() = !vfExec.back(); + } + break; + + case OP_ENDIF: + { + if (vfExec.empty()) + return false; + vfExec.pop_back(); + } + break; + + case OP_VERIFY: + { + // (true -- ) or + // (false -- false) and return + if (stack.size() < 1) + return false; + bool fValue = CastToBool(stacktop(-1)); + if (fValue) + popstack(stack); + else + return false; + } + break; + + case OP_RETURN: + { + return false; + } + break; + + + // + // Stack ops + // + case OP_TOALTSTACK: + { + if (stack.size() < 1) + return false; + altstack.push_back(stacktop(-1)); + popstack(stack); + } + break; + + case OP_FROMALTSTACK: + { + if (altstack.size() < 1) + return false; + stack.push_back(altstacktop(-1)); + popstack(altstack); + } + break; + + case OP_2DROP: + { + // (x1 x2 -- ) + if (stack.size() < 2) + return false; + popstack(stack); + popstack(stack); + } + break; + + case OP_2DUP: + { + // (x1 x2 -- x1 x2 x1 x2) + if (stack.size() < 2) + return false; + valtype vch1 = stacktop(-2); + valtype vch2 = stacktop(-1); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_3DUP: + { + // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) + if (stack.size() < 3) + return false; + valtype vch1 = stacktop(-3); + valtype vch2 = stacktop(-2); + valtype vch3 = stacktop(-1); + stack.push_back(vch1); + stack.push_back(vch2); + stack.push_back(vch3); + } + break; + + case OP_2OVER: + { + // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) + if (stack.size() < 4) + return false; + valtype vch1 = stacktop(-4); + valtype vch2 = stacktop(-3); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_2ROT: + { + // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) + if (stack.size() < 6) + return false; + valtype vch1 = stacktop(-6); + valtype vch2 = stacktop(-5); + stack.erase(stack.end()-6, stack.end()-4); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_2SWAP: + { + // (x1 x2 x3 x4 -- x3 x4 x1 x2) + if (stack.size() < 4) + return false; + swap(stacktop(-4), stacktop(-2)); + swap(stacktop(-3), stacktop(-1)); + } + break; + + case OP_IFDUP: + { + // (x - 0 | x x) + if (stack.size() < 1) + return false; + valtype vch = stacktop(-1); + if (CastToBool(vch)) + stack.push_back(vch); + } + break; + + case OP_DEPTH: + { + // -- stacksize + CBigNum bn(stack.size()); + stack.push_back(bn.getvch()); + } + break; + + case OP_DROP: + { + // (x -- ) + if (stack.size() < 1) + return false; + popstack(stack); + } + break; + + case OP_DUP: + { + // (x -- x x) + if (stack.size() < 1) + return false; + valtype vch = stacktop(-1); + stack.push_back(vch); + } + break; + + case OP_NIP: + { + // (x1 x2 -- x2) + if (stack.size() < 2) + return false; + stack.erase(stack.end() - 2); + } + break; + + case OP_OVER: + { + // (x1 x2 -- x1 x2 x1) + if (stack.size() < 2) + return false; + valtype vch = stacktop(-2); + stack.push_back(vch); + } + break; + + case OP_PICK: + case OP_ROLL: + { + // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) + // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) + if (stack.size() < 2) + return false; + int n = CastToBigNum(stacktop(-1)).getint(); + popstack(stack); + if (n < 0 || n >= stack.size()) + return false; + valtype vch = stacktop(-n-1); + if (opcode == OP_ROLL) + stack.erase(stack.end()-n-1); + stack.push_back(vch); + } + break; + + case OP_ROT: + { + // (x1 x2 x3 -- x2 x3 x1) + // x2 x1 x3 after first swap + // x2 x3 x1 after second swap + if (stack.size() < 3) + return false; + swap(stacktop(-3), stacktop(-2)); + swap(stacktop(-2), stacktop(-1)); + } + break; + + case OP_SWAP: + { + // (x1 x2 -- x2 x1) + if (stack.size() < 2) + return false; + swap(stacktop(-2), stacktop(-1)); + } + break; + + case OP_TUCK: + { + // (x1 x2 -- x2 x1 x2) + if (stack.size() < 2) + return false; + valtype vch = stacktop(-1); + stack.insert(stack.end()-2, vch); + } + break; + + + // + // Splice ops + // + case OP_CAT: + { + // (x1 x2 -- out) + if (stack.size() < 2) + return false; + valtype& vch1 = stacktop(-2); + valtype& vch2 = stacktop(-1); + vch1.insert(vch1.end(), vch2.begin(), vch2.end()); + popstack(stack); + if (stacktop(-1).size() > 520) + return false; + } + break; + + case OP_SUBSTR: + { + // (in begin size -- out) + if (stack.size() < 3) + return false; + valtype& vch = stacktop(-3); + int nBegin = CastToBigNum(stacktop(-2)).getint(); + int nEnd = nBegin + CastToBigNum(stacktop(-1)).getint(); + if (nBegin < 0 || nEnd < nBegin) + return false; + if (nBegin > vch.size()) + nBegin = vch.size(); + if (nEnd > vch.size()) + nEnd = vch.size(); + vch.erase(vch.begin() + nEnd, vch.end()); + vch.erase(vch.begin(), vch.begin() + nBegin); + popstack(stack); + popstack(stack); + } + break; + + case OP_LEFT: + case OP_RIGHT: + { + // (in size -- out) + if (stack.size() < 2) + return false; + valtype& vch = stacktop(-2); + int nSize = CastToBigNum(stacktop(-1)).getint(); + if (nSize < 0) + return false; + if (nSize > vch.size()) + nSize = vch.size(); + if (opcode == OP_LEFT) + vch.erase(vch.begin() + nSize, vch.end()); + else + vch.erase(vch.begin(), vch.end() - nSize); + popstack(stack); + } + break; + + case OP_SIZE: + { + // (in -- in size) + if (stack.size() < 1) + return false; + CBigNum bn(stacktop(-1).size()); + stack.push_back(bn.getvch()); + } + break; + + + // + // Bitwise logic + // + case OP_INVERT: + { + // (in - out) + if (stack.size() < 1) + return false; + valtype& vch = stacktop(-1); + for (int i = 0; i < vch.size(); i++) + vch[i] = ~vch[i]; + } + break; + + case OP_AND: + case OP_OR: + case OP_XOR: + { + // (x1 x2 - out) + if (stack.size() < 2) + return false; + valtype& vch1 = stacktop(-2); + valtype& vch2 = stacktop(-1); + MakeSameSize(vch1, vch2); + if (opcode == OP_AND) + { + for (int i = 0; i < vch1.size(); i++) + vch1[i] &= vch2[i]; + } + else if (opcode == OP_OR) + { + for (int i = 0; i < vch1.size(); i++) + vch1[i] |= vch2[i]; + } + else if (opcode == OP_XOR) + { + for (int i = 0; i < vch1.size(); i++) + vch1[i] ^= vch2[i]; + } + popstack(stack); + } + break; + + case OP_EQUAL: + case OP_EQUALVERIFY: + //case OP_NOTEQUAL: // use OP_NUMNOTEQUAL + { + // (x1 x2 - bool) + if (stack.size() < 2) + return false; + valtype& vch1 = stacktop(-2); + valtype& vch2 = stacktop(-1); + bool fEqual = (vch1 == vch2); + // OP_NOTEQUAL is disabled because it would be too easy to say + // something like n != 1 and have some wiseguy pass in 1 with extra + // zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001) + //if (opcode == OP_NOTEQUAL) + // fEqual = !fEqual; + popstack(stack); + popstack(stack); + stack.push_back(fEqual ? vchTrue : vchFalse); + if (opcode == OP_EQUALVERIFY) + { + if (fEqual) + popstack(stack); + else + return false; + } + } + break; + + + // + // Numeric + // + case OP_1ADD: + case OP_1SUB: + case OP_2MUL: + case OP_2DIV: + case OP_NEGATE: + case OP_ABS: + case OP_NOT: + case OP_0NOTEQUAL: + { + // (in -- out) + if (stack.size() < 1) + return false; + CBigNum bn = CastToBigNum(stacktop(-1)); + switch (opcode) + { + case OP_1ADD: bn += bnOne; break; + case OP_1SUB: bn -= bnOne; break; + case OP_2MUL: bn <<= 1; break; + case OP_2DIV: bn >>= 1; break; + case OP_NEGATE: bn = -bn; break; + case OP_ABS: if (bn < bnZero) bn = -bn; break; + case OP_NOT: bn = (bn == bnZero); break; + case OP_0NOTEQUAL: bn = (bn != bnZero); break; + } + popstack(stack); + stack.push_back(bn.getvch()); + } + break; + + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_MOD: + case OP_LSHIFT: + case OP_RSHIFT: + case OP_BOOLAND: + case OP_BOOLOR: + case OP_NUMEQUAL: + case OP_NUMEQUALVERIFY: + case OP_NUMNOTEQUAL: + case OP_LESSTHAN: + case OP_GREATERTHAN: + case OP_LESSTHANOREQUAL: + case OP_GREATERTHANOREQUAL: + case OP_MIN: + case OP_MAX: + { + // (x1 x2 -- out) + if (stack.size() < 2) + return false; + CBigNum bn1 = CastToBigNum(stacktop(-2)); + CBigNum bn2 = CastToBigNum(stacktop(-1)); + CBigNum bn; + switch (opcode) + { + case OP_ADD: + bn = bn1 + bn2; + break; + + case OP_SUB: + bn = bn1 - bn2; + break; + + case OP_MUL: + if (!BN_mul(&bn, &bn1, &bn2, pctx)) + return false; + break; + + case OP_DIV: + if (!BN_div(&bn, NULL, &bn1, &bn2, pctx)) + return false; + break; + + case OP_MOD: + if (!BN_mod(&bn, &bn1, &bn2, pctx)) + return false; + break; + + case OP_LSHIFT: + if (bn2 < bnZero || bn2 > CBigNum(2048)) + return false; + bn = bn1 << bn2.getulong(); + break; + + case OP_RSHIFT: + if (bn2 < bnZero || bn2 > CBigNum(2048)) + return false; + bn = bn1 >> bn2.getulong(); + break; + + case OP_BOOLAND: bn = (bn1 != bnZero && bn2 != bnZero); break; + case OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break; + case OP_NUMEQUAL: bn = (bn1 == bn2); break; + case OP_NUMEQUALVERIFY: bn = (bn1 == bn2); break; + case OP_NUMNOTEQUAL: bn = (bn1 != bn2); break; + case OP_LESSTHAN: bn = (bn1 < bn2); break; + case OP_GREATERTHAN: bn = (bn1 > bn2); break; + case OP_LESSTHANOREQUAL: bn = (bn1 <= bn2); break; + case OP_GREATERTHANOREQUAL: bn = (bn1 >= bn2); break; + case OP_MIN: bn = (bn1 < bn2 ? bn1 : bn2); break; + case OP_MAX: bn = (bn1 > bn2 ? bn1 : bn2); break; + } + popstack(stack); + popstack(stack); + stack.push_back(bn.getvch()); + + if (opcode == OP_NUMEQUALVERIFY) + { + if (CastToBool(stacktop(-1))) + popstack(stack); + else + return false; + } + } + break; + + case OP_WITHIN: + { + // (x min max -- out) + if (stack.size() < 3) + return false; + CBigNum bn1 = CastToBigNum(stacktop(-3)); + CBigNum bn2 = CastToBigNum(stacktop(-2)); + CBigNum bn3 = CastToBigNum(stacktop(-1)); + bool fValue = (bn2 <= bn1 && bn1 < bn3); + popstack(stack); + popstack(stack); + popstack(stack); + stack.push_back(fValue ? vchTrue : vchFalse); + } + break; + + + // + // Crypto + // + case OP_RIPEMD160: + case OP_SHA1: + case OP_SHA256: + case OP_HASH160: + case OP_HASH256: + { + // (in -- hash) + if (stack.size() < 1) + return false; + valtype& vch = stacktop(-1); + valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); + if (opcode == OP_RIPEMD160) + RIPEMD160(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_SHA1) + SHA1(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_SHA256) + SHA256(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_HASH160) + { + uint160 hash160 = Hash160(vch); + memcpy(&vchHash[0], &hash160, sizeof(hash160)); + } + else if (opcode == OP_HASH256) + { + uint256 hash = Hash(vch.begin(), vch.end()); + memcpy(&vchHash[0], &hash, sizeof(hash)); + } + popstack(stack); + stack.push_back(vchHash); + } + break; + + case OP_CODESEPARATOR: + { + // Hash starts after the code separator + pbegincodehash = pc; + } + break; + + case OP_CHECKSIG: + case OP_CHECKSIGVERIFY: + { + // (sig pubkey -- bool) + if (stack.size() < 2) + return false; + + valtype& vchSig = stacktop(-2); + valtype& vchPubKey = stacktop(-1); + + ////// debug print + //PrintHex(vchSig.begin(), vchSig.end(), "sig: %s\n"); + //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n"); + + // Subset of script starting at the most recent codeseparator + CScript scriptCode(pbegincodehash, pend); + + // Drop the signature, since there's no way for a signature to sign itself + scriptCode.FindAndDelete(CScript(vchSig)); + + bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); + + popstack(stack); + popstack(stack); + stack.push_back(fSuccess ? vchTrue : vchFalse); + if (opcode == OP_CHECKSIGVERIFY) + { + if (fSuccess) + popstack(stack); + else + return false; + } + } + break; + + case OP_CHECKMULTISIG: + case OP_CHECKMULTISIGVERIFY: + { + // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) + + int i = 1; + if (stack.size() < i) + return false; + + int nKeysCount = CastToBigNum(stacktop(-i)).getint(); + if (nKeysCount < 0 || nKeysCount > 20) + return false; + nOpCount += nKeysCount; + if (nOpCount > 201) + return false; + int ikey = ++i; + i += nKeysCount; + if (stack.size() < i) + return false; + + int nSigsCount = CastToBigNum(stacktop(-i)).getint(); + if (nSigsCount < 0 || nSigsCount > nKeysCount) + return false; + int isig = ++i; + i += nSigsCount; + if (stack.size() < i) + return false; + + // Subset of script starting at the most recent codeseparator + CScript scriptCode(pbegincodehash, pend); + + // Drop the signatures, since there's no way for a signature to sign itself + for (int k = 0; k < nSigsCount; k++) + { + valtype& vchSig = stacktop(-isig-k); + scriptCode.FindAndDelete(CScript(vchSig)); + } + + bool fSuccess = true; + while (fSuccess && nSigsCount > 0) + { + valtype& vchSig = stacktop(-isig); + valtype& vchPubKey = stacktop(-ikey); + + // Check signature + if (CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType)) + { + isig++; + nSigsCount--; + } + ikey++; + nKeysCount--; + + // If there are more signatures left than keys left, + // then too many signatures have failed + if (nSigsCount > nKeysCount) + fSuccess = false; + } + + while (i-- > 0) + popstack(stack); + stack.push_back(fSuccess ? vchTrue : vchFalse); + + if (opcode == OP_CHECKMULTISIGVERIFY) + { + if (fSuccess) + popstack(stack); + else + return false; + } + } + break; + + default: + return false; + } + + // Size limits + if (stack.size() + altstack.size() > 1000) + return false; + } + } + catch (...) + { + return false; + } + + + if (!vfExec.empty()) + return false; + + return true; +} + + + + + + + + + +uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + if (nIn >= txTo.vin.size()) + { + printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); + return 1; + } + CTransaction txTmp(txTo); + + // In case concatenating two scripts ends up with two codeseparators, + // or an extra one at the end, this prevents all those possible incompatibilities. + scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); + + // Blank out other inputs' signatures + for (int i = 0; i < txTmp.vin.size(); i++) + txTmp.vin[i].scriptSig = CScript(); + txTmp.vin[nIn].scriptSig = scriptCode; + + // Blank out some of the outputs + if ((nHashType & 0x1f) == SIGHASH_NONE) + { + // Wildcard payee + txTmp.vout.clear(); + + // Let the others update at will + for (int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + else if ((nHashType & 0x1f) == SIGHASH_SINGLE) + { + // Only lockin the txout payee at same index as txin + unsigned int nOut = nIn; + if (nOut >= txTmp.vout.size()) + { + printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); + return 1; + } + txTmp.vout.resize(nOut+1); + for (int i = 0; i < nOut; i++) + txTmp.vout[i].SetNull(); + + // Let the others update at will + for (int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + + // Blank out other inputs completely, not recommended for open transactions + if (nHashType & SIGHASH_ANYONECANPAY) + { + txTmp.vin[0] = txTmp.vin[nIn]; + txTmp.vin.resize(1); + } + + // Serialize and hash + CDataStream ss(SER_GETHASH); + ss.reserve(10000); + ss << txTmp << nHashType; + return Hash(ss.begin(), ss.end()); +} + + +bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, + const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + CKey key; + if (!key.SetPubKey(vchPubKey)) + return false; + + // Hash type is one byte tacked on to the end of the signature + if (vchSig.empty()) + return false; + if (nHashType == 0) + nHashType = vchSig.back(); + else if (nHashType != vchSig.back()) + return false; + vchSig.pop_back(); + + return key.Verify(SignatureHash(scriptCode, txTo, nIn, nHashType), vchSig); +} + + + + + + + + + + +bool Solver(const CScript& scriptPubKey, vector >& vSolutionRet) +{ + // Templates + static vector vTemplates; + if (vTemplates.empty()) + { + // Standard tx, sender provides pubkey, receiver adds signature + vTemplates.push_back(CScript() << OP_PUBKEY << OP_CHECKSIG); + + // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey + vTemplates.push_back(CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG); + } + + // Scan templates + const CScript& script1 = scriptPubKey; + BOOST_FOREACH(const CScript& script2, vTemplates) + { + vSolutionRet.clear(); + opcodetype opcode1, opcode2; + vector vch1, vch2; + + // Compare + CScript::const_iterator pc1 = script1.begin(); + CScript::const_iterator pc2 = script2.begin(); + loop + { + if (pc1 == script1.end() && pc2 == script2.end()) + { + // Found a match + reverse(vSolutionRet.begin(), vSolutionRet.end()); + return true; + } + if (!script1.GetOp(pc1, opcode1, vch1)) + break; + if (!script2.GetOp(pc2, opcode2, vch2)) + break; + if (opcode2 == OP_PUBKEY) + { + if (vch1.size() < 33 || vch1.size() > 120) + break; + vSolutionRet.push_back(make_pair(opcode2, vch1)); + } + else if (opcode2 == OP_PUBKEYHASH) + { + if (vch1.size() != sizeof(uint160)) + break; + vSolutionRet.push_back(make_pair(opcode2, vch1)); + } + else if (opcode1 != opcode2 || vch1 != vch2) + { + break; + } + } + } + + vSolutionRet.clear(); + return false; +} + + +bool Solver(const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet) +{ + scriptSigRet.clear(); + + vector > vSolution; + if (!Solver(scriptPubKey, vSolution)) + return false; + + // Compile solution + CRITICAL_BLOCK(cs_mapKeys) + { + BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) + { + if (item.first == OP_PUBKEY) + { + // Sign + const valtype& vchPubKey = item.second; + if (!mapKeys.count(vchPubKey)) + return false; + if (hash != 0) + { + vector vchSig; + if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig)) + return false; + vchSig.push_back((unsigned char)nHashType); + scriptSigRet << vchSig; + } + } + else if (item.first == OP_PUBKEYHASH) + { + // Sign and give pubkey + map::iterator mi = mapPubKeys.find(uint160(item.second)); + if (mi == mapPubKeys.end()) + return false; + const vector& vchPubKey = (*mi).second; + if (!mapKeys.count(vchPubKey)) + return false; + if (hash != 0) + { + vector vchSig; + if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig)) + return false; + vchSig.push_back((unsigned char)nHashType); + scriptSigRet << vchSig << vchPubKey; + } + } + else + { + return false; + } + } + } + + return true; +} + + +bool IsStandard(const CScript& scriptPubKey) +{ + vector > vSolution; + return Solver(scriptPubKey, vSolution); +} + + +bool IsMine(const CScript& scriptPubKey) +{ + CScript scriptSig; + return Solver(scriptPubKey, 0, 0, scriptSig); +} + + +bool ExtractPubKey(const CScript& scriptPubKey, bool fMineOnly, vector& vchPubKeyRet) +{ + vchPubKeyRet.clear(); + + vector > vSolution; + if (!Solver(scriptPubKey, vSolution)) + return false; + + CRITICAL_BLOCK(cs_mapKeys) + { + BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) + { + valtype vchPubKey; + if (item.first == OP_PUBKEY) + { + vchPubKey = item.second; + } + else if (item.first == OP_PUBKEYHASH) + { + map::iterator mi = mapPubKeys.find(uint160(item.second)); + if (mi == mapPubKeys.end()) + continue; + vchPubKey = (*mi).second; + } + if (!fMineOnly || mapKeys.count(vchPubKey)) + { + vchPubKeyRet = vchPubKey; + return true; + } + } + } + return false; +} + + +bool ExtractHash160(const CScript& scriptPubKey, uint160& hash160Ret) +{ + hash160Ret = 0; + + vector > vSolution; + if (!Solver(scriptPubKey, vSolution)) + return false; + + BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) + { + if (item.first == OP_PUBKEYHASH) + { + hash160Ret = uint160(item.second); + return true; + } + } + return false; +} + + +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + vector > stack; + if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType)) + return false; + if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType)) + return false; + if (stack.empty()) + return false; + return CastToBool(stack.back()); +} + + +bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType, CScript scriptPrereq) +{ + assert(nIn < txTo.vin.size()); + CTxIn& txin = txTo.vin[nIn]; + assert(txin.prevout.n < txFrom.vout.size()); + const CTxOut& txout = txFrom.vout[txin.prevout.n]; + + // Leave out the signature from the hash, since a signature can't sign itself. + // The checksig op will also drop the signatures from its hash. + uint256 hash = SignatureHash(scriptPrereq + txout.scriptPubKey, txTo, nIn, nHashType); + + if (!Solver(txout.scriptPubKey, hash, nHashType, txin.scriptSig)) + return false; + + txin.scriptSig = scriptPrereq + txin.scriptSig; + + // Test solution + if (scriptPrereq.empty()) + if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, 0)) + return false; + + return true; +} + + +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + assert(nIn < txTo.vin.size()); + const CTxIn& txin = txTo.vin[nIn]; + if (txin.prevout.n >= txFrom.vout.size()) + return false; + const CTxOut& txout = txFrom.vout[txin.prevout.n]; + + if (txin.prevout.hash != txFrom.GetHash()) + return false; + + if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nHashType)) + return false; + + // Anytime a signature is successfully verified, it's proof the outpoint is spent, + // so lets update the wallet spent flag if it doesn't know due to wallet.dat being + // restored from backup or the user making copies of wallet.dat. + WalletUpdateSpent(txin.prevout); + + return true; +} diff --git a/core/src/util.cpp b/core/src/util.cpp index 0e88e2eaf7..4e93f625de 100644 --- a/core/src/util.cpp +++ b/core/src/util.cpp @@ -1,21 +1,10 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. -#define __STDC_LIMIT_MACROS // to enable UINT64_MAX from stdint.h - -#include "util.h" -#include "main.h" -#include "strlcpy.h" - -#include - -#include -#include - -#include -#include +#include "headers.h" using namespace std; +using namespace boost; map mapArgs; map > mapMultiArgs; @@ -716,7 +705,7 @@ void GetDataDir(char* pszDir) if (!pfMkdir[nVariation]) { pfMkdir[nVariation] = true; - filesystem::create_directory(pszDir); + boost::filesystem::create_directory(pszDir); } } @@ -867,7 +856,7 @@ void AddTimeData(unsigned int ip, int64 nTime) { // If nobody has a time different than ours but within 5 minutes of ours, give a warning bool fMatch = false; - foreach(int64 nOffset, vTimeOffsets) + BOOST_FOREACH(int64 nOffset, vTimeOffsets) if (nOffset != 0 && abs64(nOffset) < 5 * 60) fMatch = true; @@ -881,7 +870,7 @@ void AddTimeData(unsigned int ip, int64 nTime) } } } - foreach(int64 n, vTimeOffsets) + BOOST_FOREACH(int64 n, vTimeOffsets) printf("%+"PRI64d" ", n); printf("| nTimeOffset = %+"PRI64d" (%+"PRI64d" minutes)\n", nTimeOffset, nTimeOffset/60); } diff --git a/json/include/json/json_spirit.h b/json/include/json/json_spirit.h new file mode 100644 index 0000000000..ac1879d5b3 --- /dev/null +++ b/json/include/json/json_spirit.h @@ -0,0 +1,18 @@ +#ifndef JSON_SPIRIT +#define JSON_SPIRIT + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include "json_spirit_reader.h" +#include "json_spirit_writer.h" +#include "json_spirit_utils.h" + +#endif diff --git a/json/include/json/json_spirit_error_position.h b/json/include/json/json_spirit_error_position.h new file mode 100644 index 0000000000..17208507df --- /dev/null +++ b/json/include/json/json_spirit_error_position.h @@ -0,0 +1,54 @@ +#ifndef JSON_SPIRIT_ERROR_POSITION +#define JSON_SPIRIT_ERROR_POSITION + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include + +namespace json_spirit +{ + // An Error_position exception is thrown by the "read_or_throw" functions below on finding an error. + // Note the "read_or_throw" functions are around 3 times slower than the standard functions "read" + // functions that return a bool. + // + struct Error_position + { + Error_position(); + Error_position( unsigned int line, unsigned int column, const std::string& reason ); + bool operator==( const Error_position& lhs ) const; + unsigned int line_; + unsigned int column_; + std::string reason_; + }; + + inline Error_position::Error_position() + : line_( 0 ) + , column_( 0 ) + { + } + + inline Error_position::Error_position( unsigned int line, unsigned int column, const std::string& reason ) + : line_( line ) + , column_( column ) + , reason_( reason ) + { + } + + inline bool Error_position::operator==( const Error_position& lhs ) const + { + if( this == &lhs ) return true; + + return ( reason_ == lhs.reason_ ) && + ( line_ == lhs.line_ ) && + ( column_ == lhs.column_ ); +} +} + +#endif diff --git a/json/include/json/json_spirit_reader.h b/json/include/json/json_spirit_reader.h new file mode 100644 index 0000000000..96494a9789 --- /dev/null +++ b/json/include/json/json_spirit_reader.h @@ -0,0 +1,62 @@ +#ifndef JSON_SPIRIT_READER +#define JSON_SPIRIT_READER + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include "json_spirit_error_position.h" +#include + +namespace json_spirit +{ + // functions to reads a JSON values + + bool read( const std::string& s, Value& value ); + bool read( std::istream& is, Value& value ); + bool read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); + + void read_or_throw( const std::string& s, Value& value ); + void read_or_throw( std::istream& is, Value& value ); + void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); + +#ifndef BOOST_NO_STD_WSTRING + + bool read( const std::wstring& s, wValue& value ); + bool read( std::wistream& is, wValue& value ); + bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); + + void read_or_throw( const std::wstring& s, wValue& value ); + void read_or_throw( std::wistream& is, wValue& value ); + void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); + +#endif + + bool read( const std::string& s, mValue& value ); + bool read( std::istream& is, mValue& value ); + bool read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); + + void read_or_throw( const std::string& s, mValue& value ); + void read_or_throw( std::istream& is, mValue& value ); + void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); + +#ifndef BOOST_NO_STD_WSTRING + + bool read( const std::wstring& s, wmValue& value ); + bool read( std::wistream& is, wmValue& value ); + bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); + + void read_or_throw( const std::wstring& s, wmValue& value ); + void read_or_throw( std::wistream& is, wmValue& value ); + void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); + +#endif +} + +#endif diff --git a/json/include/json/json_spirit_reader_template.h b/json/include/json/json_spirit_reader_template.h new file mode 100644 index 0000000000..4dec00e6c9 --- /dev/null +++ b/json/include/json/json_spirit_reader_template.h @@ -0,0 +1,612 @@ +#ifndef JSON_SPIRIT_READER_TEMPLATE +#define JSON_SPIRIT_READER_TEMPLATE + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json_spirit_value.h" +#include "json_spirit_error_position.h" + +//#define BOOST_SPIRIT_THREADSAFE // uncomment for multithreaded use, requires linking to boost.thread + +#include +#include +#include + +#if BOOST_VERSION >= 103800 + #include + #include + #include + #include + #include + #define spirit_namespace boost::spirit::classic +#else + #include + #include + #include + #include + #include + #define spirit_namespace boost::spirit +#endif + +namespace json_spirit +{ + const spirit_namespace::int_parser < boost::int64_t > int64_p = spirit_namespace::int_parser < boost::int64_t >(); + const spirit_namespace::uint_parser< boost::uint64_t > uint64_p = spirit_namespace::uint_parser< boost::uint64_t >(); + + template< class Iter_type > + bool is_eq( Iter_type first, Iter_type last, const char* c_str ) + { + for( Iter_type i = first; i != last; ++i, ++c_str ) + { + if( *c_str == 0 ) return false; + + if( *i != *c_str ) return false; + } + + return true; + } + + template< class Char_type > + Char_type hex_to_num( const Char_type c ) + { + if( ( c >= '0' ) && ( c <= '9' ) ) return c - '0'; + if( ( c >= 'a' ) && ( c <= 'f' ) ) return c - 'a' + 10; + if( ( c >= 'A' ) && ( c <= 'F' ) ) return c - 'A' + 10; + return 0; + } + + template< class Char_type, class Iter_type > + Char_type hex_str_to_char( Iter_type& begin ) + { + const Char_type c1( *( ++begin ) ); + const Char_type c2( *( ++begin ) ); + + return ( hex_to_num( c1 ) << 4 ) + hex_to_num( c2 ); + } + + template< class Char_type, class Iter_type > + Char_type unicode_str_to_char( Iter_type& begin ) + { + const Char_type c1( *( ++begin ) ); + const Char_type c2( *( ++begin ) ); + const Char_type c3( *( ++begin ) ); + const Char_type c4( *( ++begin ) ); + + return ( hex_to_num( c1 ) << 12 ) + + ( hex_to_num( c2 ) << 8 ) + + ( hex_to_num( c3 ) << 4 ) + + hex_to_num( c4 ); + } + + template< class String_type > + void append_esc_char_and_incr_iter( String_type& s, + typename String_type::const_iterator& begin, + typename String_type::const_iterator end ) + { + typedef typename String_type::value_type Char_type; + + const Char_type c2( *begin ); + + switch( c2 ) + { + case 't': s += '\t'; break; + case 'b': s += '\b'; break; + case 'f': s += '\f'; break; + case 'n': s += '\n'; break; + case 'r': s += '\r'; break; + case '\\': s += '\\'; break; + case '/': s += '/'; break; + case '"': s += '"'; break; + case 'x': + { + if( end - begin >= 3 ) // expecting "xHH..." + { + s += hex_str_to_char< Char_type >( begin ); + } + break; + } + case 'u': + { + if( end - begin >= 5 ) // expecting "uHHHH..." + { + s += unicode_str_to_char< Char_type >( begin ); + } + break; + } + } + } + + template< class String_type > + String_type substitute_esc_chars( typename String_type::const_iterator begin, + typename String_type::const_iterator end ) + { + typedef typename String_type::const_iterator Iter_type; + + if( end - begin < 2 ) return String_type( begin, end ); + + String_type result; + + result.reserve( end - begin ); + + const Iter_type end_minus_1( end - 1 ); + + Iter_type substr_start = begin; + Iter_type i = begin; + + for( ; i < end_minus_1; ++i ) + { + if( *i == '\\' ) + { + result.append( substr_start, i ); + + ++i; // skip the '\' + + append_esc_char_and_incr_iter( result, i, end ); + + substr_start = i + 1; + } + } + + result.append( substr_start, end ); + + return result; + } + + template< class String_type > + String_type get_str_( typename String_type::const_iterator begin, + typename String_type::const_iterator end ) + { + assert( end - begin >= 2 ); + + typedef typename String_type::const_iterator Iter_type; + + Iter_type str_without_quotes( ++begin ); + Iter_type end_without_quotes( --end ); + + return substitute_esc_chars< String_type >( str_without_quotes, end_without_quotes ); + } + + inline std::string get_str( std::string::const_iterator begin, std::string::const_iterator end ) + { + return get_str_< std::string >( begin, end ); + } + + inline std::wstring get_str( std::wstring::const_iterator begin, std::wstring::const_iterator end ) + { + return get_str_< std::wstring >( begin, end ); + } + + template< class String_type, class Iter_type > + String_type get_str( Iter_type begin, Iter_type end ) + { + const String_type tmp( begin, end ); // convert multipass iterators to string iterators + + return get_str( tmp.begin(), tmp.end() ); + } + + // this class's methods get called by the spirit parse resulting + // in the creation of a JSON object or array + // + // NB Iter_type could be a std::string iterator, wstring iterator, a position iterator or a multipass iterator + // + template< class Value_type, class Iter_type > + class Semantic_actions + { + public: + + typedef typename Value_type::Config_type Config_type; + typedef typename Config_type::String_type String_type; + typedef typename Config_type::Object_type Object_type; + typedef typename Config_type::Array_type Array_type; + typedef typename String_type::value_type Char_type; + + Semantic_actions( Value_type& value ) + : value_( value ) + , current_p_( 0 ) + { + } + + void begin_obj( Char_type c ) + { + assert( c == '{' ); + + begin_compound< Object_type >(); + } + + void end_obj( Char_type c ) + { + assert( c == '}' ); + + end_compound(); + } + + void begin_array( Char_type c ) + { + assert( c == '[' ); + + begin_compound< Array_type >(); + } + + void end_array( Char_type c ) + { + assert( c == ']' ); + + end_compound(); + } + + void new_name( Iter_type begin, Iter_type end ) + { + assert( current_p_->type() == obj_type ); + + name_ = get_str< String_type >( begin, end ); + } + + void new_str( Iter_type begin, Iter_type end ) + { + add_to_current( get_str< String_type >( begin, end ) ); + } + + void new_true( Iter_type begin, Iter_type end ) + { + assert( is_eq( begin, end, "true" ) ); + + add_to_current( true ); + } + + void new_false( Iter_type begin, Iter_type end ) + { + assert( is_eq( begin, end, "false" ) ); + + add_to_current( false ); + } + + void new_null( Iter_type begin, Iter_type end ) + { + assert( is_eq( begin, end, "null" ) ); + + add_to_current( Value_type() ); + } + + void new_int( boost::int64_t i ) + { + add_to_current( i ); + } + + void new_uint64( boost::uint64_t ui ) + { + add_to_current( ui ); + } + + void new_real( double d ) + { + add_to_current( d ); + } + + private: + + Semantic_actions& operator=( const Semantic_actions& ); + // to prevent "assignment operator could not be generated" warning + + Value_type* add_first( const Value_type& value ) + { + assert( current_p_ == 0 ); + + value_ = value; + current_p_ = &value_; + return current_p_; + } + + template< class Array_or_obj > + void begin_compound() + { + if( current_p_ == 0 ) + { + add_first( Array_or_obj() ); + } + else + { + stack_.push_back( current_p_ ); + + Array_or_obj new_array_or_obj; // avoid copy by building new array or object in place + + current_p_ = add_to_current( new_array_or_obj ); + } + } + + void end_compound() + { + if( current_p_ != &value_ ) + { + current_p_ = stack_.back(); + + stack_.pop_back(); + } + } + + Value_type* add_to_current( const Value_type& value ) + { + if( current_p_ == 0 ) + { + return add_first( value ); + } + else if( current_p_->type() == array_type ) + { + current_p_->get_array().push_back( value ); + + return ¤t_p_->get_array().back(); + } + + assert( current_p_->type() == obj_type ); + + return &Config_type::add( current_p_->get_obj(), name_, value ); + } + + Value_type& value_; // this is the object or array that is being created + Value_type* current_p_; // the child object or array that is currently being constructed + + std::vector< Value_type* > stack_; // previous child objects and arrays + + String_type name_; // of current name/value pair + }; + + template< typename Iter_type > + void throw_error( spirit_namespace::position_iterator< Iter_type > i, const std::string& reason ) + { + throw Error_position( i.get_position().line, i.get_position().column, reason ); + } + + template< typename Iter_type > + void throw_error( Iter_type i, const std::string& reason ) + { + throw reason; + } + + // the spirit grammer + // + template< class Value_type, class Iter_type > + class Json_grammer : public spirit_namespace::grammar< Json_grammer< Value_type, Iter_type > > + { + public: + + typedef Semantic_actions< Value_type, Iter_type > Semantic_actions_t; + + Json_grammer( Semantic_actions_t& semantic_actions ) + : actions_( semantic_actions ) + { + } + + static void throw_not_value( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not a value" ); + } + + static void throw_not_array( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not an array" ); + } + + static void throw_not_object( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not an object" ); + } + + static void throw_not_pair( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not a pair" ); + } + + static void throw_not_colon( Iter_type begin, Iter_type end ) + { + throw_error( begin, "no colon in pair" ); + } + + static void throw_not_string( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not a string" ); + } + + template< typename ScannerT > + class definition + { + public: + + definition( const Json_grammer& self ) + { + using namespace spirit_namespace; + + typedef typename Value_type::String_type::value_type Char_type; + + // first we convert the semantic action class methods to functors with the + // parameter signature expected by spirit + + typedef boost::function< void( Char_type ) > Char_action; + typedef boost::function< void( Iter_type, Iter_type ) > Str_action; + typedef boost::function< void( double ) > Real_action; + typedef boost::function< void( boost::int64_t ) > Int_action; + typedef boost::function< void( boost::uint64_t ) > Uint64_action; + + Char_action begin_obj ( boost::bind( &Semantic_actions_t::begin_obj, &self.actions_, _1 ) ); + Char_action end_obj ( boost::bind( &Semantic_actions_t::end_obj, &self.actions_, _1 ) ); + Char_action begin_array( boost::bind( &Semantic_actions_t::begin_array, &self.actions_, _1 ) ); + Char_action end_array ( boost::bind( &Semantic_actions_t::end_array, &self.actions_, _1 ) ); + Str_action new_name ( boost::bind( &Semantic_actions_t::new_name, &self.actions_, _1, _2 ) ); + Str_action new_str ( boost::bind( &Semantic_actions_t::new_str, &self.actions_, _1, _2 ) ); + Str_action new_true ( boost::bind( &Semantic_actions_t::new_true, &self.actions_, _1, _2 ) ); + Str_action new_false ( boost::bind( &Semantic_actions_t::new_false, &self.actions_, _1, _2 ) ); + Str_action new_null ( boost::bind( &Semantic_actions_t::new_null, &self.actions_, _1, _2 ) ); + Real_action new_real ( boost::bind( &Semantic_actions_t::new_real, &self.actions_, _1 ) ); + Int_action new_int ( boost::bind( &Semantic_actions_t::new_int, &self.actions_, _1 ) ); + Uint64_action new_uint64 ( boost::bind( &Semantic_actions_t::new_uint64, &self.actions_, _1 ) ); + + // actual grammer + + json_ + = value_ | eps_p[ &throw_not_value ] + ; + + value_ + = string_[ new_str ] + | number_ + | object_ + | array_ + | str_p( "true" ) [ new_true ] + | str_p( "false" )[ new_false ] + | str_p( "null" ) [ new_null ] + ; + + object_ + = ch_p('{')[ begin_obj ] + >> !members_ + >> ( ch_p('}')[ end_obj ] | eps_p[ &throw_not_object ] ) + ; + + members_ + = pair_ >> *( ',' >> pair_ ) + ; + + pair_ + = string_[ new_name ] + >> ( ':' | eps_p[ &throw_not_colon ] ) + >> ( value_ | eps_p[ &throw_not_value ] ) + ; + + array_ + = ch_p('[')[ begin_array ] + >> !elements_ + >> ( ch_p(']')[ end_array ] | eps_p[ &throw_not_array ] ) + ; + + elements_ + = value_ >> *( ',' >> value_ ) + ; + + string_ + = lexeme_d // this causes white space inside a string to be retained + [ + confix_p + ( + '"', + *lex_escape_ch_p, + '"' + ) + ] + ; + + number_ + = strict_real_p[ new_real ] + | int64_p [ new_int ] + | uint64_p [ new_uint64 ] + ; + } + + spirit_namespace::rule< ScannerT > json_, object_, members_, pair_, array_, elements_, value_, string_, number_; + + const spirit_namespace::rule< ScannerT >& start() const { return json_; } + }; + + private: + + Json_grammer& operator=( const Json_grammer& ); // to prevent "assignment operator could not be generated" warning + + Semantic_actions_t& actions_; + }; + + template< class Iter_type, class Value_type > + Iter_type read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) + { + Semantic_actions< Value_type, Iter_type > semantic_actions( value ); + + const spirit_namespace::parse_info< Iter_type > info = + spirit_namespace::parse( begin, end, + Json_grammer< Value_type, Iter_type >( semantic_actions ), + spirit_namespace::space_p ); + + if( !info.hit ) + { + assert( false ); // in theory exception should already have been thrown + throw_error( info.stop, "error" ); + } + + return info.stop; + } + + template< class Iter_type, class Value_type > + void add_posn_iter_and_read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) + { + typedef spirit_namespace::position_iterator< Iter_type > Posn_iter_t; + + const Posn_iter_t posn_begin( begin, end ); + const Posn_iter_t posn_end( end, end ); + + read_range_or_throw( posn_begin, posn_end, value ); + } + + template< class Iter_type, class Value_type > + bool read_range( Iter_type& begin, Iter_type end, Value_type& value ) + { + try + { + begin = read_range_or_throw( begin, end, value ); + + return true; + } + catch( ... ) + { + return false; + } + } + + template< class String_type, class Value_type > + void read_string_or_throw( const String_type& s, Value_type& value ) + { + add_posn_iter_and_read_range_or_throw( s.begin(), s.end(), value ); + } + + template< class String_type, class Value_type > + bool read_string( const String_type& s, Value_type& value ) + { + typename String_type::const_iterator begin = s.begin(); + + return read_range( begin, s.end(), value ); + } + + template< class Istream_type > + struct Multi_pass_iters + { + typedef typename Istream_type::char_type Char_type; + typedef std::istream_iterator< Char_type, Char_type > istream_iter; + typedef spirit_namespace::multi_pass< istream_iter > Mp_iter; + + Multi_pass_iters( Istream_type& is ) + { + is.unsetf( std::ios::skipws ); + + begin_ = spirit_namespace::make_multi_pass( istream_iter( is ) ); + end_ = spirit_namespace::make_multi_pass( istream_iter() ); + } + + Mp_iter begin_; + Mp_iter end_; + }; + + template< class Istream_type, class Value_type > + bool read_stream( Istream_type& is, Value_type& value ) + { + Multi_pass_iters< Istream_type > mp_iters( is ); + + return read_range( mp_iters.begin_, mp_iters.end_, value ); + } + + template< class Istream_type, class Value_type > + void read_stream_or_throw( Istream_type& is, Value_type& value ) + { + const Multi_pass_iters< Istream_type > mp_iters( is ); + + add_posn_iter_and_read_range_or_throw( mp_iters.begin_, mp_iters.end_, value ); + } +} + +#endif diff --git a/json/include/json/json_spirit_stream_reader.h b/json/include/json/json_spirit_stream_reader.h new file mode 100644 index 0000000000..7e59c9adc2 --- /dev/null +++ b/json/include/json/json_spirit_stream_reader.h @@ -0,0 +1,70 @@ +#ifndef JSON_SPIRIT_READ_STREAM +#define JSON_SPIRIT_READ_STREAM + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_reader_template.h" + +namespace json_spirit +{ + // these classes allows you to read multiple top level contiguous values from a stream, + // the normal stream read functions have a bug that prevent multiple top level values + // from being read unless they are separated by spaces + + template< class Istream_type, class Value_type > + class Stream_reader + { + public: + + Stream_reader( Istream_type& is ) + : iters_( is ) + { + } + + bool read_next( Value_type& value ) + { + return read_range( iters_.begin_, iters_.end_, value ); + } + + private: + + typedef Multi_pass_iters< Istream_type > Mp_iters; + + Mp_iters iters_; + }; + + template< class Istream_type, class Value_type > + class Stream_reader_thrower + { + public: + + Stream_reader_thrower( Istream_type& is ) + : iters_( is ) + , posn_begin_( iters_.begin_, iters_.end_ ) + , posn_end_( iters_.end_, iters_.end_ ) + { + } + + void read_next( Value_type& value ) + { + posn_begin_ = read_range_or_throw( posn_begin_, posn_end_, value ); + } + + private: + + typedef Multi_pass_iters< Istream_type > Mp_iters; + typedef spirit_namespace::position_iterator< typename Mp_iters::Mp_iter > Posn_iter_t; + + Mp_iters iters_; + Posn_iter_t posn_begin_, posn_end_; + }; +} + +#endif diff --git a/json/include/json/json_spirit_utils.h b/json/include/json/json_spirit_utils.h new file mode 100644 index 0000000000..553e3b96a4 --- /dev/null +++ b/json/include/json/json_spirit_utils.h @@ -0,0 +1,61 @@ +#ifndef JSON_SPIRIT_UTILS +#define JSON_SPIRIT_UTILS + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include + +namespace json_spirit +{ + template< class Obj_t, class Map_t > + void obj_to_map( const Obj_t& obj, Map_t& mp_obj ) + { + mp_obj.clear(); + + for( typename Obj_t::const_iterator i = obj.begin(); i != obj.end(); ++i ) + { + mp_obj[ i->name_ ] = i->value_; + } + } + + template< class Obj_t, class Map_t > + void map_to_obj( const Map_t& mp_obj, Obj_t& obj ) + { + obj.clear(); + + for( typename Map_t::const_iterator i = mp_obj.begin(); i != mp_obj.end(); ++i ) + { + obj.push_back( typename Obj_t::value_type( i->first, i->second ) ); + } + } + + typedef std::map< std::string, Value > Mapped_obj; + +#ifndef BOOST_NO_STD_WSTRING + typedef std::map< std::wstring, wValue > wMapped_obj; +#endif + + template< class Object_type, class String_type > + const typename Object_type::value_type::Value_type& find_value( const Object_type& obj, const String_type& name ) + { + for( typename Object_type::const_iterator i = obj.begin(); i != obj.end(); ++i ) + { + if( i->name_ == name ) + { + return i->value_; + } + } + + return Object_type::value_type::Value_type::null; + } +} + +#endif diff --git a/json/include/json/json_spirit_value.h b/json/include/json/json_spirit_value.h new file mode 100644 index 0000000000..7e83a2a7e3 --- /dev/null +++ b/json/include/json/json_spirit_value.h @@ -0,0 +1,534 @@ +#ifndef JSON_SPIRIT_VALUE +#define JSON_SPIRIT_VALUE + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace json_spirit +{ + enum Value_type{ obj_type, array_type, str_type, bool_type, int_type, real_type, null_type }; + static const char* Value_type_name[]={"obj", "array", "str", "bool", "int", "real", "null"}; + + template< class Config > // Config determines whether the value uses std::string or std::wstring and + // whether JSON Objects are represented as vectors or maps + class Value_impl + { + public: + + typedef Config Config_type; + typedef typename Config::String_type String_type; + typedef typename Config::Object_type Object; + typedef typename Config::Array_type Array; + typedef typename String_type::const_pointer Const_str_ptr; // eg const char* + + Value_impl(); // creates null value + Value_impl( Const_str_ptr value ); + Value_impl( const String_type& value ); + Value_impl( const Object& value ); + Value_impl( const Array& value ); + Value_impl( bool value ); + Value_impl( int value ); + Value_impl( boost::int64_t value ); + Value_impl( boost::uint64_t value ); + Value_impl( double value ); + + Value_impl( const Value_impl& other ); + + bool operator==( const Value_impl& lhs ) const; + + Value_impl& operator=( const Value_impl& lhs ); + + Value_type type() const; + + bool is_uint64() const; + bool is_null() const; + + const String_type& get_str() const; + const Object& get_obj() const; + const Array& get_array() const; + bool get_bool() const; + int get_int() const; + boost::int64_t get_int64() const; + boost::uint64_t get_uint64() const; + double get_real() const; + + Object& get_obj(); + Array& get_array(); + + template< typename T > T get_value() const; // example usage: int i = value.get_value< int >(); + // or double d = value.get_value< double >(); + + static const Value_impl null; + + private: + + void check_type( const Value_type vtype ) const; + + typedef boost::variant< String_type, + boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >, + bool, boost::int64_t, double > Variant; + + Value_type type_; + Variant v_; + bool is_uint64_; + }; + + // vector objects + + template< class Config > + struct Pair_impl + { + typedef typename Config::String_type String_type; + typedef typename Config::Value_type Value_type; + + Pair_impl( const String_type& name, const Value_type& value ); + + bool operator==( const Pair_impl& lhs ) const; + + String_type name_; + Value_type value_; + }; + + template< class String > + struct Config_vector + { + typedef String String_type; + typedef Value_impl< Config_vector > Value_type; + typedef Pair_impl < Config_vector > Pair_type; + typedef std::vector< Value_type > Array_type; + typedef std::vector< Pair_type > Object_type; + + static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) + { + obj.push_back( Pair_type( name , value ) ); + + return obj.back().value_; + } + + static String_type get_name( const Pair_type& pair ) + { + return pair.name_; + } + + static Value_type get_value( const Pair_type& pair ) + { + return pair.value_; + } + }; + + // typedefs for ASCII + + typedef Config_vector< std::string > Config; + + typedef Config::Value_type Value; + typedef Config::Pair_type Pair; + typedef Config::Object_type Object; + typedef Config::Array_type Array; + + // typedefs for Unicode + +#ifndef BOOST_NO_STD_WSTRING + + typedef Config_vector< std::wstring > wConfig; + + typedef wConfig::Value_type wValue; + typedef wConfig::Pair_type wPair; + typedef wConfig::Object_type wObject; + typedef wConfig::Array_type wArray; +#endif + + // map objects + + template< class String > + struct Config_map + { + typedef String String_type; + typedef Value_impl< Config_map > Value_type; + typedef std::vector< Value_type > Array_type; + typedef std::map< String_type, Value_type > Object_type; + typedef typename Object_type::value_type Pair_type; + + static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) + { + return obj[ name ] = value; + } + + static String_type get_name( const Pair_type& pair ) + { + return pair.first; + } + + static Value_type get_value( const Pair_type& pair ) + { + return pair.second; + } + }; + + // typedefs for ASCII + + typedef Config_map< std::string > mConfig; + + typedef mConfig::Value_type mValue; + typedef mConfig::Object_type mObject; + typedef mConfig::Array_type mArray; + + // typedefs for Unicode + +#ifndef BOOST_NO_STD_WSTRING + + typedef Config_map< std::wstring > wmConfig; + + typedef wmConfig::Value_type wmValue; + typedef wmConfig::Object_type wmObject; + typedef wmConfig::Array_type wmArray; + +#endif + + /////////////////////////////////////////////////////////////////////////////////////////////// + // + // implementation + + template< class Config > + const Value_impl< Config > Value_impl< Config >::null; + + template< class Config > + Value_impl< Config >::Value_impl() + : type_( null_type ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Const_str_ptr value ) + : type_( str_type ) + , v_( String_type( value ) ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const String_type& value ) + : type_( str_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Object& value ) + : type_( obj_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Array& value ) + : type_( array_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( bool value ) + : type_( bool_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( int value ) + : type_( int_type ) + , v_( static_cast< boost::int64_t >( value ) ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( boost::int64_t value ) + : type_( int_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( boost::uint64_t value ) + : type_( int_type ) + , v_( static_cast< boost::int64_t >( value ) ) + , is_uint64_( true ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( double value ) + : type_( real_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Value_impl< Config >& other ) + : type_( other.type() ) + , v_( other.v_ ) + , is_uint64_( other.is_uint64_ ) + { + } + + template< class Config > + Value_impl< Config >& Value_impl< Config >::operator=( const Value_impl& lhs ) + { + Value_impl tmp( lhs ); + + std::swap( type_, tmp.type_ ); + std::swap( v_, tmp.v_ ); + std::swap( is_uint64_, tmp.is_uint64_ ); + + return *this; + } + + template< class Config > + bool Value_impl< Config >::operator==( const Value_impl& lhs ) const + { + if( this == &lhs ) return true; + + if( type() != lhs.type() ) return false; + + return v_ == lhs.v_; + } + + template< class Config > + Value_type Value_impl< Config >::type() const + { + return type_; + } + + template< class Config > + bool Value_impl< Config >::is_uint64() const + { + return is_uint64_; + } + + template< class Config > + bool Value_impl< Config >::is_null() const + { + return type() == null_type; + } + + template< class Config > + void Value_impl< Config >::check_type( const Value_type vtype ) const + { + if( type() != vtype ) + { + std::ostringstream os; + + ///// Bitcoin: Tell the types by name instead of by number + os << "value is type " << Value_type_name[type()] << ", expected " << Value_type_name[vtype]; + + throw std::runtime_error( os.str() ); + } + } + + template< class Config > + const typename Config::String_type& Value_impl< Config >::get_str() const + { + check_type( str_type ); + + return *boost::get< String_type >( &v_ ); + } + + template< class Config > + const typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() const + { + check_type( obj_type ); + + return *boost::get< Object >( &v_ ); + } + + template< class Config > + const typename Value_impl< Config >::Array& Value_impl< Config >::get_array() const + { + check_type( array_type ); + + return *boost::get< Array >( &v_ ); + } + + template< class Config > + bool Value_impl< Config >::get_bool() const + { + check_type( bool_type ); + + return boost::get< bool >( v_ ); + } + + template< class Config > + int Value_impl< Config >::get_int() const + { + check_type( int_type ); + + return static_cast< int >( get_int64() ); + } + + template< class Config > + boost::int64_t Value_impl< Config >::get_int64() const + { + check_type( int_type ); + + return boost::get< boost::int64_t >( v_ ); + } + + template< class Config > + boost::uint64_t Value_impl< Config >::get_uint64() const + { + check_type( int_type ); + + return static_cast< boost::uint64_t >( get_int64() ); + } + + template< class Config > + double Value_impl< Config >::get_real() const + { + if( type() == int_type ) + { + return is_uint64() ? static_cast< double >( get_uint64() ) + : static_cast< double >( get_int64() ); + } + + check_type( real_type ); + + return boost::get< double >( v_ ); + } + + template< class Config > + typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() + { + check_type( obj_type ); + + return *boost::get< Object >( &v_ ); + } + + template< class Config > + typename Value_impl< Config >::Array& Value_impl< Config >::get_array() + { + check_type( array_type ); + + return *boost::get< Array >( &v_ ); + } + + template< class Config > + Pair_impl< Config >::Pair_impl( const String_type& name, const Value_type& value ) + : name_( name ) + , value_( value ) + { + } + + template< class Config > + bool Pair_impl< Config >::operator==( const Pair_impl< Config >& lhs ) const + { + if( this == &lhs ) return true; + + return ( name_ == lhs.name_ ) && ( value_ == lhs.value_ ); + } + + // converts a C string, ie. 8 bit char array, to a string object + // + template < class String_type > + String_type to_str( const char* c_str ) + { + String_type result; + + for( const char* p = c_str; *p != 0; ++p ) + { + result += *p; + } + + return result; + } + + // + + namespace internal_ + { + template< typename T > + struct Type_to_type + { + }; + + template< class Value > + int get_value( const Value& value, Type_to_type< int > ) + { + return value.get_int(); + } + + template< class Value > + boost::int64_t get_value( const Value& value, Type_to_type< boost::int64_t > ) + { + return value.get_int64(); + } + + template< class Value > + boost::uint64_t get_value( const Value& value, Type_to_type< boost::uint64_t > ) + { + return value.get_uint64(); + } + + template< class Value > + double get_value( const Value& value, Type_to_type< double > ) + { + return value.get_real(); + } + + template< class Value > + typename Value::String_type get_value( const Value& value, Type_to_type< typename Value::String_type > ) + { + return value.get_str(); + } + + template< class Value > + typename Value::Array get_value( const Value& value, Type_to_type< typename Value::Array > ) + { + return value.get_array(); + } + + template< class Value > + typename Value::Object get_value( const Value& value, Type_to_type< typename Value::Object > ) + { + return value.get_obj(); + } + + template< class Value > + bool get_value( const Value& value, Type_to_type< bool > ) + { + return value.get_bool(); + } + } + + template< class Config > + template< typename T > + T Value_impl< Config >::get_value() const + { + return internal_::get_value( *this, internal_::Type_to_type< T >() ); + } +} + +#endif diff --git a/json/include/json/json_spirit_writer.h b/json/include/json/json_spirit_writer.h new file mode 100644 index 0000000000..52e14068e7 --- /dev/null +++ b/json/include/json/json_spirit_writer.h @@ -0,0 +1,50 @@ +#ifndef JSON_SPIRIT_WRITER +#define JSON_SPIRIT_WRITER + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include + +namespace json_spirit +{ + // functions to convert JSON Values to text, + // the "formatted" versions add whitespace to format the output nicely + + void write ( const Value& value, std::ostream& os ); + void write_formatted( const Value& value, std::ostream& os ); + std::string write ( const Value& value ); + std::string write_formatted( const Value& value ); + +#ifndef BOOST_NO_STD_WSTRING + + void write ( const wValue& value, std::wostream& os ); + void write_formatted( const wValue& value, std::wostream& os ); + std::wstring write ( const wValue& value ); + std::wstring write_formatted( const wValue& value ); + +#endif + + void write ( const mValue& value, std::ostream& os ); + void write_formatted( const mValue& value, std::ostream& os ); + std::string write ( const mValue& value ); + std::string write_formatted( const mValue& value ); + +#ifndef BOOST_NO_STD_WSTRING + + void write ( const wmValue& value, std::wostream& os ); + void write_formatted( const wmValue& value, std::wostream& os ); + std::wstring write ( const wmValue& value ); + std::wstring write_formatted( const wmValue& value ); + +#endif +} + +#endif diff --git a/json/include/json/json_spirit_writer_template.h b/json/include/json/json_spirit_writer_template.h new file mode 100644 index 0000000000..28c49ddc64 --- /dev/null +++ b/json/include/json/json_spirit_writer_template.h @@ -0,0 +1,248 @@ +#ifndef JSON_SPIRIT_WRITER_TEMPLATE +#define JSON_SPIRIT_WRITER_TEMPLATE + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json_spirit_value.h" + +#include +#include +#include + +namespace json_spirit +{ + inline char to_hex_char( unsigned int c ) + { + assert( c <= 0xF ); + + const char ch = static_cast< char >( c ); + + if( ch < 10 ) return '0' + ch; + + return 'A' - 10 + ch; + } + + template< class String_type > + String_type non_printable_to_string( unsigned int c ) + { + typedef typename String_type::value_type Char_type; + + String_type result( 6, '\\' ); + + result[1] = 'u'; + + result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 2 ] = to_hex_char( c & 0x000F ); + + return result; + } + + template< typename Char_type, class String_type > + bool add_esc_char( Char_type c, String_type& s ) + { + switch( c ) + { + case '"': s += to_str< String_type >( "\\\"" ); return true; + case '\\': s += to_str< String_type >( "\\\\" ); return true; + case '\b': s += to_str< String_type >( "\\b" ); return true; + case '\f': s += to_str< String_type >( "\\f" ); return true; + case '\n': s += to_str< String_type >( "\\n" ); return true; + case '\r': s += to_str< String_type >( "\\r" ); return true; + case '\t': s += to_str< String_type >( "\\t" ); return true; + } + + return false; + } + + template< class String_type > + String_type add_esc_chars( const String_type& s ) + { + typedef typename String_type::const_iterator Iter_type; + typedef typename String_type::value_type Char_type; + + String_type result; + + const Iter_type end( s.end() ); + + for( Iter_type i = s.begin(); i != end; ++i ) + { + const Char_type c( *i ); + + if( add_esc_char( c, result ) ) continue; + + const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c ); + + if( iswprint( unsigned_c ) ) + { + result += c; + } + else + { + result += non_printable_to_string< String_type >( unsigned_c ); + } + } + + return result; + } + + // this class generates the JSON text, + // it keeps track of the indentation level etc. + // + template< class Value_type, class Ostream_type > + class Generator + { + typedef typename Value_type::Config_type Config_type; + typedef typename Config_type::String_type String_type; + typedef typename Config_type::Object_type Object_type; + typedef typename Config_type::Array_type Array_type; + typedef typename String_type::value_type Char_type; + typedef typename Object_type::value_type Obj_member_type; + + public: + + Generator( const Value_type& value, Ostream_type& os, bool pretty ) + : os_( os ) + , indentation_level_( 0 ) + , pretty_( pretty ) + { + output( value ); + } + + private: + + void output( const Value_type& value ) + { + switch( value.type() ) + { + case obj_type: output( value.get_obj() ); break; + case array_type: output( value.get_array() ); break; + case str_type: output( value.get_str() ); break; + case bool_type: output( value.get_bool() ); break; + case int_type: output_int( value ); break; + + /// Bitcoin: Added std::fixed and changed precision from 16 to 8 + case real_type: os_ << std::showpoint << std::fixed << std::setprecision(8) + << value.get_real(); break; + + case null_type: os_ << "null"; break; + default: assert( false ); + } + } + + void output( const Object_type& obj ) + { + output_array_or_obj( obj, '{', '}' ); + } + + void output( const Array_type& arr ) + { + output_array_or_obj( arr, '[', ']' ); + } + + void output( const Obj_member_type& member ) + { + output( Config_type::get_name( member ) ); space(); + os_ << ':'; space(); + output( Config_type::get_value( member ) ); + } + + void output_int( const Value_type& value ) + { + if( value.is_uint64() ) + { + os_ << value.get_uint64(); + } + else + { + os_ << value.get_int64(); + } + } + + void output( const String_type& s ) + { + os_ << '"' << add_esc_chars( s ) << '"'; + } + + void output( bool b ) + { + os_ << to_str< String_type >( b ? "true" : "false" ); + } + + template< class T > + void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char ) + { + os_ << start_char; new_line(); + + ++indentation_level_; + + for( typename T::const_iterator i = t.begin(); i != t.end(); ++i ) + { + indent(); output( *i ); + + typename T::const_iterator next = i; + + if( ++next != t.end()) + { + os_ << ','; + } + + new_line(); + } + + --indentation_level_; + + indent(); os_ << end_char; + } + + void indent() + { + if( !pretty_ ) return; + + for( int i = 0; i < indentation_level_; ++i ) + { + os_ << " "; + } + } + + void space() + { + if( pretty_ ) os_ << ' '; + } + + void new_line() + { + if( pretty_ ) os_ << '\n'; + } + + Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning + + Ostream_type& os_; + int indentation_level_; + bool pretty_; + }; + + template< class Value_type, class Ostream_type > + void write_stream( const Value_type& value, Ostream_type& os, bool pretty ) + { + Generator< Value_type, Ostream_type >( value, os, pretty ); + } + + template< class Value_type > + typename Value_type::String_type write_string( const Value_type& value, bool pretty ) + { + typedef typename Value_type::String_type::value_type Char_type; + + std::basic_ostringstream< Char_type > os; + + write_stream( value, os, pretty ); + + return os.str(); + } +} + +#endif diff --git a/json/src/json_spirit_reader.cpp b/json/src/json_spirit_reader.cpp new file mode 100644 index 0000000000..3469bf17af --- /dev/null +++ b/json/src/json_spirit_reader.cpp @@ -0,0 +1,137 @@ +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json/json_spirit_reader.h" +#include "json/json_spirit_reader_template.h" + +using namespace json_spirit; + +bool json_spirit::read( const std::string& s, Value& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::string& s, Value& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::istream& is, Value& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::istream& is, Value& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#ifndef BOOST_NO_STD_WSTRING + +bool json_spirit::read( const std::wstring& s, wValue& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::wstring& s, wValue& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::wistream& is, wValue& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::wistream& is, wValue& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#endif + +bool json_spirit::read( const std::string& s, mValue& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::string& s, mValue& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::istream& is, mValue& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::istream& is, mValue& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#ifndef BOOST_NO_STD_WSTRING + +bool json_spirit::read( const std::wstring& s, wmValue& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::wstring& s, wmValue& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::wistream& is, wmValue& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::wistream& is, wmValue& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#endif diff --git a/json/src/json_spirit_value.cpp b/json/src/json_spirit_value.cpp new file mode 100644 index 0000000000..2d7236855d --- /dev/null +++ b/json/src/json_spirit_value.cpp @@ -0,0 +1,8 @@ +/* Copyright (c) 2007 John W Wilkinson + + This source code can be used for any purpose as long as + this comment is retained. */ + +// json spirit version 2.00 + +#include "json/json_spirit_value.h" diff --git a/json/src/json_spirit_writer.cpp b/json/src/json_spirit_writer.cpp new file mode 100644 index 0000000000..32dfeb2c6c --- /dev/null +++ b/json/src/json_spirit_writer.cpp @@ -0,0 +1,95 @@ +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json/json_spirit_writer.h" +#include "json/json_spirit_writer_template.h" + +void json_spirit::write( const Value& value, std::ostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const Value& value, std::ostream& os ) +{ + write_stream( value, os, true ); +} + +std::string json_spirit::write( const Value& value ) +{ + return write_string( value, false ); +} + +std::string json_spirit::write_formatted( const Value& value ) +{ + return write_string( value, true ); +} + +#ifndef BOOST_NO_STD_WSTRING + +void json_spirit::write( const wValue& value, std::wostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const wValue& value, std::wostream& os ) +{ + write_stream( value, os, true ); +} + +std::wstring json_spirit::write( const wValue& value ) +{ + return write_string( value, false ); +} + +std::wstring json_spirit::write_formatted( const wValue& value ) +{ + return write_string( value, true ); +} + +#endif + +void json_spirit::write( const mValue& value, std::ostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const mValue& value, std::ostream& os ) +{ + write_stream( value, os, true ); +} + +std::string json_spirit::write( const mValue& value ) +{ + return write_string( value, false ); +} + +std::string json_spirit::write_formatted( const mValue& value ) +{ + return write_string( value, true ); +} + +#ifndef BOOST_NO_STD_WSTRING + +void json_spirit::write( const wmValue& value, std::wostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const wmValue& value, std::wostream& os ) +{ + write_stream( value, os, true ); +} + +std::wstring json_spirit::write( const wmValue& value ) +{ + return write_string( value, false ); +} + +std::wstring json_spirit::write_formatted( const wmValue& value ) +{ + return write_string( value, true ); +} + +#endif -- cgit v1.2.3 From 6a8062a30d0977118dd378d5a2ef31b40b260165 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 15 May 2011 08:32:30 +0200 Subject: todo update --- TODO | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index 33cf4063f7..d1713ebacf 100644 --- a/TODO +++ b/TODO @@ -70,13 +70,14 @@ Compatibility with Qt, and some more modularity - Put guard statements around header files. -- Proper include between header files; remove central "header.h" file. - - Removed macro foreach: conflicts with Qt keyword foreach, replaced back with BOOST_FOREACH - Prefix stdlib structures and functions with std:: in headers; "using namespace" in header files is generally frowned upon +- Modularity: base48.h can now be included without including all the headers + (useful for linking external GUI to bitcoin core, part of GUI separation) + Todo: - Repeated in all files? -- cgit v1.2.3 From 85663f2c1886cf867ebe8c5b11b5d8fea10f6e82 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 15 May 2011 16:50:28 +0200 Subject: update to bitcoin-git --- TODO | 13 ++++++++----- core/include/base58.h | 4 ++-- core/include/net.h | 5 ++++- core/include/util.h | 3 ++- core/src/net.cpp | 10 ++++++---- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/TODO b/TODO index d1713ebacf..dceee9b24d 100644 --- a/TODO +++ b/TODO @@ -66,17 +66,20 @@ AboutDialog Done: -Compatibility with Qt, and some more modularity +To be able to make an external GUI, I am working on modularizing bitcoin into a library. This is basic code +plumbing, 100% no functional changes. - Put guard statements around header files. -- Removed macro foreach: conflicts with Qt keyword foreach, replaced back with BOOST_FOREACH +- Removed macro foreach: conflicts with Qt4 keyword `foreach`, replaced with BOOST_FOREACH. - Prefix stdlib structures and functions with std:: in headers; "using namespace" in header files is - generally frowned upon + generally frowned upon. These are moved to the implementation files. + +- Modularity: base48.h and most other header files can now be included without the other shebang + (useful for linking external GUI to bitcoin core, part of GUI separation). + The include files that need each other now include each other. -- Modularity: base48.h can now be included without including all the headers - (useful for linking external GUI to bitcoin core, part of GUI separation) Todo: diff --git a/core/include/base58.h b/core/include/base58.h index 7acdf63ae3..580bd3fc63 100644 --- a/core/include/base58.h +++ b/core/include/base58.h @@ -7,7 +7,7 @@ // Why base-58 instead of standard base-64 encoding? // - Don't want 0OIl characters that look the same in some fonts and // could be used to create visually identical looking account numbers. -// - A std::string with non-alphanumeric characters is not as easily accepted as an account number. +// - A string with non-alphanumeric characters is not as easily accepted as an account number. // - E-mail usually won't line-break if there's no punctuation to break at. // - Doubleclicking selects the whole number as one word if it's all alphanumeric. // @@ -74,7 +74,7 @@ inline bool DecodeBase58(const char* psz, std::vector& vchRet) while (isspace(*psz)) psz++; - // Convert big endian std::string to bignum + // Convert big endian string to bignum for (const char* p = psz; *p; p++) { const char* p1 = strchr(pszBase58, *p); diff --git a/core/include/net.h b/core/include/net.h index 746dd02fe8..6bbcd64e42 100644 --- a/core/include/net.h +++ b/core/include/net.h @@ -6,9 +6,12 @@ #include #include -#include #include +#ifndef __WXMSW__ +#include +#endif + class CMessageHeader; class CAddress; class CInv; diff --git a/core/include/util.h b/core/include/util.h index 2d1d42975b..b1eabd52d5 100644 --- a/core/include/util.h +++ b/core/include/util.h @@ -5,11 +5,12 @@ #define BITCOIN_UTIL_H #include "uint256.h" -//#include "cryptopp/sha.h" +#ifndef __WXMSW__ #include #include #include +#endif #include #include #include diff --git a/core/src/net.cpp b/core/src/net.cpp index 4f489fdb57..1320781cb2 100644 --- a/core/src/net.cpp +++ b/core/src/net.cpp @@ -133,6 +133,8 @@ bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) bool Lookup(const char *pszName, vector& vaddr, int nServices, int nMaxSolutions, bool fAllowLookup, int portDefault, bool fAllowPort) { vaddr.clear(); + if (pszName[0] == 0) + return false; int port = portDefault; char psz[256]; char *pszHost = psz; @@ -158,11 +160,11 @@ bool Lookup(const char *pszName, vector& vaddr, int nServices, int nMa } } - struct in_addr addrIP; - if (inet_aton(pszHost, &addrIP)) + unsigned int addrIP = inet_addr(pszHost); + if (addrIP != INADDR_NONE) { // valid IP address passed - vaddr.push_back(CAddress(addrIP.s_addr, port, nServices)); + vaddr.push_back(CAddress(addrIP, port, nServices)); return true; } @@ -1527,7 +1529,7 @@ void StartNode(void* parg) if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) { vector vaddr; - if (NameLookup(pszHostName, vaddr, nLocalServices)) + if (Lookup(pszHostName, vaddr, nLocalServices, -1, true)) BOOST_FOREACH (const CAddress &addr, vaddr) if (addr.GetByte(3) != 127) { -- cgit v1.2.3 From 992ff49b43cd9110fc8ce41151f7555458dcf4dc Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 15 May 2011 19:31:20 +0200 Subject: make send coins dialog more user friendly (better checking) --- TODO | 4 ++++ gui/forms/sendcoinsdialog.ui | 28 ++++++++++------------------ gui/include/sendcoinsdialog.h | 2 +- gui/src/addressbookdialog.cpp | 10 ++++++++-- gui/src/bitcoingui.cpp | 7 ++++++- gui/src/sendcoinsdialog.cpp | 43 +++++++++++++++++++++++++++++++++++++------ 6 files changed, 66 insertions(+), 28 deletions(-) diff --git a/TODO b/TODO index dceee9b24d..3136696ffc 100644 --- a/TODO +++ b/TODO @@ -92,3 +92,7 @@ Todo: - Check windows support / cross platform +Check send dialog: + 1MCwBbhNGp5hRm5rC1Aims2YFRe2SXPYKt + + diff --git a/gui/forms/sendcoinsdialog.ui b/gui/forms/sendcoinsdialog.ui index 31a0b99e20..ce4edde4ed 100644 --- a/gui/forms/sendcoinsdialog.ui +++ b/gui/forms/sendcoinsdialog.ui @@ -64,6 +64,9 @@ &Paste + + false + @@ -71,6 +74,9 @@ Address &Book... + + false + @@ -124,6 +130,9 @@ :/icons/send:/icons/send + + true + @@ -146,22 +155,5 @@ - - - payAmount - returnPressed() - sendButton - click() - - - 191 - 65 - - - 570 - 121 - - - - + diff --git a/gui/include/sendcoinsdialog.h b/gui/include/sendcoinsdialog.h index a2fcdd0762..95dd34b1e5 100644 --- a/gui/include/sendcoinsdialog.h +++ b/gui/include/sendcoinsdialog.h @@ -12,7 +12,7 @@ class SendCoinsDialog : public QDialog Q_OBJECT public: - explicit SendCoinsDialog(QWidget *parent = 0); + explicit SendCoinsDialog(QWidget *parent = 0, const QString &address = ""); ~SendCoinsDialog(); private: diff --git a/gui/src/addressbookdialog.cpp b/gui/src/addressbookdialog.cpp index 71543f11a7..9ad18f1cd7 100644 --- a/gui/src/addressbookdialog.cpp +++ b/gui/src/addressbookdialog.cpp @@ -127,6 +127,12 @@ void AddressBookDialog::on_buttonBox_accepted() QVariant address = table->model()->data(index); returnValue = address.toString(); } - - accept(); + if(!returnValue.isEmpty()) + { + accept(); + } + else + { + reject(); + } } diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index 4af703cf8d..760789a015 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -211,7 +211,12 @@ void BitcoinGUI::addressbookClicked() qDebug() << "Address book clicked"; AddressBookDialog dlg; dlg.setTab(AddressBookDialog::SendingTab); - dlg.exec(); + /* if an address accepted, do a 'send' to specified address */ + if(dlg.exec()) + { + SendCoinsDialog send(0, dlg.getReturnValue()); + send.exec(); + } } void BitcoinGUI::receivingAddressesClicked() diff --git a/gui/src/sendcoinsdialog.cpp b/gui/src/sendcoinsdialog.cpp index ce95244dcb..3907b4bc59 100644 --- a/gui/src/sendcoinsdialog.cpp +++ b/gui/src/sendcoinsdialog.cpp @@ -6,17 +6,31 @@ #include #include +#include +#include #include "base58.h" -SendCoinsDialog::SendCoinsDialog(QWidget *parent) : +SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : QDialog(parent), ui(new Ui::SendCoinsDialog) { ui->setupUi(this); + + /* Set up validators */ ui->payTo->setMaxLength(BitcoinAddressValidator::MaxAddressLength); ui->payTo->setValidator(new BitcoinAddressValidator(this)); - ui->payAmount->setValidator(new QDoubleValidator(this)); + QDoubleValidator *amountValidator = new QDoubleValidator(this); + amountValidator->setDecimals(8); + amountValidator->setBottom(0.0); + ui->payAmount->setValidator(amountValidator); + + /* Set initial address if provided */ + if(!address.isEmpty()) + { + ui->payTo->setText(address); + ui->payAmount->setFocus(); + } } SendCoinsDialog::~SendCoinsDialog() @@ -28,14 +42,31 @@ void SendCoinsDialog::on_sendButton_clicked() { QByteArray payTo = ui->payTo->text().toUtf8(); uint160 payToHash = 0; - if(AddressToHash160(payTo.constData(), payToHash)) + double payAmount = 0.0; + bool valid = false; + + if(!AddressToHash160(payTo.constData(), payToHash)) { - accept(); + QMessageBox::warning(this, tr("Warning"), + tr("The recepient address is not valid, please recheck."), + QMessageBox::Ok, + QMessageBox::Ok); + ui->payTo->setFocus(); + return; } - else + payAmount = QLocale::system().toDouble(ui->payAmount->text(), &valid); + if(!valid || payAmount <= 0.0) { - + QMessageBox::warning(this, tr("Warning"), + tr("The amount to pay must be a valid number larger than 0."), + QMessageBox::Ok, + QMessageBox::Ok); + ui->payAmount->setFocus(); + return; } + + /* TODO: send command to core, once this succeeds do accept() */ + accept(); } void SendCoinsDialog::on_pasteButton_clicked() -- cgit v1.2.3 From fb7e2901b77838b0ac65e3eb78245015f9f37d90 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 15 May 2011 19:39:21 +0200 Subject: remove debug stuff for implemented methods --- gui/src/bitcoingui.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index 760789a015..e7cd34aaa6 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -201,14 +201,12 @@ QWidget *BitcoinGUI::createTabs() void BitcoinGUI::sendcoinsClicked() { - qDebug() << "Send coins clicked"; SendCoinsDialog dlg; dlg.exec(); } void BitcoinGUI::addressbookClicked() { - qDebug() << "Address book clicked"; AddressBookDialog dlg; dlg.setTab(AddressBookDialog::SendingTab); /* if an address accepted, do a 'send' to specified address */ @@ -221,7 +219,6 @@ void BitcoinGUI::addressbookClicked() void BitcoinGUI::receivingAddressesClicked() { - qDebug() << "Receiving addresses clicked"; AddressBookDialog dlg; dlg.setTab(AddressBookDialog::ReceivingTab); dlg.exec(); @@ -229,14 +226,12 @@ void BitcoinGUI::receivingAddressesClicked() void BitcoinGUI::optionsClicked() { - qDebug() << "Options clicked"; OptionsDialog dlg; dlg.exec(); } void BitcoinGUI::aboutClicked() { - qDebug() << "About clicked"; AboutDialog dlg; dlg.exec(); } @@ -249,7 +244,6 @@ void BitcoinGUI::newAddressClicked() void BitcoinGUI::copyClipboardClicked() { - qDebug() << "Copy to clipboard"; /* Copy text in address to clipboard */ QApplication::clipboard()->setText(address->text()); } -- cgit v1.2.3 From b90626021bbaa93adb60834b930bacc8b995d1e5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 15 May 2011 20:11:58 +0200 Subject: add readme file --- README.rst | 33 +++++++++++++++++++++ TODO | 98 -------------------------------------------------------------- 2 files changed, 33 insertions(+), 98 deletions(-) create mode 100644 README.rst delete mode 100644 TODO diff --git a/README.rst b/README.rst new file mode 100644 index 0000000000..ef33af0730 --- /dev/null +++ b/README.rst @@ -0,0 +1,33 @@ +Bitcoin-qt: Qt4 based GUI replacement for Bitcoin +================================================= + +**Warning** **Warning** **Warning** +Pre-alpha stuff! Developer only! + +This has been implemented: + +- qmake / QtCreator project (.pro) + +- All dialogs (main GUI, address book, send coins) and menus + +- Taskbar icon/menu + +- GUI only functionality (copy to clipboard, select address, address/transaction filter proxys) + +- Bitcoin core is made compatible with Qt4, and linked against + +- Send coins dialog: address and input validation + +- Address book and transactions views + +This has to be done: + +- Further integration of bitcoin core, so that it can actually connect + to the network, send commands and get feedback + +- Address book and transactions models: so that + something sensible is shown instead of dummy data :) + +- Internationalization (convert WX language files) + +- Build on Windows diff --git a/TODO b/TODO deleted file mode 100644 index 3136696ffc..0000000000 --- a/TODO +++ /dev/null @@ -1,98 +0,0 @@ -Toolbar: - Send coins - Address book - -- "Your bitcoin address" label -- address field -- "New..." -- Copy to Clipboard - -Balance: XXX - -Tabs: - All transactions - Sent/Received - Sent - Received - -Table [columns]: - Status - Date - Description - Debit - Credit - - ** Table should be the same in all tabs. Do we really need different widgets? - -> yes, to have different proxy views - - ** Table rows are much too high? - -Status bar: - Permanent status indicators: - < actions_crystal_project: connect_established.png / connect_no.png > - N connections - M blocks - O transactions - -SendCoinDialog -AddressesDialog (Address book) - Receiving/Sending - -OptionsDialog - Tabs at the left -AboutDialog - - -- Move resources to res/ - - - Send icon - - - Address Book icon - -- Translation - -- Toolbar icon - -- 'notify' on incoming transaction - -- AddressTableModel - - Name / Label - - Address - - Delete / Copy to clipboard based on tab - -- Make icon blend into taskbar and maybe silver/grey - Same for the other icons? - - -Done: - -To be able to make an external GUI, I am working on modularizing bitcoin into a library. This is basic code -plumbing, 100% no functional changes. - -- Put guard statements around header files. - -- Removed macro foreach: conflicts with Qt4 keyword `foreach`, replaced with BOOST_FOREACH. - -- Prefix stdlib structures and functions with std:: in headers; "using namespace" in header files is - generally frowned upon. These are moved to the implementation files. - -- Modularity: base48.h and most other header files can now be included without the other shebang - (useful for linking external GUI to bitcoin core, part of GUI separation). - The include files that need each other now include each other. - - -Todo: - -- Repeated in all files? -#define __STDC_LIMIT_MACROS // to enable UINT64_MAX from stdint.h -#include "main.h" -#ifndef GUI -#include "noui.h" -#endif - -- Check windows support / cross platform - -Check send dialog: - 1MCwBbhNGp5hRm5rC1Aims2YFRe2SXPYKt - - -- cgit v1.2.3 From 29bbcab6b4a37de45c45f0747d2692005cdf4cd3 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 16 May 2011 08:14:04 +0200 Subject: update build system for macosx --- bitcoin.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/bitcoin.pro b/bitcoin.pro index b323ba8a54..e87169e898 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -3,6 +3,7 @@ TARGET = DEPENDPATH += . INCLUDEPATH += gui/include core/include cryptopp/include json/include unix:LIBS += -lssl -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx +macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 # Input HEADERS += gui/include/bitcoingui.h \ -- cgit v1.2.3 From ad88e7626ba5ac2274d7ec9c218537ec7df0ad50 Mon Sep 17 00:00:00 2001 From: Wladimir van der Laan Date: Sun, 22 May 2011 14:54:13 +0200 Subject: go on testnet for now --- gui/src/bitcoin.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gui/src/bitcoin.cpp b/gui/src/bitcoin.cpp index 403e4c517b..a5f4a05a07 100644 --- a/gui/src/bitcoin.cpp +++ b/gui/src/bitcoin.cpp @@ -2,6 +2,7 @@ * W.J. van der Laan 2011 */ #include "bitcoingui.h" +#include "util.h" #include @@ -9,6 +10,9 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); + /* Testing on testnet */ + fTestNet = true; + BitcoinGUI window; window.setBalance(1234.567890); window.setNumConnections(4); -- cgit v1.2.3 From 18cab09a959bc3f62e5112be5cb5ad61f871d963 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 22 May 2011 17:19:43 +0200 Subject: core initialisation, client model binding --- bitcoin.pro | 8 +++++-- core/src/init.cpp | 2 ++ gui/include/bitcoingui.h | 3 +++ gui/include/clientmodel.h | 31 +++++++++++++++++++++++++ gui/src/bitcoin.cpp | 34 ++++++++++++++++++--------- gui/src/bitcoingui.cpp | 21 +++++++++++++++++ gui/src/clientmodel.cpp | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 144 insertions(+), 13 deletions(-) create mode 100644 gui/include/clientmodel.h create mode 100644 gui/src/clientmodel.cpp diff --git a/bitcoin.pro b/bitcoin.pro index e87169e898..f323182308 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -4,6 +4,7 @@ DEPENDPATH += . INCLUDEPATH += gui/include core/include cryptopp/include json/include unix:LIBS += -lssl -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 +# WINDOWS defines, -DSSL, look at build system # Input HEADERS += gui/include/bitcoingui.h \ @@ -51,7 +52,9 @@ HEADERS += gui/include/bitcoingui.h \ json/include/json/json_spirit_reader.h \ json/include/json/json_spirit_error_position.h \ json/include/json/json_spirit.h \ - core/include/rpc.h + core/include/rpc.h \ + gui/src/clientmodel.h \ + gui/include/clientmodel.h SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/transactiontablemodel.cpp \ gui/src/addresstablemodel.cpp \ @@ -74,7 +77,8 @@ SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ core/src/db.cpp \ json/src/json_spirit_writer.cpp \ json/src/json_spirit_value.cpp \ - json/src/json_spirit_reader.cpp + json/src/json_spirit_reader.cpp \ + gui/src/clientmodel.cpp RESOURCES += \ gui/bitcoin.qrc diff --git a/core/src/init.cpp b/core/src/init.cpp index 3126d3489e..5528c430f9 100644 --- a/core/src/init.cpp +++ b/core/src/init.cpp @@ -513,9 +513,11 @@ bool AppInit2(int argc, char* argv[]) SetStartOnSystemStartup(true); #endif +#if 0 #ifndef GUI while (1) Sleep(5000); +#endif #endif return true; diff --git a/gui/include/bitcoingui.h b/gui/include/bitcoingui.h index 9142b6b84b..3b722fcca4 100644 --- a/gui/include/bitcoingui.h +++ b/gui/include/bitcoingui.h @@ -6,6 +6,7 @@ /* Forward declarations */ class TransactionTableModel; +class ClientModel; QT_BEGIN_NAMESPACE class QLabel; @@ -17,6 +18,7 @@ class BitcoinGUI : public QMainWindow Q_OBJECT public: explicit BitcoinGUI(QWidget *parent = 0); + void setModel(ClientModel *model); /* Transaction table tab indices */ enum { @@ -27,6 +29,7 @@ public: } TabIndex; private: TransactionTableModel *transaction_model; + ClientModel *model; QLineEdit *address; QLabel *labelBalance; diff --git a/gui/include/clientmodel.h b/gui/include/clientmodel.h new file mode 100644 index 0000000000..fb1a5edebc --- /dev/null +++ b/gui/include/clientmodel.h @@ -0,0 +1,31 @@ +#ifndef CLIENTMODEL_H +#define CLIENTMODEL_H + +#include + +class ClientModel : public QObject +{ + Q_OBJECT +public: + explicit ClientModel(QObject *parent = 0); + + double getBalance(); + QString getAddress(); + int getNumConnections(); + int getNumBlocks(); + int getNumTransactions(); + +signals: + void balanceChanged(double balance); + void addressChanged(const QString &address); + void numConnectionsChanged(int count); + void numBlocksChanged(int count); + void numTransactionsChanged(int count); + +public slots: + +private slots: + void update(); +}; + +#endif // CLIENTMODEL_H diff --git a/gui/src/bitcoin.cpp b/gui/src/bitcoin.cpp index a5f4a05a07..c843cc406e 100644 --- a/gui/src/bitcoin.cpp +++ b/gui/src/bitcoin.cpp @@ -2,7 +2,9 @@ * W.J. van der Laan 2011 */ #include "bitcoingui.h" +#include "clientmodel.h" #include "util.h" +#include "init.h" #include @@ -10,19 +12,29 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - /* Testing on testnet */ - fTestNet = true; + try { + if(AppInit2(argc, argv)) + { + ClientModel model; + BitcoinGUI window; + window.setModel(&model); - BitcoinGUI window; - window.setBalance(1234.567890); - window.setNumConnections(4); - window.setNumTransactions(4); - window.setNumBlocks(33); - window.setAddress("123456789"); + window.show(); - window.show(); + /* Depending on settings: QApplication::setQuitOnLastWindowClosed(false); */ + int retval = app.exec(); - /* Depending on settings: QApplication::setQuitOnLastWindowClosed(false); */ + Shutdown(NULL); - return app.exec(); + return retval; + } + else + { + return 1; + } + } catch (std::exception& e) { + PrintException(&e, "Runaway exception"); + } catch (...) { + PrintException(NULL, "Runaway exception"); + } } diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index e7cd34aaa6..5546a0ecfb 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -9,6 +9,7 @@ #include "sendcoinsdialog.h" #include "optionsdialog.h" #include "aboutdialog.h" +#include "clientmodel.h" #include #include @@ -139,6 +140,26 @@ void BitcoinGUI::createActions() connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); } +void BitcoinGUI::setModel(ClientModel *model) +{ + this->model = model; + + setBalance(model->getBalance()); + connect(model, SIGNAL(balanceChanged(double)), this, SLOT(setBalance(double))); + + setNumConnections(model->getNumConnections()); + connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); + + setNumTransactions(model->getNumTransactions()); + connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); + + setNumBlocks(model->getNumBlocks()); + connect(model, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int))); + + setAddress(model->getAddress()); + connect(model, SIGNAL(addressChanged(QString)), this, SLOT(setAddress(QString))); +} + void BitcoinGUI::createTrayIcon() { QMenu *trayIconMenu = new QMenu(this); diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp new file mode 100644 index 0000000000..0f191023b7 --- /dev/null +++ b/gui/src/clientmodel.cpp @@ -0,0 +1,58 @@ +#include "clientmodel.h" +#include "main.h" + +#include + +/* milliseconds between model updates */ +const int MODEL_UPDATE_DELAY = 250; + +ClientModel::ClientModel(QObject *parent) : + QObject(parent) +{ + /* Until we build signal notifications into the bitcoin core, + simply update everything using a timer. + */ + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(update())); + timer->start(MODEL_UPDATE_DELAY); +} + +double ClientModel::getBalance() +{ + return GetBalance(); +} + +QString ClientModel::getAddress() +{ + std::vector vchPubKey; + if (CWalletDB("r").ReadDefaultKey(vchPubKey)) + { + return QString::fromStdString(PubKeyToAddress(vchPubKey)); + } else { + return QString(); + } +} + +int ClientModel::getNumConnections() +{ + return vNodes.size(); +} + +int ClientModel::getNumBlocks() +{ + return nBestHeight; +} + +int ClientModel::getNumTransactions() +{ + return 0; +} + +void ClientModel::update() +{ + emit balanceChanged(getBalance()); + emit addressChanged(getAddress()); + emit numConnectionsChanged(getNumConnections()); + emit numBlocksChanged(getNumBlocks()); + emit numTransactionsChanged(getNumTransactions()); +} -- cgit v1.2.3 From 858ff187f5c2411e9618aefd698845f522a61809 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 22 May 2011 19:32:18 +0200 Subject: don't start in server mode --- core/src/init.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/init.cpp b/core/src/init.cpp index 5528c430f9..d40d29cf09 100644 --- a/core/src/init.cpp +++ b/core/src/init.cpp @@ -220,10 +220,11 @@ bool AppInit2(int argc, char* argv[]) fServer = GetBoolArg("-server"); /* force fServer when running without GUI */ +#if 0 #ifndef GUI fServer = true; #endif - +#endif fPrintToConsole = GetBoolArg("-printtoconsole"); fPrintToDebugger = GetBoolArg("-printtodebugger"); -- cgit v1.2.3 From 8968bf2e36ca17d18ffe4c333d349cd7557c058b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 22 May 2011 19:32:37 +0200 Subject: use user roles instead of hidden columns for model sort/filter keys --- gui/include/addresstablemodel.h | 9 ++++++--- gui/include/transactiontablemodel.h | 7 +++++-- gui/src/addressbookdialog.cpp | 10 ++-------- gui/src/addresstablemodel.cpp | 5 +++-- gui/src/bitcoingui.cpp | 4 +--- gui/src/transactiontablemodel.cpp | 7 ++++--- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/gui/include/addresstablemodel.h b/gui/include/addresstablemodel.h index 50ed80dd39..97780c5428 100644 --- a/gui/include/addresstablemodel.h +++ b/gui/include/addresstablemodel.h @@ -10,11 +10,14 @@ public: explicit AddressTableModel(QObject *parent = 0); enum { - Label = 0, /* User specified label */ - Address = 1, /* Bitcoin address */ - Type = 2 /* Send/Receive, used for filter */ + Label = 0, /* User specified label */ + Address = 1 /* Bitcoin address */ } ColumnIndex; + enum { + TypeRole = Qt::UserRole + } RoleIndex; + static const QString Send; /* Send addres */ static const QString Receive; /* Receive address */ diff --git a/gui/include/transactiontablemodel.h b/gui/include/transactiontablemodel.h index 77ad730673..e86ab988c4 100644 --- a/gui/include/transactiontablemodel.h +++ b/gui/include/transactiontablemodel.h @@ -15,10 +15,13 @@ public: Date = 1, Description = 2, Debit = 3, - Credit = 4, - Type = 5 + Credit = 4 } ColumnIndex; + enum { + TypeRole = Qt::UserRole + } RoleIndex; + /* Transaction type */ static const QString Sent; static const QString Received; diff --git a/gui/src/addressbookdialog.cpp b/gui/src/addressbookdialog.cpp index 9ad18f1cd7..4ca863bd20 100644 --- a/gui/src/addressbookdialog.cpp +++ b/gui/src/addressbookdialog.cpp @@ -29,8 +29,7 @@ void AddressBookDialog::setModel(AddressTableModel *model) QSortFilterProxyModel *receive_model = new QSortFilterProxyModel(this); receive_model->setSourceModel(model); receive_model->setDynamicSortFilter(true); - receive_model->setFilterRole(Qt::UserRole); - receive_model->setFilterKeyColumn(AddressTableModel::Type); + receive_model->setFilterRole(AddressTableModel::TypeRole); receive_model->setFilterFixedString(AddressTableModel::Receive); ui->receiveTableView->setModel(receive_model); @@ -38,8 +37,7 @@ void AddressBookDialog::setModel(AddressTableModel *model) QSortFilterProxyModel *send_model = new QSortFilterProxyModel(this); send_model->setSourceModel(model); send_model->setDynamicSortFilter(true); - send_model->setFilterRole(Qt::UserRole); - send_model->setFilterKeyColumn(AddressTableModel::Type); + send_model->setFilterRole(AddressTableModel::TypeRole); send_model->setFilterFixedString(AddressTableModel::Send); ui->sendTableView->setModel(send_model); @@ -52,10 +50,6 @@ void AddressBookDialog::setModel(AddressTableModel *model) AddressTableModel::Address, 320); ui->sendTableView->horizontalHeader()->setResizeMode( AddressTableModel::Label, QHeaderView::Stretch); - - /* Hide "Type" column in both views as it is only used for filtering */ - ui->receiveTableView->setColumnHidden(AddressTableModel::Type, true); - ui->sendTableView->setColumnHidden(AddressTableModel::Type, true); } void AddressBookDialog::setTab(int tab) diff --git a/gui/src/addresstablemodel.cpp b/gui/src/addresstablemodel.cpp index d985bcee3c..9a72cd9725 100644 --- a/gui/src/addresstablemodel.cpp +++ b/gui/src/addresstablemodel.cpp @@ -1,4 +1,5 @@ #include "addresstablemodel.h" +#include "main.h" const QString AddressTableModel::Send = "S"; const QString AddressTableModel::Receive = "R"; @@ -16,7 +17,7 @@ int AddressTableModel::rowCount(const QModelIndex &parent) const int AddressTableModel::columnCount(const QModelIndex &parent) const { - return 3; + return 2; } QVariant AddressTableModel::data(const QModelIndex &index, int role) const @@ -32,7 +33,7 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const return "1PC9aZC4hNX2rmmrt7uHTfYAS3hRbph4UN" + QString::number(index.row()); else return "Description"; - } else if (role == Qt::UserRole) + } else if (role == TypeRole) { switch(index.row() % 2) { diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index 5546a0ecfb..168dffd7cd 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -193,8 +193,7 @@ QWidget *BitcoinGUI::createTabs() QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this); proxy_model->setSourceModel(transaction_model); proxy_model->setDynamicSortFilter(true); - proxy_model->setFilterRole(Qt::UserRole); - proxy_model->setFilterKeyColumn(TransactionTableModel::Type); + proxy_model->setFilterRole(TransactionTableModel::TypeRole); proxy_model->setFilterRegExp(QRegExp(tab_filters.at(i))); QTableView *transaction_table = new QTableView(this); @@ -213,7 +212,6 @@ QWidget *BitcoinGUI::createTabs() TransactionTableModel::Debit, 79); transaction_table->horizontalHeader()->resizeSection( TransactionTableModel::Credit, 79); - transaction_table->setColumnHidden(TransactionTableModel::Type, true); tabs->addTab(transaction_table, tab_labels.at(i)); } diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index ee58ecba40..0f16a3faf2 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -1,4 +1,5 @@ #include "transactiontablemodel.h" +#include "main.h" #include @@ -19,13 +20,13 @@ static int column_alignments[] = { TransactionTableModel::TransactionTableModel(QObject *parent): QAbstractTableModel(parent) { - columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit") << tr("Type"); + columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); } int TransactionTableModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); - return 5; + return 4; } int TransactionTableModel::columnCount(const QModelIndex &parent) const @@ -47,7 +48,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const } else if (role == Qt::TextAlignmentRole) { return column_alignments[index.column()]; - } else if (role == Qt::UserRole) + } else if (role == TypeRole) { /* user role: transaction type for filtering "s" (sent) -- cgit v1.2.3 From 213f7636301a3311cfcc47421910e326157501c2 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 27 May 2011 08:20:23 +0200 Subject: bind transactionmodel --- gui/forms/addressbookdialog.ui | 6 + gui/include/transactiontablemodel.h | 12 ++ gui/src/addresstablemodel.cpp | 8 +- gui/src/bitcoingui.cpp | 1 + gui/src/transactiontablemodel.cpp | 211 ++++++++++++++++++++++++++++++++++-- 5 files changed, 229 insertions(+), 9 deletions(-) diff --git a/gui/forms/addressbookdialog.ui b/gui/forms/addressbookdialog.ui index d66962a2be..761ea0fb91 100644 --- a/gui/forms/addressbookdialog.ui +++ b/gui/forms/addressbookdialog.ui @@ -32,6 +32,9 @@ QAbstractItemView::SelectRows + + true + false @@ -65,6 +68,9 @@ QAbstractItemView::SelectRows + + true + false diff --git a/gui/include/transactiontablemodel.h b/gui/include/transactiontablemodel.h index e86ab988c4..684a9470dd 100644 --- a/gui/include/transactiontablemodel.h +++ b/gui/include/transactiontablemodel.h @@ -4,11 +4,15 @@ #include #include +class TransactionTableImpl; +class TransactionRecord; + class TransactionTableModel : public QAbstractTableModel { Q_OBJECT public: explicit TransactionTableModel(QObject *parent = 0); + ~TransactionTableModel(); enum { Status = 0, @@ -32,8 +36,16 @@ public: QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const; + QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; private: QStringList columns; + TransactionTableImpl *impl; + + QVariant formatTxStatus(const TransactionRecord *wtx) const; + QVariant formatTxDate(const TransactionRecord *wtx) const; + QVariant formatTxDescription(const TransactionRecord *wtx) const; + QVariant formatTxDebit(const TransactionRecord *wtx) const; + QVariant formatTxCredit(const TransactionRecord *wtx) const; }; #endif diff --git a/gui/src/addresstablemodel.cpp b/gui/src/addresstablemodel.cpp index 9a72cd9725..aec29fa202 100644 --- a/gui/src/addresstablemodel.cpp +++ b/gui/src/addresstablemodel.cpp @@ -12,7 +12,13 @@ AddressTableModel::AddressTableModel(QObject *parent) : int AddressTableModel::rowCount(const QModelIndex &parent) const { - return 5; + Q_UNUSED(parent); + int retval = 0; + CRITICAL_BLOCK(cs_mapAddressBook) + { + retval = mapAddressBook.size(); + } + return retval; } int AddressTableModel::columnCount(const QModelIndex &parent) const diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index 168dffd7cd..d8c72ae019 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -200,6 +200,7 @@ QWidget *BitcoinGUI::createTabs() transaction_table->setModel(proxy_model); transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); + transaction_table->setSortingEnabled(true); transaction_table->verticalHeader()->hide(); transaction_table->horizontalHeader()->resizeSection( diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index 0f16a3faf2..454a10d93e 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -2,11 +2,122 @@ #include "main.h" #include +#include +#include const QString TransactionTableModel::Sent = "s"; const QString TransactionTableModel::Received = "r"; const QString TransactionTableModel::Generated = "g"; +/* Separate transaction record format from core. + * When the GUI is going to communicate with the core through the network, + * we'll need our own internal formats anyway. + */ +class TransactionRecord +{ +public: + /* Information that never changes for the life of the transaction + */ + uint256 hash; + int64 time; + int64 credit; + int64 debit; + int64 change; + int64 lockTime; + int64 timeReceived; + bool isCoinBase; + int blockIndex; + + /* Properties that change based on changes in block chain that come in + over the network. + */ + bool confirmed; + int depthInMainChain; + bool final; + int requestCount; + + TransactionRecord(const CWalletTx &tx) + { + /* Copy immutable properties. + */ + hash = tx.GetHash(); + time = tx.GetTxTime(); + credit = tx.GetCredit(true); + debit = tx.GetDebit(); + change = tx.GetChange(); + isCoinBase = tx.IsCoinBase(); + lockTime = tx.nLockTime; + timeReceived = tx.nTimeReceived; + + /* Find the block the tx is in, store the index + */ + CBlockIndex* pindex = NULL; + std::map::iterator mi = mapBlockIndex.find(tx.hashBlock); + if (mi != mapBlockIndex.end()) + pindex = (*mi).second; + blockIndex = (pindex ? pindex->nHeight : INT_MAX); + + update(tx); + } + + void update(const CWalletTx &tx) + { + confirmed = tx.IsConfirmed(); + depthInMainChain = tx.GetDepthInMainChain(); + final = tx.IsFinal(); + requestCount = tx.GetRequestCount(); + } +}; + +/* Internal implementation */ +class TransactionTableImpl +{ +public: + QList cachedWallet; + + /* Update our model of the wallet */ + void updateWallet() + { + QList insertedIndices; + QList removedIndices; + + cachedWallet.clear(); + + /* Query wallet from core, and compare with our own + representation. + */ + CRITICAL_BLOCK(cs_mapWallet) + { + for(std::map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + /* TODO: Make note of new and removed transactions */ + /* insertedIndices */ + /* removedIndices */ + cachedWallet.append(TransactionRecord(it->second)); + } + } + /* beginInsertRows(QModelIndex(), first, last) */ + /* endInsertRows */ + /* beginRemoveRows(QModelIndex(), first, last) */ + /* beginEndRows */ + } + + int size() + { + return cachedWallet.size(); + } + + TransactionRecord *index(int idx) + { + if(idx >= 0 && idx < cachedWallet.size()) + { + return &cachedWallet[idx]; + } else { + return 0; + } + } +}; + /* Credit and Debit columns are right-aligned as they contain numbers */ static int column_alignments[] = { Qt::AlignLeft|Qt::AlignVCenter, @@ -18,15 +129,32 @@ static int column_alignments[] = { }; TransactionTableModel::TransactionTableModel(QObject *parent): - QAbstractTableModel(parent) + QAbstractTableModel(parent), + impl(new TransactionTableImpl()) { columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); + + impl->updateWallet(); +} + +TransactionTableModel::~TransactionTableModel() +{ + delete impl; } + int TransactionTableModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); - return 4; + /* + int retval = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + retval = mapWallet.size(); + } + return retval; + */ + return impl->size(); } int TransactionTableModel::columnCount(const QModelIndex &parent) const @@ -35,16 +163,72 @@ int TransactionTableModel::columnCount(const QModelIndex &parent) const return columns.length(); } +QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const +{ + return QVariant(QString("Test")); +#if 0 + // Status + if (!wtx.IsFinal()) + { + if (wtx.nLockTime < 500000000) + return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime); + else + return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str()); + } + else + { + int nDepth = wtx.GetDepthInMainChain(); + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + return strprintf(_("%d/offline?"), nDepth); + else if (nDepth < 6) + return strprintf(_("%d/unconfirmed"), nDepth); + else + return strprintf(_("%d confirmations"), nDepth); + } +#endif +} + +QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const +{ + return QVariant(); +} + +QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx) const +{ + return QVariant(); +} + +QVariant TransactionTableModel::formatTxDebit(const TransactionRecord *wtx) const +{ + return QVariant(); +} + +QVariant TransactionTableModel::formatTxCredit(const TransactionRecord *wtx) const +{ + return QVariant(); +} + QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { if(!index.isValid()) return QVariant(); + TransactionRecord *rec = static_cast(index.internalPointer()); if(role == Qt::DisplayRole) { - /* index.row(), index.column() */ - /* Return QString */ - return QLocale::system().toString(index.row()); + switch(index.column()) + { + case Status: + return formatTxStatus(rec); + case Date: + return formatTxDate(rec); + case Description: + return formatTxDescription(rec); + case Debit: + return formatTxDebit(rec); + case Credit: + return formatTxCredit(rec); + } } else if (role == Qt::TextAlignmentRole) { return column_alignments[index.column()]; @@ -82,8 +266,19 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat Qt::ItemFlags TransactionTableModel::flags(const QModelIndex &index) const { - if (!index.isValid()) - return Qt::ItemIsEnabled; - return QAbstractTableModel::flags(index); } + + +QModelIndex TransactionTableModel::index ( int row, int column, const QModelIndex & parent ) const +{ + Q_UNUSED(parent); + TransactionRecord *data = impl->index(row); + if(data) + { + return createIndex(row, column, impl->index(row)); + } else { + return QModelIndex(); + } +} + -- cgit v1.2.3 From 0856c1a03e4c663b09fa50237198c4af382dc18d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 27 May 2011 18:38:30 +0200 Subject: work on transaction list model --- bitcoin.pro | 6 +- gui/include/guiutil.h | 8 + gui/include/transactiontablemodel.h | 3 +- gui/src/guiutil.cpp | 9 + gui/src/transactiontablemodel.cpp | 423 +++++++++++++++++++++++++++++------- 5 files changed, 370 insertions(+), 79 deletions(-) create mode 100644 gui/include/guiutil.h create mode 100644 gui/src/guiutil.cpp diff --git a/bitcoin.pro b/bitcoin.pro index f323182308..f4f6a1a1e5 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -54,7 +54,8 @@ HEADERS += gui/include/bitcoingui.h \ json/include/json/json_spirit.h \ core/include/rpc.h \ gui/src/clientmodel.h \ - gui/include/clientmodel.h + gui/include/clientmodel.h \ + gui/include/guiutil.h SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/transactiontablemodel.cpp \ gui/src/addresstablemodel.cpp \ @@ -78,7 +79,8 @@ SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ json/src/json_spirit_writer.cpp \ json/src/json_spirit_value.cpp \ json/src/json_spirit_reader.cpp \ - gui/src/clientmodel.cpp + gui/src/clientmodel.cpp \ + gui/src/guiutil.cpp RESOURCES += \ gui/bitcoin.qrc diff --git a/gui/include/guiutil.h b/gui/include/guiutil.h new file mode 100644 index 0000000000..a73eadc02a --- /dev/null +++ b/gui/include/guiutil.h @@ -0,0 +1,8 @@ +#ifndef GUIUTIL_H +#define GUIUTIL_H + +#include + +QString DateTimeStr(qint64 nTime); + +#endif // GUIUTIL_H diff --git a/gui/include/transactiontablemodel.h b/gui/include/transactiontablemodel.h index 684a9470dd..2d56299574 100644 --- a/gui/include/transactiontablemodel.h +++ b/gui/include/transactiontablemodel.h @@ -26,10 +26,11 @@ public: TypeRole = Qt::UserRole } RoleIndex; - /* Transaction type */ + /* TypeRole values */ static const QString Sent; static const QString Received; static const QString Generated; + static const QString Other; int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; diff --git a/gui/src/guiutil.cpp b/gui/src/guiutil.cpp new file mode 100644 index 0000000000..3a5b5ac344 --- /dev/null +++ b/gui/src/guiutil.cpp @@ -0,0 +1,9 @@ +#include "guiutil.h" + +#include + +QString DateTimeStr(qint64 nTime) +{ + QDateTime date = QDateTime::fromMSecsSinceEpoch(nTime*1000); + return date.toString(Qt::DefaultLocaleShortDate) + QString(" ") + date.toString("hh:mm"); +} diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index 454a10d93e..af907474fa 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -1,4 +1,5 @@ #include "transactiontablemodel.h" +#include "guiutil.h" #include "main.h" #include @@ -8,66 +9,319 @@ const QString TransactionTableModel::Sent = "s"; const QString TransactionTableModel::Received = "r"; const QString TransactionTableModel::Generated = "g"; +const QString TransactionTableModel::Other = "o"; -/* Separate transaction record format from core. - * When the GUI is going to communicate with the core through the network, - * we'll need our own internal formats anyway. +/* TODO: look up address in address book + when showing. + Color based on confirmation status. + (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128)) */ + +class TransactionStatus +{ +public: + TransactionStatus(): + confirmed(false), sortKey(""), maturity(Mature), + matures_in(0), status(Offline), depth(0), open_for(0) + { } + + enum Maturity + { + Immature, + Mature, + MaturesIn, + MaturesWarning, /* Will probably not mature because no nodes have confirmed */ + NotAccepted + }; + + enum Status { + OpenUntilDate, + OpenUntilBlock, + Offline, + Unconfirmed, + HaveConfirmations + }; + + bool confirmed; + std::string sortKey; + + /* For "Generated" transactions */ + Maturity maturity; + int matures_in; + + /* Reported status */ + Status status; + int64 depth; + int64 open_for; /* Timestamp if status==OpenUntilDate, otherwise number of blocks */ +}; + class TransactionRecord { public: - /* Information that never changes for the life of the transaction - */ + enum Type + { + Other, + Generated, + SendToAddress, + SendToIP, + RecvFromAddress, + RecvFromIP, + SendToSelf + }; + + TransactionRecord(): + hash(), time(0), type(Other), address(""), debit(0), credit(0) + { + } + + TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status): + hash(hash), time(time), type(Other), address(""), debit(0), + credit(0), status(status) + { + } + + TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status, + Type type, const std::string &address, + int64 debit, int64 credit): + hash(hash), time(time), type(type), address(address), debit(debit), credit(credit), + status(status) + { + } + + /* Fixed */ uint256 hash; int64 time; - int64 credit; + Type type; + std::string address; int64 debit; - int64 change; - int64 lockTime; - int64 timeReceived; - bool isCoinBase; - int blockIndex; - - /* Properties that change based on changes in block chain that come in - over the network. - */ - bool confirmed; - int depthInMainChain; - bool final; - int requestCount; + int64 credit; + + /* Status: can change with block chain update */ + TransactionStatus status; +}; - TransactionRecord(const CWalletTx &tx) +/* Return positive answer if transaction should be shown in list. + */ +bool showTransaction(const CWalletTx &wtx) +{ + if (wtx.IsCoinBase()) { - /* Copy immutable properties. - */ - hash = tx.GetHash(); - time = tx.GetTxTime(); - credit = tx.GetCredit(true); - debit = tx.GetDebit(); - change = tx.GetChange(); - isCoinBase = tx.IsCoinBase(); - lockTime = tx.nLockTime; - timeReceived = tx.nTimeReceived; - - /* Find the block the tx is in, store the index - */ - CBlockIndex* pindex = NULL; - std::map::iterator mi = mapBlockIndex.find(tx.hashBlock); - if (mi != mapBlockIndex.end()) - pindex = (*mi).second; - blockIndex = (pindex ? pindex->nHeight : INT_MAX); + // Don't show generated coin until confirmed by at least one block after it + // so we don't get the user's hopes up until it looks like it's probably accepted. + // + // It is not an error when generated blocks are not accepted. By design, + // some percentage of blocks, like 10% or more, will end up not accepted. + // This is the normal mechanism by which the network copes with latency. + // + // We display regular transactions right away before any confirmation + // because they can always get into some block eventually. Generated coins + // are special because if their block is not accepted, they are not valid. + // + if (wtx.GetDepthInMainChain() < 2) + { + return false; + } + } + return true; +} + +/* Decompose CWallet transaction to model transaction records. + */ +QList decomposeTransaction(const CWalletTx &wtx) +{ + QList parts; + int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime(); + int64 nCredit = wtx.GetCredit(true); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + uint256 hash = wtx.GetHash(); + std::map mapValue = wtx.mapValue; + + // Find the block the tx is in + CBlockIndex* pindex = NULL; + std::map::iterator mi = mapBlockIndex.find(wtx.hashBlock); + if (mi != mapBlockIndex.end()) + pindex = (*mi).second; + + // Determine transaction status + TransactionStatus status; + // Sort order, unrecorded transactions sort to the top + status.sortKey = strprintf("%010d-%01d-%010u", + (pindex ? pindex->nHeight : INT_MAX), + (wtx.IsCoinBase() ? 1 : 0), + wtx.nTimeReceived); + status.confirmed = wtx.IsConfirmed(); + status.depth = wtx.GetDepthInMainChain(); - update(tx); + if (!wtx.IsFinal()) + { + if (wtx.nLockTime < 500000000) + { + status.status = TransactionStatus::OpenUntilBlock; + status.open_for = nBestHeight - wtx.nLockTime; + } else { + status.status = TransactionStatus::OpenUntilDate; + status.open_for = wtx.nLockTime; + } + } + else + { + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + { + status.status = TransactionStatus::Offline; + } else if (status.depth < 6) + { + status.status = TransactionStatus::Unconfirmed; + } else + { + status.status = TransactionStatus::HaveConfirmations; + } } - void update(const CWalletTx &tx) + if (showTransaction(wtx)) { - confirmed = tx.IsConfirmed(); - depthInMainChain = tx.GetDepthInMainChain(); - final = tx.IsFinal(); - requestCount = tx.GetRequestCount(); + + if (nNet > 0 || wtx.IsCoinBase()) + { + // + // Credit + // + TransactionRecord sub(hash, nTime, status); + + sub.credit = nNet; + + if (wtx.IsCoinBase()) + { + // Generated + sub.type = TransactionRecord::Generated; + + if (nCredit == 0) + { + sub.status.maturity = TransactionStatus::Immature; + + int64 nUnmatured = 0; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + nUnmatured += txout.GetCredit(); + sub.credit = nUnmatured; + + if (wtx.IsInMainChain()) + { + sub.status.maturity = TransactionStatus::MaturesIn; + sub.status.matures_in = wtx.GetBlocksToMaturity(); + + // Check if the block was requested by anyone + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + sub.status.maturity = TransactionStatus::MaturesWarning; + } + else + { + sub.status.maturity = TransactionStatus::NotAccepted; + } + } + } + else if (!mapValue["from"].empty() || !mapValue["message"].empty()) + { + // Received by IP connection + sub.type = TransactionRecord::RecvFromIP; + if (!mapValue["from"].empty()) + sub.address = mapValue["from"]; + } + else + { + // Received by Bitcoin Address + sub.type = TransactionRecord::RecvFromAddress; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (txout.IsMine()) + { + std::vector vchPubKey; + if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) + { + sub.address = PubKeyToAddress(vchPubKey); + } + break; + } + } + } + parts.append(sub); + } + else + { + bool fAllFromMe = true; + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + fAllFromMe = fAllFromMe && txin.IsMine(); + + bool fAllToMe = true; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + fAllToMe = fAllToMe && txout.IsMine(); + + if (fAllFromMe && fAllToMe) + { + // Payment to self + int64 nChange = wtx.GetChange(); + + parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::SendToSelf, "", + -(nDebit - nChange), nCredit - nChange)); + } + else if (fAllFromMe) + { + // + // Debit + // + int64 nTxFee = nDebit - wtx.GetValueOut(); + + for (int nOut = 0; nOut < wtx.vout.size(); nOut++) + { + const CTxOut& txout = wtx.vout[nOut]; + TransactionRecord sub(hash, nTime, status); + + if (txout.IsMine()) + { + // Sent to self + sub.type = TransactionRecord::SendToSelf; + sub.credit = txout.nValue; + } else if (!mapValue["to"].empty()) + { + // Sent to IP + sub.type = TransactionRecord::SendToIP; + sub.address = mapValue["to"]; + } else { + // Sent to Bitcoin Address + sub.type = TransactionRecord::SendToAddress; + uint160 hash160; + if (ExtractHash160(txout.scriptPubKey, hash160)) + sub.address = Hash160ToAddress(hash160); + } + + int64 nValue = txout.nValue; + /* Add fee to first output */ + if (nTxFee > 0) + { + nValue += nTxFee; + nTxFee = 0; + } + sub.debit = nValue; + sub.status.sortKey += strprintf("-%d", nOut); + + parts.append(sub); + } + } else { + // + // Mixed debit transaction, can't break down payees + // + bool fAllMine = true; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + fAllMine = fAllMine && txout.IsMine(); + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + fAllMine = fAllMine && txin.IsMine(); + + parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::Other, "", nNet, 0)); + } + } } -}; + + return parts; +} /* Internal implementation */ class TransactionTableImpl @@ -93,7 +347,7 @@ public: /* TODO: Make note of new and removed transactions */ /* insertedIndices */ /* removedIndices */ - cachedWallet.append(TransactionRecord(it->second)); + cachedWallet.append(decomposeTransaction(it->second)); } } /* beginInsertRows(QModelIndex(), first, last) */ @@ -146,14 +400,6 @@ TransactionTableModel::~TransactionTableModel() int TransactionTableModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); - /* - int retval = 0; - CRITICAL_BLOCK(cs_mapWallet) - { - retval = mapWallet.size(); - } - return retval; - */ return impl->size(); } @@ -165,32 +411,37 @@ int TransactionTableModel::columnCount(const QModelIndex &parent) const QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const { - return QVariant(QString("Test")); -#if 0 - // Status - if (!wtx.IsFinal()) + QString status; + switch(wtx->status.status) { - if (wtx.nLockTime < 500000000) - return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime); - else - return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str()); - } - else - { - int nDepth = wtx.GetDepthInMainChain(); - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) - return strprintf(_("%d/offline?"), nDepth); - else if (nDepth < 6) - return strprintf(_("%d/unconfirmed"), nDepth); - else - return strprintf(_("%d confirmations"), nDepth); + case TransactionStatus::OpenUntilBlock: + status = tr("Open for %n block(s)","",wtx->status.open_for); + break; + case TransactionStatus::OpenUntilDate: + status = tr("Open until ") + DateTimeStr(wtx->status.open_for); + break; + case TransactionStatus::Offline: + status = tr("%1/offline").arg(wtx->status.depth); + break; + case TransactionStatus::Unconfirmed: + status = tr("%1/unconfirmed").arg(wtx->status.depth); + break; + case TransactionStatus::HaveConfirmations: + status = tr("%1 confirmations").arg(wtx->status.depth); + break; } -#endif + + return QVariant(status); } QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const { - return QVariant(); + if(wtx->time) + { + return QVariant(DateTimeStr(wtx->time)); + } else { + return QVariant(); + } } QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx) const @@ -200,12 +451,32 @@ QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx QVariant TransactionTableModel::formatTxDebit(const TransactionRecord *wtx) const { - return QVariant(); + if(wtx->debit) + { + QString str = QString::fromStdString(FormatMoney(wtx->debit)); + if(!wtx->status.confirmed) + { + str = QString("[") + str + QString("]"); + } + return QVariant(str); + } else { + return QVariant(); + } } QVariant TransactionTableModel::formatTxCredit(const TransactionRecord *wtx) const { - return QVariant(); + if(wtx->credit) + { + QString str = QString::fromStdString(FormatMoney(wtx->credit)); + if(!wtx->status.confirmed) + { + str = QString("[") + str + QString("]"); + } + return QVariant(str); + } else { + return QVariant(); + } } QVariant TransactionTableModel::data(const QModelIndex &index, int role) const -- cgit v1.2.3 From f488e7358dad96a676ea000d1d253ccfa24afaaa Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 27 May 2011 19:48:42 +0200 Subject: transaction color based on confirmed/not confirmed, basic transaction model impl --- bitcoin.pro | 6 +- gui/include/transactionrecord.h | 94 +++++++++ gui/include/transactiontablemodel.h | 1 - gui/src/bitcoingui.cpp | 4 +- gui/src/guiutil.cpp | 2 +- gui/src/transactionrecord.cpp | 224 +++++++++++++++++++++ gui/src/transactiontablemodel.cpp | 375 +++++------------------------------- 7 files changed, 376 insertions(+), 330 deletions(-) create mode 100644 gui/include/transactionrecord.h create mode 100644 gui/src/transactionrecord.cpp diff --git a/bitcoin.pro b/bitcoin.pro index f4f6a1a1e5..5f406e82f8 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -55,7 +55,8 @@ HEADERS += gui/include/bitcoingui.h \ core/include/rpc.h \ gui/src/clientmodel.h \ gui/include/clientmodel.h \ - gui/include/guiutil.h + gui/include/guiutil.h \ + gui/include/transactionrecord.h SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/transactiontablemodel.cpp \ gui/src/addresstablemodel.cpp \ @@ -80,7 +81,8 @@ SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ json/src/json_spirit_value.cpp \ json/src/json_spirit_reader.cpp \ gui/src/clientmodel.cpp \ - gui/src/guiutil.cpp + gui/src/guiutil.cpp \ + gui/src/transactionrecord.cpp RESOURCES += \ gui/bitcoin.qrc diff --git a/gui/include/transactionrecord.h b/gui/include/transactionrecord.h new file mode 100644 index 0000000000..47eed05d93 --- /dev/null +++ b/gui/include/transactionrecord.h @@ -0,0 +1,94 @@ +#ifndef TRANSACTIONRECORD_H +#define TRANSACTIONRECORD_H + +#include "main.h" + +#include + +class TransactionStatus +{ +public: + TransactionStatus(): + confirmed(false), sortKey(""), maturity(Mature), + matures_in(0), status(Offline), depth(0), open_for(0) + { } + + enum Maturity + { + Immature, + Mature, + MaturesIn, + MaturesWarning, /* Will likely not mature because no nodes have confirmed */ + NotAccepted + }; + + enum Status { + OpenUntilDate, + OpenUntilBlock, + Offline, + Unconfirmed, + HaveConfirmations + }; + + bool confirmed; + std::string sortKey; + + /* For "Generated" transactions */ + Maturity maturity; + int matures_in; + + /* Reported status */ + Status status; + int64 depth; + int64 open_for; /* Timestamp if status==OpenUntilDate, otherwise number of blocks */ +}; + +class TransactionRecord +{ +public: + enum Type + { + Other, + Generated, + SendToAddress, + SendToIP, + RecvFromAddress, + RecvFromIP, + SendToSelf + }; + + TransactionRecord(): + hash(), time(0), type(Other), address(""), debit(0), credit(0) + { + } + + TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status): + hash(hash), time(time), type(Other), address(""), debit(0), + credit(0), status(status) + { + } + + TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status, + Type type, const std::string &address, + int64 debit, int64 credit): + hash(hash), time(time), type(type), address(address), debit(debit), credit(credit), + status(status) + { + } + + static bool showTransaction(const CWalletTx &wtx); + static QList decomposeTransaction(const CWalletTx &wtx); + + /* Fixed */ + uint256 hash; + int64 time; + Type type; + std::string address; + int64 debit; + int64 credit; + + /* Status: can change with block chain update */ + TransactionStatus status; +}; + +#endif // TRANSACTIONRECORD_H diff --git a/gui/include/transactiontablemodel.h b/gui/include/transactiontablemodel.h index 2d56299574..2ff3b0cbe1 100644 --- a/gui/include/transactiontablemodel.h +++ b/gui/include/transactiontablemodel.h @@ -29,7 +29,6 @@ public: /* TypeRole values */ static const QString Sent; static const QString Received; - static const QString Generated; static const QString Other; int rowCount(const QModelIndex &parent) const; diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index d8c72ae019..d7a71fc56c 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -204,9 +204,9 @@ QWidget *BitcoinGUI::createTabs() transaction_table->verticalHeader()->hide(); transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Status, 112); + TransactionTableModel::Status, 120); transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Date, 112); + TransactionTableModel::Date, 120); transaction_table->horizontalHeader()->setResizeMode( TransactionTableModel::Description, QHeaderView::Stretch); transaction_table->horizontalHeader()->resizeSection( diff --git a/gui/src/guiutil.cpp b/gui/src/guiutil.cpp index 3a5b5ac344..d27a8e101c 100644 --- a/gui/src/guiutil.cpp +++ b/gui/src/guiutil.cpp @@ -5,5 +5,5 @@ QString DateTimeStr(qint64 nTime) { QDateTime date = QDateTime::fromMSecsSinceEpoch(nTime*1000); - return date.toString(Qt::DefaultLocaleShortDate) + QString(" ") + date.toString("hh:mm"); + return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm"); } diff --git a/gui/src/transactionrecord.cpp b/gui/src/transactionrecord.cpp new file mode 100644 index 0000000000..8126cc763a --- /dev/null +++ b/gui/src/transactionrecord.cpp @@ -0,0 +1,224 @@ +#include "transactionrecord.h" + + +/* Return positive answer if transaction should be shown in list. + */ +bool TransactionRecord::showTransaction(const CWalletTx &wtx) +{ + if (wtx.IsCoinBase()) + { + // Don't show generated coin until confirmed by at least one block after it + // so we don't get the user's hopes up until it looks like it's probably accepted. + // + // It is not an error when generated blocks are not accepted. By design, + // some percentage of blocks, like 10% or more, will end up not accepted. + // This is the normal mechanism by which the network copes with latency. + // + // We display regular transactions right away before any confirmation + // because they can always get into some block eventually. Generated coins + // are special because if their block is not accepted, they are not valid. + // + if (wtx.GetDepthInMainChain() < 2) + { + return false; + } + } + return true; +} + +/* Decompose CWallet transaction to model transaction records. + */ +QList TransactionRecord::decomposeTransaction(const CWalletTx &wtx) +{ + QList parts; + int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime(); + int64 nCredit = wtx.GetCredit(true); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + uint256 hash = wtx.GetHash(); + std::map mapValue = wtx.mapValue; + + // Find the block the tx is in + CBlockIndex* pindex = NULL; + std::map::iterator mi = mapBlockIndex.find(wtx.hashBlock); + if (mi != mapBlockIndex.end()) + pindex = (*mi).second; + + // Determine transaction status + TransactionStatus status; + // Sort order, unrecorded transactions sort to the top + status.sortKey = strprintf("%010d-%01d-%010u", + (pindex ? pindex->nHeight : INT_MAX), + (wtx.IsCoinBase() ? 1 : 0), + wtx.nTimeReceived); + status.confirmed = wtx.IsConfirmed(); + status.depth = wtx.GetDepthInMainChain(); + + if (!wtx.IsFinal()) + { + if (wtx.nLockTime < 500000000) + { + status.status = TransactionStatus::OpenUntilBlock; + status.open_for = nBestHeight - wtx.nLockTime; + } else { + status.status = TransactionStatus::OpenUntilDate; + status.open_for = wtx.nLockTime; + } + } + else + { + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + { + status.status = TransactionStatus::Offline; + } else if (status.depth < 6) + { + status.status = TransactionStatus::Unconfirmed; + } else + { + status.status = TransactionStatus::HaveConfirmations; + } + } + + if (showTransaction(wtx)) + { + if (nNet > 0 || wtx.IsCoinBase()) + { + // + // Credit + // + TransactionRecord sub(hash, nTime, status); + + sub.credit = nNet; + + if (wtx.IsCoinBase()) + { + // Generated + sub.type = TransactionRecord::Generated; + + if (nCredit == 0) + { + sub.status.maturity = TransactionStatus::Immature; + + int64 nUnmatured = 0; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + nUnmatured += txout.GetCredit(); + sub.credit = nUnmatured; + + if (wtx.IsInMainChain()) + { + sub.status.maturity = TransactionStatus::MaturesIn; + sub.status.matures_in = wtx.GetBlocksToMaturity(); + + // Check if the block was requested by anyone + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + sub.status.maturity = TransactionStatus::MaturesWarning; + } + else + { + sub.status.maturity = TransactionStatus::NotAccepted; + } + } + } + else if (!mapValue["from"].empty() || !mapValue["message"].empty()) + { + // Received by IP connection + sub.type = TransactionRecord::RecvFromIP; + if (!mapValue["from"].empty()) + sub.address = mapValue["from"]; + } + else + { + // Received by Bitcoin Address + sub.type = TransactionRecord::RecvFromAddress; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (txout.IsMine()) + { + std::vector vchPubKey; + if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) + { + sub.address = PubKeyToAddress(vchPubKey); + } + break; + } + } + } + parts.append(sub); + } + else + { + bool fAllFromMe = true; + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + fAllFromMe = fAllFromMe && txin.IsMine(); + + bool fAllToMe = true; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + fAllToMe = fAllToMe && txout.IsMine(); + + if (fAllFromMe && fAllToMe) + { + // Payment to self + int64 nChange = wtx.GetChange(); + + parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::SendToSelf, "", + -(nDebit - nChange), nCredit - nChange)); + } + else if (fAllFromMe) + { + // + // Debit + // + int64 nTxFee = nDebit - wtx.GetValueOut(); + + for (int nOut = 0; nOut < wtx.vout.size(); nOut++) + { + const CTxOut& txout = wtx.vout[nOut]; + TransactionRecord sub(hash, nTime, status); + + if (txout.IsMine()) + { + // Sent to self + sub.type = TransactionRecord::SendToSelf; + sub.credit = txout.nValue; + } else if (!mapValue["to"].empty()) + { + // Sent to IP + sub.type = TransactionRecord::SendToIP; + sub.address = mapValue["to"]; + } else { + // Sent to Bitcoin Address + sub.type = TransactionRecord::SendToAddress; + uint160 hash160; + if (ExtractHash160(txout.scriptPubKey, hash160)) + sub.address = Hash160ToAddress(hash160); + } + + int64 nValue = txout.nValue; + /* Add fee to first output */ + if (nTxFee > 0) + { + nValue += nTxFee; + nTxFee = 0; + } + sub.debit = nValue; + sub.status.sortKey += strprintf("-%d", nOut); + + parts.append(sub); + } + } else { + // + // Mixed debit transaction, can't break down payees + // + bool fAllMine = true; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + fAllMine = fAllMine && txout.IsMine(); + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + fAllMine = fAllMine && txin.IsMine(); + + parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::Other, "", nNet, 0)); + } + } + } + + return parts; +} diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index af907474fa..4f84f4aedf 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -1,328 +1,17 @@ #include "transactiontablemodel.h" #include "guiutil.h" +#include "transactionrecord.h" #include "main.h" #include #include #include +#include const QString TransactionTableModel::Sent = "s"; const QString TransactionTableModel::Received = "r"; -const QString TransactionTableModel::Generated = "g"; const QString TransactionTableModel::Other = "o"; -/* TODO: look up address in address book - when showing. - Color based on confirmation status. - (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128)) - */ - -class TransactionStatus -{ -public: - TransactionStatus(): - confirmed(false), sortKey(""), maturity(Mature), - matures_in(0), status(Offline), depth(0), open_for(0) - { } - - enum Maturity - { - Immature, - Mature, - MaturesIn, - MaturesWarning, /* Will probably not mature because no nodes have confirmed */ - NotAccepted - }; - - enum Status { - OpenUntilDate, - OpenUntilBlock, - Offline, - Unconfirmed, - HaveConfirmations - }; - - bool confirmed; - std::string sortKey; - - /* For "Generated" transactions */ - Maturity maturity; - int matures_in; - - /* Reported status */ - Status status; - int64 depth; - int64 open_for; /* Timestamp if status==OpenUntilDate, otherwise number of blocks */ -}; - -class TransactionRecord -{ -public: - enum Type - { - Other, - Generated, - SendToAddress, - SendToIP, - RecvFromAddress, - RecvFromIP, - SendToSelf - }; - - TransactionRecord(): - hash(), time(0), type(Other), address(""), debit(0), credit(0) - { - } - - TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status): - hash(hash), time(time), type(Other), address(""), debit(0), - credit(0), status(status) - { - } - - TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status, - Type type, const std::string &address, - int64 debit, int64 credit): - hash(hash), time(time), type(type), address(address), debit(debit), credit(credit), - status(status) - { - } - - /* Fixed */ - uint256 hash; - int64 time; - Type type; - std::string address; - int64 debit; - int64 credit; - - /* Status: can change with block chain update */ - TransactionStatus status; -}; - -/* Return positive answer if transaction should be shown in list. - */ -bool showTransaction(const CWalletTx &wtx) -{ - if (wtx.IsCoinBase()) - { - // Don't show generated coin until confirmed by at least one block after it - // so we don't get the user's hopes up until it looks like it's probably accepted. - // - // It is not an error when generated blocks are not accepted. By design, - // some percentage of blocks, like 10% or more, will end up not accepted. - // This is the normal mechanism by which the network copes with latency. - // - // We display regular transactions right away before any confirmation - // because they can always get into some block eventually. Generated coins - // are special because if their block is not accepted, they are not valid. - // - if (wtx.GetDepthInMainChain() < 2) - { - return false; - } - } - return true; -} - -/* Decompose CWallet transaction to model transaction records. - */ -QList decomposeTransaction(const CWalletTx &wtx) -{ - QList parts; - int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime(); - int64 nCredit = wtx.GetCredit(true); - int64 nDebit = wtx.GetDebit(); - int64 nNet = nCredit - nDebit; - uint256 hash = wtx.GetHash(); - std::map mapValue = wtx.mapValue; - - // Find the block the tx is in - CBlockIndex* pindex = NULL; - std::map::iterator mi = mapBlockIndex.find(wtx.hashBlock); - if (mi != mapBlockIndex.end()) - pindex = (*mi).second; - - // Determine transaction status - TransactionStatus status; - // Sort order, unrecorded transactions sort to the top - status.sortKey = strprintf("%010d-%01d-%010u", - (pindex ? pindex->nHeight : INT_MAX), - (wtx.IsCoinBase() ? 1 : 0), - wtx.nTimeReceived); - status.confirmed = wtx.IsConfirmed(); - status.depth = wtx.GetDepthInMainChain(); - - if (!wtx.IsFinal()) - { - if (wtx.nLockTime < 500000000) - { - status.status = TransactionStatus::OpenUntilBlock; - status.open_for = nBestHeight - wtx.nLockTime; - } else { - status.status = TransactionStatus::OpenUntilDate; - status.open_for = wtx.nLockTime; - } - } - else - { - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) - { - status.status = TransactionStatus::Offline; - } else if (status.depth < 6) - { - status.status = TransactionStatus::Unconfirmed; - } else - { - status.status = TransactionStatus::HaveConfirmations; - } - } - - if (showTransaction(wtx)) - { - - if (nNet > 0 || wtx.IsCoinBase()) - { - // - // Credit - // - TransactionRecord sub(hash, nTime, status); - - sub.credit = nNet; - - if (wtx.IsCoinBase()) - { - // Generated - sub.type = TransactionRecord::Generated; - - if (nCredit == 0) - { - sub.status.maturity = TransactionStatus::Immature; - - int64 nUnmatured = 0; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - nUnmatured += txout.GetCredit(); - sub.credit = nUnmatured; - - if (wtx.IsInMainChain()) - { - sub.status.maturity = TransactionStatus::MaturesIn; - sub.status.matures_in = wtx.GetBlocksToMaturity(); - - // Check if the block was requested by anyone - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) - sub.status.maturity = TransactionStatus::MaturesWarning; - } - else - { - sub.status.maturity = TransactionStatus::NotAccepted; - } - } - } - else if (!mapValue["from"].empty() || !mapValue["message"].empty()) - { - // Received by IP connection - sub.type = TransactionRecord::RecvFromIP; - if (!mapValue["from"].empty()) - sub.address = mapValue["from"]; - } - else - { - // Received by Bitcoin Address - sub.type = TransactionRecord::RecvFromAddress; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - if (txout.IsMine()) - { - std::vector vchPubKey; - if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) - { - sub.address = PubKeyToAddress(vchPubKey); - } - break; - } - } - } - parts.append(sub); - } - else - { - bool fAllFromMe = true; - BOOST_FOREACH(const CTxIn& txin, wtx.vin) - fAllFromMe = fAllFromMe && txin.IsMine(); - - bool fAllToMe = true; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - fAllToMe = fAllToMe && txout.IsMine(); - - if (fAllFromMe && fAllToMe) - { - // Payment to self - int64 nChange = wtx.GetChange(); - - parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::SendToSelf, "", - -(nDebit - nChange), nCredit - nChange)); - } - else if (fAllFromMe) - { - // - // Debit - // - int64 nTxFee = nDebit - wtx.GetValueOut(); - - for (int nOut = 0; nOut < wtx.vout.size(); nOut++) - { - const CTxOut& txout = wtx.vout[nOut]; - TransactionRecord sub(hash, nTime, status); - - if (txout.IsMine()) - { - // Sent to self - sub.type = TransactionRecord::SendToSelf; - sub.credit = txout.nValue; - } else if (!mapValue["to"].empty()) - { - // Sent to IP - sub.type = TransactionRecord::SendToIP; - sub.address = mapValue["to"]; - } else { - // Sent to Bitcoin Address - sub.type = TransactionRecord::SendToAddress; - uint160 hash160; - if (ExtractHash160(txout.scriptPubKey, hash160)) - sub.address = Hash160ToAddress(hash160); - } - - int64 nValue = txout.nValue; - /* Add fee to first output */ - if (nTxFee > 0) - { - nValue += nTxFee; - nTxFee = 0; - } - sub.debit = nValue; - sub.status.sortKey += strprintf("-%d", nOut); - - parts.append(sub); - } - } else { - // - // Mixed debit transaction, can't break down payees - // - bool fAllMine = true; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - fAllMine = fAllMine && txout.IsMine(); - BOOST_FOREACH(const CTxIn& txin, wtx.vin) - fAllMine = fAllMine && txin.IsMine(); - - parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::Other, "", nNet, 0)); - } - } - } - - return parts; -} - /* Internal implementation */ class TransactionTableImpl { @@ -347,7 +36,7 @@ public: /* TODO: Make note of new and removed transactions */ /* insertedIndices */ /* removedIndices */ - cachedWallet.append(decomposeTransaction(it->second)); + cachedWallet.append(TransactionRecord::decomposeTransaction(it->second)); } } /* beginInsertRows(QModelIndex(), first, last) */ @@ -446,7 +135,35 @@ QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx) const { - return QVariant(); + QString description; + /* TODO: look up label for wtx->address in address book if + TransactionRecord::RecvFromAddress / TransactionRecord::SendToAddress + + strDescription += strAddress.substr(0,12) + "... "; + strDescription += "(" + strLabel + ")"; + */ + switch(wtx->type) + { + case TransactionRecord::RecvFromAddress: + description = tr("From: ") + QString::fromStdString(wtx->address); + break; + case TransactionRecord::RecvFromIP: + description = tr("From IP: ") + QString::fromStdString(wtx->address); + break; + case TransactionRecord::SendToAddress: + description = tr("To: ") + QString::fromStdString(wtx->address); + break; + case TransactionRecord::SendToIP: + description = tr("To IP: ") + QString::fromStdString(wtx->address); + break; + case TransactionRecord::SendToSelf: + description = tr("Payment to yourself"); + break; + case TransactionRecord::Generated: + description = tr("Generated"); + break; + } + return QVariant(description); } QVariant TransactionTableModel::formatTxDebit(const TransactionRecord *wtx) const @@ -503,18 +220,28 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const } else if (role == Qt::TextAlignmentRole) { return column_alignments[index.column()]; + } else if (role == Qt::ForegroundRole) + { + if(rec->status.confirmed) + { + return QColor(0, 0, 0); + } else { + return QColor(128, 128, 128); + } } else if (role == TypeRole) { - /* user role: transaction type for filtering - "s" (sent) - "r" (received) - "g" (generated) - */ - switch(index.row() % 3) + switch(rec->type) { - case 0: return QString("s"); - case 1: return QString("r"); - case 2: return QString("o"); + case TransactionRecord::RecvFromAddress: + case TransactionRecord::RecvFromIP: + case TransactionRecord::Generated: + return TransactionTableModel::Received; + case TransactionRecord::SendToAddress: + case TransactionRecord::SendToIP: + case TransactionRecord::SendToSelf: + return TransactionTableModel::Sent; + default: + return TransactionTableModel::Other; } } return QVariant(); -- cgit v1.2.3 From f79efbab6f0b6304e18df9452aae6aa0a1803ad9 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 27 May 2011 20:36:58 +0200 Subject: look up addresses in address book --- gui/src/transactiontablemodel.cpp | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index 4f84f4aedf..ffc2472f2f 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -101,6 +101,7 @@ int TransactionTableModel::columnCount(const QModelIndex &parent) const QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const { QString status; + switch(wtx->status.status) { case TransactionStatus::OpenUntilBlock: @@ -133,25 +134,42 @@ QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const } } +/* Look up address in address book, if found return + address[0:12]... (label) + otherwise just return address + */ +std::string lookupAddress(const std::string &address) +{ + std::string description; + CRITICAL_BLOCK(cs_mapAddressBook) + { + std::map::iterator mi = mapAddressBook.find(address); + if (mi != mapAddressBook.end() && !(*mi).second.empty()) + { + std::string label = (*mi).second; + description += address.substr(0,12) + "... "; + description += "(" + label + ")"; + } + else + description += address; + } + return description; +} + QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx) const { QString description; - /* TODO: look up label for wtx->address in address book if - TransactionRecord::RecvFromAddress / TransactionRecord::SendToAddress - strDescription += strAddress.substr(0,12) + "... "; - strDescription += "(" + strLabel + ")"; - */ switch(wtx->type) { case TransactionRecord::RecvFromAddress: - description = tr("From: ") + QString::fromStdString(wtx->address); + description = tr("From: ") + QString::fromStdString(lookupAddress(wtx->address)); break; case TransactionRecord::RecvFromIP: description = tr("From IP: ") + QString::fromStdString(wtx->address); break; case TransactionRecord::SendToAddress: - description = tr("To: ") + QString::fromStdString(wtx->address); + description = tr("To: ") + QString::fromStdString(lookupAddress(wtx->address)); break; case TransactionRecord::SendToIP: description = tr("To IP: ") + QString::fromStdString(wtx->address); @@ -160,6 +178,7 @@ QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx description = tr("Payment to yourself"); break; case TransactionRecord::Generated: + /* TODO: more extensive description */ description = tr("Generated"); break; } -- cgit v1.2.3 From dd8e82f797f0c34a248837013925344aeab2c877 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 27 May 2011 21:24:17 +0200 Subject: fix balance display, display number of transactions --- gui/include/bitcoingui.h | 2 +- gui/include/clientmodel.h | 4 ++-- gui/include/transactiontablemodel.h | 2 ++ gui/src/bitcoingui.cpp | 12 +++++++----- gui/src/clientmodel.cpp | 9 +++++++-- gui/src/transactiontablemodel.cpp | 6 ++++++ 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/gui/include/bitcoingui.h b/gui/include/bitcoingui.h index 3b722fcca4..80c24a3733 100644 --- a/gui/include/bitcoingui.h +++ b/gui/include/bitcoingui.h @@ -52,7 +52,7 @@ private: void createTrayIcon(); public slots: - void setBalance(double balance); + void setBalance(qint64 balance); void setAddress(const QString &address); void setNumConnections(int count); void setNumBlocks(int count); diff --git a/gui/include/clientmodel.h b/gui/include/clientmodel.h index fb1a5edebc..a5613e34b6 100644 --- a/gui/include/clientmodel.h +++ b/gui/include/clientmodel.h @@ -9,14 +9,14 @@ class ClientModel : public QObject public: explicit ClientModel(QObject *parent = 0); - double getBalance(); + qint64 getBalance(); QString getAddress(); int getNumConnections(); int getNumBlocks(); int getNumTransactions(); signals: - void balanceChanged(double balance); + void balanceChanged(qint64 balance); void addressChanged(const QString &address); void numConnectionsChanged(int count); void numBlocksChanged(int count); diff --git a/gui/include/transactiontablemodel.h b/gui/include/transactiontablemodel.h index 2ff3b0cbe1..1ea749d86a 100644 --- a/gui/include/transactiontablemodel.h +++ b/gui/include/transactiontablemodel.h @@ -37,6 +37,8 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const; QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; +public slots: + void updateWallet(); private: QStringList columns; TransactionTableImpl *impl; diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index d7a71fc56c..831bef3f20 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -11,6 +11,8 @@ #include "aboutdialog.h" #include "clientmodel.h" +#include "main.h" + #include #include #include @@ -268,9 +270,9 @@ void BitcoinGUI::copyClipboardClicked() QApplication::clipboard()->setText(address->text()); } -void BitcoinGUI::setBalance(double balance) +void BitcoinGUI::setBalance(qint64 balance) { - labelBalance->setText(QLocale::system().toString(balance, 8)); + labelBalance->setText(QString::fromStdString(FormatMoney(balance))); } void BitcoinGUI::setAddress(const QString &addr) @@ -280,15 +282,15 @@ void BitcoinGUI::setAddress(const QString &addr) void BitcoinGUI::setNumConnections(int count) { - labelConnections->setText(QLocale::system().toString(count)+" "+tr("connections")); + labelConnections->setText(QLocale::system().toString(count)+" "+tr("connections(s)", "", count)); } void BitcoinGUI::setNumBlocks(int count) { - labelBlocks->setText(QLocale::system().toString(count)+" "+tr("blocks")); + labelBlocks->setText(QLocale::system().toString(count)+" "+tr("block(s)", "", count)); } void BitcoinGUI::setNumTransactions(int count) { - labelTransactions->setText(QLocale::system().toString(count)+" "+tr("transactions")); + labelTransactions->setText(QLocale::system().toString(count)+" "+tr("transaction(s)", "", count)); } diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp index 0f191023b7..cc52df4123 100644 --- a/gui/src/clientmodel.cpp +++ b/gui/src/clientmodel.cpp @@ -17,7 +17,7 @@ ClientModel::ClientModel(QObject *parent) : timer->start(MODEL_UPDATE_DELAY); } -double ClientModel::getBalance() +qint64 ClientModel::getBalance() { return GetBalance(); } @@ -45,7 +45,12 @@ int ClientModel::getNumBlocks() int ClientModel::getNumTransactions() { - return 0; + int numTransactions = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + numTransactions = mapWallet.size(); + } + return numTransactions; } void ClientModel::update() diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index ffc2472f2f..900cb6daa8 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -85,6 +85,12 @@ TransactionTableModel::~TransactionTableModel() delete impl; } +void TransactionTableModel::updateWallet() +{ + beginResetModel(); + impl->updateWallet(); + endResetModel(); +} int TransactionTableModel::rowCount(const QModelIndex &parent) const { -- cgit v1.2.3 From 0eba00447e9e14415e1aa4834a03de0c5d221a1d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 27 May 2011 21:43:05 +0200 Subject: use real ParseMoney function to parse input to Send dialog --- gui/src/sendcoinsdialog.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/gui/src/sendcoinsdialog.cpp b/gui/src/sendcoinsdialog.cpp index 3907b4bc59..123b930a20 100644 --- a/gui/src/sendcoinsdialog.cpp +++ b/gui/src/sendcoinsdialog.cpp @@ -8,7 +8,9 @@ #include #include #include +#include +#include "util.h" #include "base58.h" SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : @@ -42,7 +44,7 @@ void SendCoinsDialog::on_sendButton_clicked() { QByteArray payTo = ui->payTo->text().toUtf8(); uint160 payToHash = 0; - double payAmount = 0.0; + int64 payAmount = 0.0; bool valid = false; if(!AddressToHash160(payTo.constData(), payToHash)) @@ -54,8 +56,9 @@ void SendCoinsDialog::on_sendButton_clicked() ui->payTo->setFocus(); return; } - payAmount = QLocale::system().toDouble(ui->payAmount->text(), &valid); - if(!valid || payAmount <= 0.0) + valid = ParseMoney(ui->payAmount->text().toStdString(), payAmount); + + if(!valid || payAmount <= 0) { QMessageBox::warning(this, tr("Warning"), tr("The amount to pay must be a valid number larger than 0."), @@ -64,6 +67,7 @@ void SendCoinsDialog::on_sendButton_clicked() ui->payAmount->setFocus(); return; } + qDebug() << "Pay " << payAmount; /* TODO: send command to core, once this succeeds do accept() */ accept(); -- cgit v1.2.3 From e923f8188ddd46f4565a3cffebb9fb260e3239bf Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 27 May 2011 22:06:30 +0200 Subject: extend generation descriptions --- gui/include/transactionrecord.h | 1 - gui/src/transactionrecord.cpp | 1 - gui/src/transactiontablemodel.cpp | 18 ++++++++++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/gui/include/transactionrecord.h b/gui/include/transactionrecord.h index 47eed05d93..646f87c918 100644 --- a/gui/include/transactionrecord.h +++ b/gui/include/transactionrecord.h @@ -17,7 +17,6 @@ public: { Immature, Mature, - MaturesIn, MaturesWarning, /* Will likely not mature because no nodes have confirmed */ NotAccepted }; diff --git a/gui/src/transactionrecord.cpp b/gui/src/transactionrecord.cpp index 8126cc763a..4024e25c59 100644 --- a/gui/src/transactionrecord.cpp +++ b/gui/src/transactionrecord.cpp @@ -106,7 +106,6 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx if (wtx.IsInMainChain()) { - sub.status.maturity = TransactionStatus::MaturesIn; sub.status.matures_in = wtx.GetBlocksToMaturity(); // Check if the block was requested by anyone diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index 900cb6daa8..a5d8ffdaaa 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -184,8 +184,22 @@ QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx description = tr("Payment to yourself"); break; case TransactionRecord::Generated: - /* TODO: more extensive description */ - description = tr("Generated"); + switch(wtx->status.maturity) + { + case TransactionStatus::Immature: + description = tr("Generated (matures in %n more blocks)", "", + wtx->status.matures_in); + break; + case TransactionStatus::Mature: + description = tr("Generated"); + break; + case TransactionStatus::MaturesWarning: + description = tr("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!"); + break; + case TransactionStatus::NotAccepted: + description = tr("Generated (not accepted)"); + break; + } break; } return QVariant(description); -- cgit v1.2.3 From 3e1ea1c0251261d423818aa11f01ff82c71672a5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 27 May 2011 22:09:22 +0200 Subject: Generated transactions are 'other', and only show up in All tab --- gui/src/transactiontablemodel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index a5d8ffdaaa..2f1b77eea1 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -273,7 +273,6 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { case TransactionRecord::RecvFromAddress: case TransactionRecord::RecvFromIP: - case TransactionRecord::Generated: return TransactionTableModel::Received; case TransactionRecord::SendToAddress: case TransactionRecord::SendToIP: -- cgit v1.2.3 From f6c18bc9edb921d189ffe888020d6d965ac2a3de Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 28 May 2011 16:09:23 +0200 Subject: documentation, small fixes --- gui/src/transactiontablemodel.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index 2f1b77eea1..af8234bddf 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -12,7 +12,7 @@ const QString TransactionTableModel::Sent = "s"; const QString TransactionTableModel::Received = "r"; const QString TransactionTableModel::Other = "o"; -/* Internal implementation */ +/* Private implementation, no need to pull this into header */ class TransactionTableImpl { public: @@ -87,6 +87,10 @@ TransactionTableModel::~TransactionTableModel() void TransactionTableModel::updateWallet() { + /* TODO: improve this, way too brute-force at the moment, + only update transactions that actually changed, and add/remove + transactions that were added/removed. + */ beginResetModel(); impl->updateWallet(); endResetModel(); @@ -210,7 +214,7 @@ QVariant TransactionTableModel::formatTxDebit(const TransactionRecord *wtx) cons if(wtx->debit) { QString str = QString::fromStdString(FormatMoney(wtx->debit)); - if(!wtx->status.confirmed) + if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) { str = QString("[") + str + QString("]"); } @@ -225,7 +229,7 @@ QVariant TransactionTableModel::formatTxCredit(const TransactionRecord *wtx) con if(wtx->credit) { QString str = QString::fromStdString(FormatMoney(wtx->credit)); - if(!wtx->status.confirmed) + if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) { str = QString("[") + str + QString("]"); } @@ -243,6 +247,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const if(role == Qt::DisplayRole) { + /* Delegate to specific column handlers */ switch(index.column()) { case Status: @@ -261,6 +266,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return column_alignments[index.column()]; } else if (role == Qt::ForegroundRole) { + /* Non-confirmed transactions are grey */ if(rec->status.confirmed) { return QColor(0, 0, 0); @@ -269,6 +275,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const } } else if (role == TypeRole) { + /* Role for filtering tabs by type */ switch(rec->type) { case TransactionRecord::RecvFromAddress: @@ -287,15 +294,15 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const { - if(role == Qt::DisplayRole) + if(orientation == Qt::Horizontal) { - if(orientation == Qt::Horizontal) + if(role == Qt::DisplayRole) { return columns[section]; + } else if (role == Qt::TextAlignmentRole) + { + return column_alignments[section]; } - } else if (role == Qt::TextAlignmentRole) - { - return column_alignments[section]; } return QVariant(); } -- cgit v1.2.3 From 63760fa1cdb9f07fea7bb4c05a7fd7e0af5ead6d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 28 May 2011 20:32:19 +0200 Subject: auto-update transaction list --- bitcoin.pro | 3 +- gui/include/guiconstants.h | 11 +++++ gui/include/transactionrecord.h | 2 + gui/include/transactiontablemodel.h | 5 ++- gui/src/bitcoingui.cpp | 4 +- gui/src/clientmodel.cpp | 4 +- gui/src/transactiontablemodel.cpp | 90 +++++++++++++++++++++++++++++++------ 7 files changed, 98 insertions(+), 21 deletions(-) create mode 100644 gui/include/guiconstants.h diff --git a/bitcoin.pro b/bitcoin.pro index 5f406e82f8..e4d694eb41 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -56,7 +56,8 @@ HEADERS += gui/include/bitcoingui.h \ gui/src/clientmodel.h \ gui/include/clientmodel.h \ gui/include/guiutil.h \ - gui/include/transactionrecord.h + gui/include/transactionrecord.h \ + gui/include/guiconstants.h SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/transactiontablemodel.cpp \ gui/src/addresstablemodel.cpp \ diff --git a/gui/include/guiconstants.h b/gui/include/guiconstants.h new file mode 100644 index 0000000000..cdd1a74d98 --- /dev/null +++ b/gui/include/guiconstants.h @@ -0,0 +1,11 @@ +#ifndef GUICONSTANTS_H +#define GUICONSTANTS_H + +/* milliseconds between model updates */ +static const int MODEL_UPDATE_DELAY = 250; + +/* size of cache */ +static const unsigned int WALLET_CACHE_SIZE = 100; + + +#endif // GUICONSTANTS_H diff --git a/gui/include/transactionrecord.h b/gui/include/transactionrecord.h index 646f87c918..9769d8061e 100644 --- a/gui/include/transactionrecord.h +++ b/gui/include/transactionrecord.h @@ -75,6 +75,8 @@ public: { } + /* Decompose CWallet transaction to model transaction records. + */ static bool showTransaction(const CWalletTx &wtx); static QList decomposeTransaction(const CWalletTx &wtx); diff --git a/gui/include/transactiontablemodel.h b/gui/include/transactiontablemodel.h index 1ea749d86a..b02019d472 100644 --- a/gui/include/transactiontablemodel.h +++ b/gui/include/transactiontablemodel.h @@ -37,8 +37,6 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const; QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; -public slots: - void updateWallet(); private: QStringList columns; TransactionTableImpl *impl; @@ -48,6 +46,9 @@ private: QVariant formatTxDescription(const TransactionRecord *wtx) const; QVariant formatTxDebit(const TransactionRecord *wtx) const; QVariant formatTxCredit(const TransactionRecord *wtx) const; + +private slots: + void update(); }; #endif diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index 831bef3f20..b409744b98 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -147,7 +147,7 @@ void BitcoinGUI::setModel(ClientModel *model) this->model = model; setBalance(model->getBalance()); - connect(model, SIGNAL(balanceChanged(double)), this, SLOT(setBalance(double))); + connect(model, SIGNAL(balanceChanged(qint64)), this, SLOT(setBalance(qint64))); setNumConnections(model->getNumConnections()); connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); @@ -197,12 +197,14 @@ QWidget *BitcoinGUI::createTabs() proxy_model->setDynamicSortFilter(true); proxy_model->setFilterRole(TransactionTableModel::TypeRole); proxy_model->setFilterRegExp(QRegExp(tab_filters.at(i))); + proxy_model->setSortRole(Qt::EditRole); QTableView *transaction_table = new QTableView(this); transaction_table->setModel(proxy_model); transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); transaction_table->setSortingEnabled(true); + transaction_table->sortByColumn(TransactionTableModel::Status, Qt::DescendingOrder); transaction_table->verticalHeader()->hide(); transaction_table->horizontalHeader()->resizeSection( diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp index cc52df4123..e355676cd0 100644 --- a/gui/src/clientmodel.cpp +++ b/gui/src/clientmodel.cpp @@ -1,11 +1,9 @@ #include "clientmodel.h" #include "main.h" +#include "guiconstants.h" #include -/* milliseconds between model updates */ -const int MODEL_UPDATE_DELAY = 250; - ClientModel::ClientModel(QObject *parent) : QObject(parent) { diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index af8234bddf..3a501979f5 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -1,12 +1,14 @@ #include "transactiontablemodel.h" #include "guiutil.h" #include "transactionrecord.h" +#include "guiconstants.h" #include "main.h" #include #include #include #include +#include const QString TransactionTableModel::Sent = "s"; const QString TransactionTableModel::Received = "r"; @@ -16,14 +18,15 @@ const QString TransactionTableModel::Other = "o"; class TransactionTableImpl { public: + /* Local cache of wallet. + * As it is in the same order as the CWallet, by definition + * this is sorted by sha256. + */ QList cachedWallet; - /* Update our model of the wallet */ - void updateWallet() + void refreshWallet() { - QList insertedIndices; - QList removedIndices; - + qDebug() << "refreshWallet"; cachedWallet.clear(); /* Query wallet from core, and compare with our own @@ -45,6 +48,26 @@ public: /* beginEndRows */ } + /* Update our model of the wallet. + Call with list of hashes of transactions that were added, removed or changed. + */ + void updateWallet(const QList &updated) + { + /* TODO: update only transactions in updated, and only if + the transactions are really part of the visible wallet. + + Update status of the other transactions in the cache just in case, + because this call means that a new block came in. + */ + qDebug() << "updateWallet"; + foreach(uint256 hash, updated) + { + qDebug() << " " << QString::fromStdString(hash.ToString()); + } + + refreshWallet(); + } + int size() { return cachedWallet.size(); @@ -59,6 +82,7 @@ public: return 0; } } + }; /* Credit and Debit columns are right-aligned as they contain numbers */ @@ -77,7 +101,11 @@ TransactionTableModel::TransactionTableModel(QObject *parent): { columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); - impl->updateWallet(); + impl->refreshWallet(); + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(update())); + timer->start(MODEL_UPDATE_DELAY); } TransactionTableModel::~TransactionTableModel() @@ -85,15 +113,33 @@ TransactionTableModel::~TransactionTableModel() delete impl; } -void TransactionTableModel::updateWallet() +void TransactionTableModel::update() { - /* TODO: improve this, way too brute-force at the moment, - only update transactions that actually changed, and add/remove - transactions that were added/removed. - */ - beginResetModel(); - impl->updateWallet(); - endResetModel(); + QList updated; + + /* Check if there are changes to wallet map */ + TRY_CRITICAL_BLOCK(cs_mapWallet) + { + if(!vWalletUpdated.empty()) + { + BOOST_FOREACH(uint256 hash, vWalletUpdated) + { + updated.append(hash); + } + vWalletUpdated.clear(); + } + } + + if(!updated.empty()) + { + /* TODO: improve this, way too brute-force at the moment, + only update transactions that actually changed, and add/remove + transactions that were added/removed. + */ + beginResetModel(); + impl->updateWallet(updated); + endResetModel(); + } } int TransactionTableModel::rowCount(const QModelIndex &parent) const @@ -261,6 +307,22 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case Credit: return formatTxCredit(rec); } + } else if(role == Qt::EditRole) + { + /* Edit role is used for sorting so return the real values */ + switch(index.column()) + { + case Status: + return QString::fromStdString(rec->status.sortKey); + case Date: + return rec->time; + case Description: + return formatTxDescription(rec); + case Debit: + return rec->debit; + case Credit: + return rec->credit; + } } else if (role == Qt::TextAlignmentRole) { return column_alignments[index.column()]; -- cgit v1.2.3 From 8c937da5f2533dc31e5f071e4e3dcda12fe25e02 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 28 May 2011 22:31:27 +0200 Subject: "Receive with" i.s.o. "Receive from" address --- gui/include/transactionrecord.h | 2 +- gui/src/transactionrecord.cpp | 10 +++++----- gui/src/transactiontablemodel.cpp | 28 ++++++++++++---------------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/gui/include/transactionrecord.h b/gui/include/transactionrecord.h index 9769d8061e..d8aca76129 100644 --- a/gui/include/transactionrecord.h +++ b/gui/include/transactionrecord.h @@ -51,7 +51,7 @@ public: Generated, SendToAddress, SendToIP, - RecvFromAddress, + RecvWithAddress, RecvFromIP, SendToSelf }; diff --git a/gui/src/transactionrecord.cpp b/gui/src/transactionrecord.cpp index 4024e25c59..9feca4434b 100644 --- a/gui/src/transactionrecord.cpp +++ b/gui/src/transactionrecord.cpp @@ -128,7 +128,7 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx else { // Received by Bitcoin Address - sub.type = TransactionRecord::RecvFromAddress; + sub.type = TransactionRecord::RecvWithAddress; BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (txout.IsMine()) @@ -176,9 +176,9 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx if (txout.IsMine()) { - // Sent to self - sub.type = TransactionRecord::SendToSelf; - sub.credit = txout.nValue; + // Ignore parts sent to self, as this is usually the change + // from a transaction sent back to our own address. + continue; } else if (!mapValue["to"].empty()) { // Sent to IP @@ -199,7 +199,7 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx nValue += nTxFee; nTxFee = 0; } - sub.debit = nValue; + sub.debit = -nValue; sub.status.sortKey += strprintf("-%d", nOut); parts.append(sub); diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index 3a501979f5..57d618e9db 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -27,25 +27,17 @@ public: void refreshWallet() { qDebug() << "refreshWallet"; - cachedWallet.clear(); - /* Query wallet from core, and compare with our own - representation. + /* Query entire wallet from core. */ + cachedWallet.clear(); CRITICAL_BLOCK(cs_mapWallet) { for(std::map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - /* TODO: Make note of new and removed transactions */ - /* insertedIndices */ - /* removedIndices */ cachedWallet.append(TransactionRecord::decomposeTransaction(it->second)); } } - /* beginInsertRows(QModelIndex(), first, last) */ - /* endInsertRows */ - /* beginRemoveRows(QModelIndex(), first, last) */ - /* beginEndRows */ } /* Update our model of the wallet. @@ -64,6 +56,10 @@ public: { qDebug() << " " << QString::fromStdString(hash.ToString()); } + /* beginInsertRows(QModelIndex(), first, last) */ + /* endInsertRows */ + /* beginRemoveRows(QModelIndex(), first, last) */ + /* beginEndRows */ refreshWallet(); } @@ -218,17 +214,17 @@ QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx switch(wtx->type) { - case TransactionRecord::RecvFromAddress: - description = tr("From: ") + QString::fromStdString(lookupAddress(wtx->address)); + case TransactionRecord::RecvWithAddress: + description = tr("Received with: ") + QString::fromStdString(lookupAddress(wtx->address)); break; case TransactionRecord::RecvFromIP: - description = tr("From IP: ") + QString::fromStdString(wtx->address); + description = tr("Received from IP: ") + QString::fromStdString(wtx->address); break; case TransactionRecord::SendToAddress: - description = tr("To: ") + QString::fromStdString(lookupAddress(wtx->address)); + description = tr("Sent to: ") + QString::fromStdString(lookupAddress(wtx->address)); break; case TransactionRecord::SendToIP: - description = tr("To IP: ") + QString::fromStdString(wtx->address); + description = tr("Sent to IP: ") + QString::fromStdString(wtx->address); break; case TransactionRecord::SendToSelf: description = tr("Payment to yourself"); @@ -340,7 +336,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const /* Role for filtering tabs by type */ switch(rec->type) { - case TransactionRecord::RecvFromAddress: + case TransactionRecord::RecvWithAddress: case TransactionRecord::RecvFromIP: return TransactionTableModel::Received; case TransactionRecord::SendToAddress: -- cgit v1.2.3 From 1d7e321c10c568fec3f174df2287d8513d168a40 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 29 May 2011 21:31:29 +0200 Subject: mark specific warnings to disable for the cases where bitcoin core "sins" --- bitcoin.pro | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bitcoin.pro b/bitcoin.pro index e4d694eb41..49574d60f5 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -4,6 +4,10 @@ DEPENDPATH += . INCLUDEPATH += gui/include core/include cryptopp/include json/include unix:LIBS += -lssl -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 + +# disable quite some warnings becuase bitcoin core "sins" a lot +QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wno-invalid-offsetof -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare -Wno-char-subscripts -Wno-unused-value -Wno-sequence-point -Wno-parentheses -Wno-unknown-pragmas -Wno-switch + # WINDOWS defines, -DSSL, look at build system # Input @@ -53,7 +57,6 @@ HEADERS += gui/include/bitcoingui.h \ json/include/json/json_spirit_error_position.h \ json/include/json/json_spirit.h \ core/include/rpc.h \ - gui/src/clientmodel.h \ gui/include/clientmodel.h \ gui/include/guiutil.h \ gui/include/transactionrecord.h \ -- cgit v1.2.3 From 6630c1cbf5cb1f2672776ada14315dcdf058b567 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 30 May 2011 20:20:12 +0200 Subject: sending support --- gui/include/bitcoingui.h | 1 + gui/include/clientmodel.h | 17 ++++++++++++ gui/include/sendcoinsdialog.h | 4 +++ gui/src/bitcoingui.cpp | 13 +++++++++ gui/src/clientmodel.cpp | 53 +++++++++++++++++++++++++++++++++++ gui/src/sendcoinsdialog.cpp | 64 +++++++++++++++++++++++++++++-------------- 6 files changed, 131 insertions(+), 21 deletions(-) diff --git a/gui/include/bitcoingui.h b/gui/include/bitcoingui.h index 80c24a3733..955d7b4781 100644 --- a/gui/include/bitcoingui.h +++ b/gui/include/bitcoingui.h @@ -67,6 +67,7 @@ private slots: void newAddressClicked(); void copyClipboardClicked(); + void error(const QString &title, const QString &message); }; #endif diff --git a/gui/include/clientmodel.h b/gui/include/clientmodel.h index a5613e34b6..828c80f8db 100644 --- a/gui/include/clientmodel.h +++ b/gui/include/clientmodel.h @@ -9,18 +9,35 @@ class ClientModel : public QObject public: explicit ClientModel(QObject *parent = 0); + enum StatusCode + { + OK, + InvalidAmount, + InvalidAddress, + AmountExceedsBalance, + AmountWithFeeExceedsBalance, + Aborted, + MiscError + }; + qint64 getBalance(); QString getAddress(); int getNumConnections(); int getNumBlocks(); int getNumTransactions(); + qint64 getTransactionFee(); + + StatusCode sendCoins(const QString &payTo, qint64 payAmount); + signals: void balanceChanged(qint64 balance); void addressChanged(const QString &address); void numConnectionsChanged(int count); void numBlocksChanged(int count); void numTransactionsChanged(int count); + /* Asynchronous error notification */ + void error(const QString &title, const QString &message); public slots: diff --git a/gui/include/sendcoinsdialog.h b/gui/include/sendcoinsdialog.h index 95dd34b1e5..f73c38d63a 100644 --- a/gui/include/sendcoinsdialog.h +++ b/gui/include/sendcoinsdialog.h @@ -6,6 +6,7 @@ namespace Ui { class SendCoinsDialog; } +class ClientModel; class SendCoinsDialog : public QDialog { @@ -15,8 +16,11 @@ public: explicit SendCoinsDialog(QWidget *parent = 0, const QString &address = ""); ~SendCoinsDialog(); + void setModel(ClientModel *model); + private: Ui::SendCoinsDialog *ui; + ClientModel *model; private slots: void on_buttonBox_rejected(); diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index b409744b98..66a3edd3ad 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -160,6 +161,9 @@ void BitcoinGUI::setModel(ClientModel *model) setAddress(model->getAddress()); connect(model, SIGNAL(addressChanged(QString)), this, SLOT(setAddress(QString))); + + /* Report errors from network/worker thread */ + connect(model, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); } void BitcoinGUI::createTrayIcon() @@ -226,6 +230,7 @@ QWidget *BitcoinGUI::createTabs() void BitcoinGUI::sendcoinsClicked() { SendCoinsDialog dlg; + dlg.setModel(model); dlg.exec(); } @@ -296,3 +301,11 @@ void BitcoinGUI::setNumTransactions(int count) { labelTransactions->setText(QLocale::system().toString(count)+" "+tr("transaction(s)", "", count)); } + +void BitcoinGUI::error(const QString &title, const QString &message) +{ + /* Report errors from network/worker thread */ + QMessageBox::critical(this, title, + message, + QMessageBox::Ok, QMessageBox::Ok); +} diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp index e355676cd0..be8b0b4183 100644 --- a/gui/src/clientmodel.cpp +++ b/gui/src/clientmodel.cpp @@ -51,6 +51,11 @@ int ClientModel::getNumTransactions() return numTransactions; } +qint64 ClientModel::getTransactionFee() +{ + return nTransactionFee; +} + void ClientModel::update() { emit balanceChanged(getBalance()); @@ -59,3 +64,51 @@ void ClientModel::update() emit numBlocksChanged(getNumBlocks()); emit numTransactionsChanged(getNumTransactions()); } + +ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payAmount) +{ + uint160 hash160 = 0; + bool valid = false; + + if(!AddressToHash160(payTo.toUtf8().constData(), hash160)) + { + return InvalidAddress; + } + + if(payAmount <= 0) + { + return InvalidAmount; + } + + if(payAmount > getBalance()) + { + return AmountExceedsBalance; + } + + if((payAmount + nTransactionFee) > getBalance()) + { + return AmountWithFeeExceedsBalance; + } + + CRITICAL_BLOCK(cs_main) + { + // Send to bitcoin address + CWalletTx wtx; + CScript scriptPubKey; + scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; + + std::string strError = SendMoney(scriptPubKey, payAmount, wtx, true); + if (strError == "") + return OK; + else if (strError == "ABORTED") + return Aborted; + else + { + emit error(tr("Sending..."), QString::fromStdString(strError)); + return MiscError; + } + } + + return OK; +} + diff --git a/gui/src/sendcoinsdialog.cpp b/gui/src/sendcoinsdialog.cpp index 123b930a20..398e236ee5 100644 --- a/gui/src/sendcoinsdialog.cpp +++ b/gui/src/sendcoinsdialog.cpp @@ -1,5 +1,6 @@ #include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" +#include "clientmodel.h" #include "addressbookdialog.h" #include "bitcoinaddressvalidator.h" @@ -15,7 +16,8 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : QDialog(parent), - ui(new Ui::SendCoinsDialog) + ui(new Ui::SendCoinsDialog), + model(0) { ui->setupUi(this); @@ -35,6 +37,11 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : } } +void SendCoinsDialog::setModel(ClientModel *model) +{ + this->model = model; +} + SendCoinsDialog::~SendCoinsDialog() { delete ui; @@ -42,35 +49,50 @@ SendCoinsDialog::~SendCoinsDialog() void SendCoinsDialog::on_sendButton_clicked() { - QByteArray payTo = ui->payTo->text().toUtf8(); - uint160 payToHash = 0; - int64 payAmount = 0.0; - bool valid = false; + bool valid; + QString payAmount = ui->payAmount->text(); + qint64 payAmountParsed; - if(!AddressToHash160(payTo.constData(), payToHash)) + valid = ParseMoney(payAmount.toStdString(), payAmountParsed); + + if(!valid) { - QMessageBox::warning(this, tr("Warning"), - tr("The recepient address is not valid, please recheck."), - QMessageBox::Ok, - QMessageBox::Ok); - ui->payTo->setFocus(); + QMessageBox::warning(this, tr("Send Coins"), + tr("The amount to pay must be a valid number."), + QMessageBox::Ok, QMessageBox::Ok); return; } - valid = ParseMoney(ui->payAmount->text().toStdString(), payAmount); - if(!valid || payAmount <= 0) + switch(model->sendCoins(ui->payTo->text(), payAmountParsed)) { - QMessageBox::warning(this, tr("Warning"), - tr("The amount to pay must be a valid number larger than 0."), - QMessageBox::Ok, - QMessageBox::Ok); + case ClientModel::InvalidAddress: + QMessageBox::warning(this, tr("Send Coins"), + tr("The recepient address is not valid, please recheck."), + QMessageBox::Ok, QMessageBox::Ok); + ui->payTo->setFocus(); + break; + case ClientModel::InvalidAmount: + QMessageBox::warning(this, tr("Send Coins"), + tr("The amount to pay must be larger than 0."), + QMessageBox::Ok, QMessageBox::Ok); ui->payAmount->setFocus(); - return; + break; + case ClientModel::AmountExceedsBalance: + QMessageBox::warning(this, tr("Send Coins"), + tr("Amount exceeds your balance"), + QMessageBox::Ok, QMessageBox::Ok); + ui->payAmount->setFocus(); + break; + case ClientModel::AmountWithFeeExceedsBalance: + QMessageBox::warning(this, tr("Send Coins"), + tr("Total exceeds your balance when the %1 transaction fee is included"). + arg(QString::fromStdString(FormatMoney(model->getTransactionFee()))), + QMessageBox::Ok, QMessageBox::Ok); + ui->payAmount->setFocus(); + break; } - qDebug() << "Pay " << payAmount; - /* TODO: send command to core, once this succeeds do accept() */ - accept(); + //accept(); } void SendCoinsDialog::on_pasteButton_clicked() -- cgit v1.2.3 From 92f20d53fb946a6d87e0b3aff3fd258ea31338db Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 31 May 2011 22:24:53 +0200 Subject: implement options model, show current options in options dialog --- bitcoin.pro | 6 +++-- gui/include/clientmodel.h | 5 +++++ gui/include/mainoptionspage.h | 21 ++++++++++++++++- gui/include/optionsdialog.h | 10 ++++++++- gui/include/optionsmodel.h | 34 ++++++++++++++++++++++++++++ gui/src/bitcoingui.cpp | 1 + gui/src/clientmodel.cpp | 9 +++++++- gui/src/mainoptionspage.cpp | 33 +++++++++++++++++++-------- gui/src/optionsdialog.cpp | 23 +++++++++++++++---- gui/src/optionsmodel.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++ gui/src/sendcoinsdialog.cpp | 5 +++-- 11 files changed, 179 insertions(+), 20 deletions(-) create mode 100644 gui/include/optionsmodel.h create mode 100644 gui/src/optionsmodel.cpp diff --git a/bitcoin.pro b/bitcoin.pro index 49574d60f5..53835632ad 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -60,7 +60,8 @@ HEADERS += gui/include/bitcoingui.h \ gui/include/clientmodel.h \ gui/include/guiutil.h \ gui/include/transactionrecord.h \ - gui/include/guiconstants.h + gui/include/guiconstants.h \ + gui/include/optionsmodel.h SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/transactiontablemodel.cpp \ gui/src/addresstablemodel.cpp \ @@ -86,7 +87,8 @@ SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ json/src/json_spirit_reader.cpp \ gui/src/clientmodel.cpp \ gui/src/guiutil.cpp \ - gui/src/transactionrecord.cpp + gui/src/transactionrecord.cpp \ + gui/src/optionsmodel.cpp RESOURCES += \ gui/bitcoin.qrc diff --git a/gui/include/clientmodel.h b/gui/include/clientmodel.h index 828c80f8db..44f1c0ab64 100644 --- a/gui/include/clientmodel.h +++ b/gui/include/clientmodel.h @@ -2,6 +2,7 @@ #define CLIENTMODEL_H #include +class OptionsModel; class ClientModel : public QObject { @@ -20,6 +21,8 @@ public: MiscError }; + OptionsModel *getOptionsModel(); + qint64 getBalance(); QString getAddress(); int getNumConnections(); @@ -29,6 +32,8 @@ public: qint64 getTransactionFee(); StatusCode sendCoins(const QString &payTo, qint64 payAmount); +private: + OptionsModel *options_model; signals: void balanceChanged(qint64 balance); diff --git a/gui/include/mainoptionspage.h b/gui/include/mainoptionspage.h index de2ef9fcd0..4ef5e60a25 100644 --- a/gui/include/mainoptionspage.h +++ b/gui/include/mainoptionspage.h @@ -3,11 +3,30 @@ #include +QT_BEGIN_NAMESPACE +class QDataWidgetMapper; +class QCheckBox; +class QLineEdit; +QT_END_NAMESPACE + +class OptionsModel; + class MainOptionsPage : public QWidget { Q_OBJECT public: - explicit MainOptionsPage(QWidget *parent = 0); + explicit MainOptionsPage(QWidget *parent=0); + + void setMapper(QDataWidgetMapper *mapper); +private: + QCheckBox *bitcoin_at_startup; + QCheckBox *minimize_to_tray; + QCheckBox *map_port_upnp; + QCheckBox *minimize_on_close; + QCheckBox *connect_socks4; + QLineEdit *proxy_ip; + QLineEdit *proxy_port; + QLineEdit *fee_edit; signals: diff --git a/gui/include/optionsdialog.h b/gui/include/optionsdialog.h index 501c82e9f3..c064b0a93c 100644 --- a/gui/include/optionsdialog.h +++ b/gui/include/optionsdialog.h @@ -7,13 +7,18 @@ QT_BEGIN_NAMESPACE class QStackedWidget; class QListWidget; class QListWidgetItem; +class QDataWidgetMapper; QT_END_NAMESPACE +class OptionsModel; +class MainOptionsPage; class OptionsDialog : public QDialog { Q_OBJECT public: - explicit OptionsDialog(QWidget *parent = 0); + explicit OptionsDialog(QWidget *parent=0); + + void setModel(OptionsModel *model); signals: @@ -22,6 +27,9 @@ public slots: private: QListWidget *contents_widget; QStackedWidget *pages_widget; + MainOptionsPage *main_options_page; + OptionsModel *model; + QDataWidgetMapper *mapper; void setupMainPage(); }; diff --git a/gui/include/optionsmodel.h b/gui/include/optionsmodel.h new file mode 100644 index 0000000000..4dd21c7f07 --- /dev/null +++ b/gui/include/optionsmodel.h @@ -0,0 +1,34 @@ +#ifndef OPTIONSMODEL_H +#define OPTIONSMODEL_H + +#include + +class OptionsModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit OptionsModel(QObject *parent = 0); + + enum OptionID { + StartAtStartup, + MinimizeToTray, + MapPortUPnP, + MinimizeOnClose, + ConnectSOCKS4, + ProxyIP, + ProxyPort, + Fee, + OptionIDRowCount + }; + + int rowCount(const QModelIndex & parent = QModelIndex()) const; + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); + +signals: + +public slots: + +}; + +#endif // OPTIONSMODEL_H diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index 66a3edd3ad..66891222a8 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -256,6 +256,7 @@ void BitcoinGUI::receivingAddressesClicked() void BitcoinGUI::optionsClicked() { OptionsDialog dlg; + dlg.setModel(model->getOptionsModel()); dlg.exec(); } diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp index be8b0b4183..35824720df 100644 --- a/gui/src/clientmodel.cpp +++ b/gui/src/clientmodel.cpp @@ -1,11 +1,12 @@ #include "clientmodel.h" #include "main.h" #include "guiconstants.h" +#include "optionsmodel.h" #include ClientModel::ClientModel(QObject *parent) : - QObject(parent) + QObject(parent), options_model(0) { /* Until we build signal notifications into the bitcoin core, simply update everything using a timer. @@ -13,6 +14,8 @@ ClientModel::ClientModel(QObject *parent) : QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(MODEL_UPDATE_DELAY); + + options_model = new OptionsModel(this); } qint64 ClientModel::getBalance() @@ -112,3 +115,7 @@ ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payA return OK; } +OptionsModel *ClientModel::getOptionsModel() +{ + return options_model; +} diff --git a/gui/src/mainoptionspage.cpp b/gui/src/mainoptionspage.cpp index 021e1e470f..3d69baeda7 100644 --- a/gui/src/mainoptionspage.cpp +++ b/gui/src/mainoptionspage.cpp @@ -1,42 +1,45 @@ #include "mainoptionspage.h" +#include "optionsmodel.h" #include #include #include #include #include +#include +#include MainOptionsPage::MainOptionsPage(QWidget *parent): QWidget(parent) { QVBoxLayout *layout = new QVBoxLayout(); - QCheckBox *bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup")); + bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup")); layout->addWidget(bitcoin_at_startup); - QCheckBox *minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar")); + minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar")); layout->addWidget(minimize_to_tray); - QCheckBox *map_port_upnp = new QCheckBox(tr("Map port using &UPnP")); + map_port_upnp = new QCheckBox(tr("Map port using &UPnP")); layout->addWidget(map_port_upnp); - QCheckBox *minimize_on_close = new QCheckBox(tr("M&inimize on close")); + minimize_on_close = new QCheckBox(tr("M&inimize on close")); layout->addWidget(minimize_on_close); - QCheckBox *connect_socks4 = new QCheckBox(tr("&Connect through socks4 proxy:")); + connect_socks4 = new QCheckBox(tr("&Connect through socks4 proxy:")); layout->addWidget(connect_socks4); QHBoxLayout *proxy_hbox = new QHBoxLayout(); proxy_hbox->addSpacing(18); QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP: ")); proxy_hbox->addWidget(proxy_ip_label); - QLineEdit *proxy_ip = new QLineEdit(); + proxy_ip = new QLineEdit(); proxy_ip->setMaximumWidth(140); proxy_ip_label->setBuddy(proxy_ip); proxy_hbox->addWidget(proxy_ip); QLabel *proxy_port_label = new QLabel(tr("&Port: ")); proxy_hbox->addWidget(proxy_port_label); - QLineEdit *proxy_port = new QLineEdit(); + proxy_port = new QLineEdit(); proxy_port->setMaximumWidth(55); proxy_port_label->setBuddy(proxy_port); proxy_hbox->addWidget(proxy_port); @@ -51,7 +54,7 @@ MainOptionsPage::MainOptionsPage(QWidget *parent): fee_hbox->addSpacing(18); QLabel *fee_label = new QLabel(tr("Pay transaction &fee")); fee_hbox->addWidget(fee_label); - QLineEdit *fee_edit = new QLineEdit(); + fee_edit = new QLineEdit(); fee_edit->setMaximumWidth(70); fee_label->setBuddy(fee_edit); fee_hbox->addWidget(fee_edit); @@ -59,9 +62,21 @@ MainOptionsPage::MainOptionsPage(QWidget *parent): layout->addLayout(fee_hbox); - layout->addStretch(1); /* Extra space at bottom */ setLayout(layout); } +void MainOptionsPage::setMapper(QDataWidgetMapper *mapper) +{ + /* Map model to widgets */ + mapper->addMapping(bitcoin_at_startup, OptionsModel::StartAtStartup); + mapper->addMapping(minimize_to_tray, OptionsModel::MinimizeToTray); + mapper->addMapping(map_port_upnp, OptionsModel::MapPortUPnP); + mapper->addMapping(minimize_on_close, OptionsModel::MinimizeOnClose); + mapper->addMapping(connect_socks4, OptionsModel::ConnectSOCKS4); + mapper->addMapping(proxy_ip, OptionsModel::ProxyIP); + mapper->addMapping(proxy_port, OptionsModel::ProxyPort); + mapper->addMapping(fee_edit, OptionsModel::Fee); +} + diff --git a/gui/src/optionsdialog.cpp b/gui/src/optionsdialog.cpp index 70dd86323e..e1f3d67dd7 100644 --- a/gui/src/optionsdialog.cpp +++ b/gui/src/optionsdialog.cpp @@ -1,4 +1,5 @@ #include "optionsdialog.h" +#include "optionsmodel.h" #include "mainoptionspage.h" #include @@ -6,9 +7,11 @@ #include #include #include +#include -OptionsDialog::OptionsDialog(QWidget *parent) : - QDialog(parent), contents_widget(0), pages_widget(0) +OptionsDialog::OptionsDialog(QWidget *parent): + QDialog(parent), contents_widget(0), pages_widget(0), + main_options_page(0), model(0) { contents_widget = new QListWidget(); contents_widget->setMaximumWidth(128); @@ -18,7 +21,8 @@ OptionsDialog::OptionsDialog(QWidget *parent) : QListWidgetItem *item_main = new QListWidgetItem(tr("Main")); contents_widget->addItem(item_main); - pages_widget->addWidget(new MainOptionsPage(this)); + main_options_page = new MainOptionsPage(this); + pages_widget->addWidget(main_options_page); contents_widget->setCurrentRow(0); @@ -40,11 +44,22 @@ OptionsDialog::OptionsDialog(QWidget *parent) : layout->addLayout(buttons); - setLayout(layout); setWindowTitle(tr("Options")); + mapper = new QDataWidgetMapper(); + mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); + mapper->setOrientation(Qt::Vertical); +} + +void OptionsDialog::setModel(OptionsModel *model) +{ + this->model = model; + + mapper->setModel(model); + main_options_page->setMapper(mapper); + mapper->toFirst(); } void OptionsDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) diff --git a/gui/src/optionsmodel.cpp b/gui/src/optionsmodel.cpp new file mode 100644 index 0000000000..25c7366c13 --- /dev/null +++ b/gui/src/optionsmodel.cpp @@ -0,0 +1,52 @@ +#include "optionsmodel.h" +#include "main.h" + +#include + +OptionsModel::OptionsModel(QObject *parent) : + QAbstractListModel(parent) +{ +} + +int OptionsModel::rowCount(const QModelIndex & parent) const +{ + return OptionIDRowCount; +} + +QVariant OptionsModel::data(const QModelIndex & index, int role) const +{ + qDebug() << "OptionsModel::data" << " " << index.row() << " " << role; + if(role == Qt::EditRole) + { + /* Delegate to specific column handlers */ + switch(index.row()) + { + case StartAtStartup: + return QVariant(); + case MinimizeToTray: + return QVariant(fMinimizeToTray); + case MapPortUPnP: + return QVariant(fUseUPnP); + case MinimizeOnClose: + return QVariant(fMinimizeOnClose); + case ConnectSOCKS4: + return QVariant(fUseProxy); + case ProxyIP: + return QVariant(QString::fromStdString(addrProxy.ToStringIP())); + case ProxyPort: + return QVariant(QString::fromStdString(addrProxy.ToStringPort())); + case Fee: + return QVariant(QString::fromStdString(FormatMoney(nTransactionFee))); + default: + return QVariant(); + } + } + return QVariant(); +} + +bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role) +{ + qDebug() << "OptionsModel::setData" << " " << index.row() << "=" << value; + emit dataChanged(index, index); + return true; +} diff --git a/gui/src/sendcoinsdialog.cpp b/gui/src/sendcoinsdialog.cpp index 398e236ee5..9040d21fb3 100644 --- a/gui/src/sendcoinsdialog.cpp +++ b/gui/src/sendcoinsdialog.cpp @@ -90,9 +90,10 @@ void SendCoinsDialog::on_sendButton_clicked() QMessageBox::Ok, QMessageBox::Ok); ui->payAmount->setFocus(); break; + case ClientModel::OK: + accept(); + break; } - /* TODO: send command to core, once this succeeds do accept() */ - //accept(); } void SendCoinsDialog::on_pasteButton_clicked() -- cgit v1.2.3 From 968d55aafa20b1da7b245f3116370e3fa6c17e5c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 1 Jun 2011 09:34:12 +0200 Subject: move getTransactionFee to OptionsModel --- gui/include/clientmodel.h | 2 -- gui/include/optionsmodel.h | 3 +++ gui/src/clientmodel.cpp | 5 ----- gui/src/optionsmodel.cpp | 5 +++++ gui/src/sendcoinsdialog.cpp | 3 ++- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/gui/include/clientmodel.h b/gui/include/clientmodel.h index 44f1c0ab64..49b3460978 100644 --- a/gui/include/clientmodel.h +++ b/gui/include/clientmodel.h @@ -29,8 +29,6 @@ public: int getNumBlocks(); int getNumTransactions(); - qint64 getTransactionFee(); - StatusCode sendCoins(const QString &payTo, qint64 payAmount); private: OptionsModel *options_model; diff --git a/gui/include/optionsmodel.h b/gui/include/optionsmodel.h index 4dd21c7f07..3e0bcc1ddd 100644 --- a/gui/include/optionsmodel.h +++ b/gui/include/optionsmodel.h @@ -3,6 +3,7 @@ #include +/* Configuration data structure for bitcoin client */ class OptionsModel : public QAbstractListModel { Q_OBJECT @@ -25,6 +26,8 @@ public: QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); + /* Explicit getters */ + qint64 getTransactionFee(); signals: public slots: diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp index 35824720df..641c515ef9 100644 --- a/gui/src/clientmodel.cpp +++ b/gui/src/clientmodel.cpp @@ -54,11 +54,6 @@ int ClientModel::getNumTransactions() return numTransactions; } -qint64 ClientModel::getTransactionFee() -{ - return nTransactionFee; -} - void ClientModel::update() { emit balanceChanged(getBalance()); diff --git a/gui/src/optionsmodel.cpp b/gui/src/optionsmodel.cpp index 25c7366c13..e3287f3916 100644 --- a/gui/src/optionsmodel.cpp +++ b/gui/src/optionsmodel.cpp @@ -50,3 +50,8 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in emit dataChanged(index, index); return true; } + +qint64 OptionsModel::getTransactionFee() +{ + return nTransactionFee; +} diff --git a/gui/src/sendcoinsdialog.cpp b/gui/src/sendcoinsdialog.cpp index 9040d21fb3..a6ab601592 100644 --- a/gui/src/sendcoinsdialog.cpp +++ b/gui/src/sendcoinsdialog.cpp @@ -4,6 +4,7 @@ #include "addressbookdialog.h" #include "bitcoinaddressvalidator.h" +#include "optionsmodel.h" #include #include @@ -86,7 +87,7 @@ void SendCoinsDialog::on_sendButton_clicked() case ClientModel::AmountWithFeeExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), tr("Total exceeds your balance when the %1 transaction fee is included"). - arg(QString::fromStdString(FormatMoney(model->getTransactionFee()))), + arg(QString::fromStdString(FormatMoney(model->getOptionsModel()->getTransactionFee()))), QMessageBox::Ok, QMessageBox::Ok); ui->payAmount->setFocus(); break; -- cgit v1.2.3 From c6dd35f03dd58a5712d38a3ce535723cdf0ffb30 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 1 Jun 2011 09:33:48 +0200 Subject: Apply button --- gui/include/optionsdialog.h | 7 +++++++ gui/src/optionsdialog.cpp | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/gui/include/optionsdialog.h b/gui/include/optionsdialog.h index c064b0a93c..ff8542d41a 100644 --- a/gui/include/optionsdialog.h +++ b/gui/include/optionsdialog.h @@ -8,6 +8,7 @@ class QStackedWidget; class QListWidget; class QListWidgetItem; class QDataWidgetMapper; +class QPushButton; QT_END_NAMESPACE class OptionsModel; class MainOptionsPage; @@ -24,12 +25,18 @@ signals: public slots: void changePage(QListWidgetItem *current, QListWidgetItem *previous); +private slots: + void okClicked(); + void cancelClicked(); + void applyClicked(); + void enableApply(); private: QListWidget *contents_widget; QStackedWidget *pages_widget; MainOptionsPage *main_options_page; OptionsModel *model; QDataWidgetMapper *mapper; + QPushButton *apply_button; void setupMainPage(); }; diff --git a/gui/src/optionsdialog.cpp b/gui/src/optionsdialog.cpp index e1f3d67dd7..4d0493a26e 100644 --- a/gui/src/optionsdialog.cpp +++ b/gui/src/optionsdialog.cpp @@ -8,6 +8,7 @@ #include #include #include +#include OptionsDialog::OptionsDialog(QWidget *parent): QDialog(parent), contents_widget(0), pages_widget(0), @@ -39,7 +40,8 @@ OptionsDialog::OptionsDialog(QWidget *parent): buttons->addWidget(ok_button); QPushButton *cancel_button = new QPushButton(tr("Cancel")); buttons->addWidget(cancel_button); - QPushButton *apply_button = new QPushButton(tr("Apply")); + apply_button = new QPushButton(tr("Apply")); + apply_button->setEnabled(false); buttons->addWidget(apply_button); layout->addLayout(buttons); @@ -47,9 +49,16 @@ OptionsDialog::OptionsDialog(QWidget *parent): setLayout(layout); setWindowTitle(tr("Options")); + /* Widget-to-option mapper */ mapper = new QDataWidgetMapper(); mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); mapper->setOrientation(Qt::Vertical); + connect(mapper->itemDelegate(), SIGNAL(commitData(QWidget*)), this, SLOT(enableApply())); + + /* Event bindings */ + connect(ok_button, SIGNAL(clicked()), this, SLOT(okClicked())); + connect(cancel_button, SIGNAL(clicked()), this, SLOT(cancelClicked())); + connect(apply_button, SIGNAL(clicked()), this, SLOT(applyClicked())); } void OptionsDialog::setModel(OptionsModel *model) @@ -70,3 +79,25 @@ void OptionsDialog::changePage(QListWidgetItem *current, QListWidgetItem *previo pages_widget->setCurrentIndex(contents_widget->row(current)); } } + +void OptionsDialog::okClicked() +{ + mapper->submit(); + accept(); +} + +void OptionsDialog::cancelClicked() +{ + reject(); +} + +void OptionsDialog::applyClicked() +{ + mapper->submit(); + apply_button->setEnabled(false); +} + +void OptionsDialog::enableApply() +{ + apply_button->setEnabled(true); +} -- cgit v1.2.3 From c3e0734dbc3c9ace13fbf4188b52aff6e56832b8 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 1 Jun 2011 14:40:06 +0200 Subject: implement options model / improve view with validators --- bitcoin.pro | 8 +-- gui/include/mainoptionspage.h | 37 ----------- gui/include/monitoreddatamapper.h | 32 ++++++++++ gui/include/optionsdialog.h | 5 +- gui/include/optionsmodel.h | 4 +- gui/src/mainoptionspage.cpp | 82 ------------------------ gui/src/monitoreddatamapper.cpp | 39 ++++++++++++ gui/src/optionsdialog.cpp | 131 ++++++++++++++++++++++++++++++++++++-- gui/src/optionsmodel.cpp | 73 +++++++++++++++++++-- 9 files changed, 276 insertions(+), 135 deletions(-) delete mode 100644 gui/include/mainoptionspage.h create mode 100644 gui/include/monitoreddatamapper.h delete mode 100644 gui/src/mainoptionspage.cpp create mode 100644 gui/src/monitoreddatamapper.cpp diff --git a/bitcoin.pro b/bitcoin.pro index 53835632ad..d34488dd48 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -15,7 +15,6 @@ HEADERS += gui/include/bitcoingui.h \ gui/include/transactiontablemodel.h \ gui/include/addresstablemodel.h \ gui/include/optionsdialog.h \ - gui/include/mainoptionspage.h \ gui/include/sendcoinsdialog.h \ gui/include/addressbookdialog.h \ gui/include/aboutdialog.h \ @@ -61,12 +60,12 @@ HEADERS += gui/include/bitcoingui.h \ gui/include/guiutil.h \ gui/include/transactionrecord.h \ gui/include/guiconstants.h \ - gui/include/optionsmodel.h + gui/include/optionsmodel.h \ + gui/include/monitoreddatamapper.h SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/transactiontablemodel.cpp \ gui/src/addresstablemodel.cpp \ gui/src/optionsdialog.cpp \ - gui/src/mainoptionspage.cpp \ gui/src/sendcoinsdialog.cpp \ gui/src/addressbookdialog.cpp \ gui/src/aboutdialog.cpp \ @@ -88,7 +87,8 @@ SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/clientmodel.cpp \ gui/src/guiutil.cpp \ gui/src/transactionrecord.cpp \ - gui/src/optionsmodel.cpp + gui/src/optionsmodel.cpp \ + gui/src/monitoreddatamapper.cpp RESOURCES += \ gui/bitcoin.qrc diff --git a/gui/include/mainoptionspage.h b/gui/include/mainoptionspage.h deleted file mode 100644 index 4ef5e60a25..0000000000 --- a/gui/include/mainoptionspage.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef MAINOPTIONSPAGE_H -#define MAINOPTIONSPAGE_H - -#include - -QT_BEGIN_NAMESPACE -class QDataWidgetMapper; -class QCheckBox; -class QLineEdit; -QT_END_NAMESPACE - -class OptionsModel; - -class MainOptionsPage : public QWidget -{ - Q_OBJECT -public: - explicit MainOptionsPage(QWidget *parent=0); - - void setMapper(QDataWidgetMapper *mapper); -private: - QCheckBox *bitcoin_at_startup; - QCheckBox *minimize_to_tray; - QCheckBox *map_port_upnp; - QCheckBox *minimize_on_close; - QCheckBox *connect_socks4; - QLineEdit *proxy_ip; - QLineEdit *proxy_port; - QLineEdit *fee_edit; - -signals: - -public slots: - -}; - -#endif // MAINOPTIONSPAGE_H diff --git a/gui/include/monitoreddatamapper.h b/gui/include/monitoreddatamapper.h new file mode 100644 index 0000000000..4dd2d1a86a --- /dev/null +++ b/gui/include/monitoreddatamapper.h @@ -0,0 +1,32 @@ +#ifndef MONITOREDDATAMAPPER_H +#define MONITOREDDATAMAPPER_H + +#include + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +/* Data <-> Widget mapper that watches for changes, + to be able to notify when 'dirty' (for example, to + enable a commit/apply button). + */ +class MonitoredDataMapper : public QDataWidgetMapper +{ + Q_OBJECT +public: + explicit MonitoredDataMapper(QObject *parent=0); + + void addMapping(QWidget *widget, int section); + void addMapping(QWidget *widget, int section, const QByteArray &propertyName); +private: + void addChangeMonitor(QWidget *widget); + +signals: + void viewModified(); + +}; + + + +#endif // MONITOREDDATAMAPPER_H diff --git a/gui/include/optionsdialog.h b/gui/include/optionsdialog.h index ff8542d41a..07e85297d5 100644 --- a/gui/include/optionsdialog.h +++ b/gui/include/optionsdialog.h @@ -7,11 +7,11 @@ QT_BEGIN_NAMESPACE class QStackedWidget; class QListWidget; class QListWidgetItem; -class QDataWidgetMapper; class QPushButton; QT_END_NAMESPACE class OptionsModel; class MainOptionsPage; +class MonitoredDataMapper; class OptionsDialog : public QDialog { @@ -30,12 +30,13 @@ private slots: void cancelClicked(); void applyClicked(); void enableApply(); + void disableApply(); private: QListWidget *contents_widget; QStackedWidget *pages_widget; MainOptionsPage *main_options_page; OptionsModel *model; - QDataWidgetMapper *mapper; + MonitoredDataMapper *mapper; QPushButton *apply_button; void setupMainPage(); diff --git a/gui/include/optionsmodel.h b/gui/include/optionsmodel.h index 3e0bcc1ddd..4fb6d25145 100644 --- a/gui/include/optionsmodel.h +++ b/gui/include/optionsmodel.h @@ -3,7 +3,7 @@ #include -/* Configuration data structure for bitcoin client */ +/* Interface from QT to configuration data structure for bitcoin client */ class OptionsModel : public QAbstractListModel { Q_OBJECT @@ -28,6 +28,8 @@ public: /* Explicit getters */ qint64 getTransactionFee(); + bool getMinimizeToTray(); + bool getMinimizeOnClose(); signals: public slots: diff --git a/gui/src/mainoptionspage.cpp b/gui/src/mainoptionspage.cpp deleted file mode 100644 index 3d69baeda7..0000000000 --- a/gui/src/mainoptionspage.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "mainoptionspage.h" -#include "optionsmodel.h" - -#include -#include -#include -#include -#include -#include -#include - -MainOptionsPage::MainOptionsPage(QWidget *parent): - QWidget(parent) -{ - QVBoxLayout *layout = new QVBoxLayout(); - - bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup")); - layout->addWidget(bitcoin_at_startup); - - minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar")); - layout->addWidget(minimize_to_tray); - - map_port_upnp = new QCheckBox(tr("Map port using &UPnP")); - layout->addWidget(map_port_upnp); - - minimize_on_close = new QCheckBox(tr("M&inimize on close")); - layout->addWidget(minimize_on_close); - - connect_socks4 = new QCheckBox(tr("&Connect through socks4 proxy:")); - layout->addWidget(connect_socks4); - - QHBoxLayout *proxy_hbox = new QHBoxLayout(); - proxy_hbox->addSpacing(18); - QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP: ")); - proxy_hbox->addWidget(proxy_ip_label); - proxy_ip = new QLineEdit(); - proxy_ip->setMaximumWidth(140); - proxy_ip_label->setBuddy(proxy_ip); - proxy_hbox->addWidget(proxy_ip); - QLabel *proxy_port_label = new QLabel(tr("&Port: ")); - proxy_hbox->addWidget(proxy_port_label); - proxy_port = new QLineEdit(); - proxy_port->setMaximumWidth(55); - proxy_port_label->setBuddy(proxy_port); - proxy_hbox->addWidget(proxy_port); - proxy_hbox->addStretch(1); - - layout->addLayout(proxy_hbox); - QLabel *fee_help = new QLabel(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended.")); - fee_help->setWordWrap(true); - layout->addWidget(fee_help); - - QHBoxLayout *fee_hbox = new QHBoxLayout(); - fee_hbox->addSpacing(18); - QLabel *fee_label = new QLabel(tr("Pay transaction &fee")); - fee_hbox->addWidget(fee_label); - fee_edit = new QLineEdit(); - fee_edit->setMaximumWidth(70); - fee_label->setBuddy(fee_edit); - fee_hbox->addWidget(fee_edit); - fee_hbox->addStretch(1); - - layout->addLayout(fee_hbox); - - layout->addStretch(1); /* Extra space at bottom */ - - setLayout(layout); -} - -void MainOptionsPage::setMapper(QDataWidgetMapper *mapper) -{ - /* Map model to widgets */ - mapper->addMapping(bitcoin_at_startup, OptionsModel::StartAtStartup); - mapper->addMapping(minimize_to_tray, OptionsModel::MinimizeToTray); - mapper->addMapping(map_port_upnp, OptionsModel::MapPortUPnP); - mapper->addMapping(minimize_on_close, OptionsModel::MinimizeOnClose); - mapper->addMapping(connect_socks4, OptionsModel::ConnectSOCKS4); - mapper->addMapping(proxy_ip, OptionsModel::ProxyIP); - mapper->addMapping(proxy_port, OptionsModel::ProxyPort); - mapper->addMapping(fee_edit, OptionsModel::Fee); -} - diff --git a/gui/src/monitoreddatamapper.cpp b/gui/src/monitoreddatamapper.cpp new file mode 100644 index 0000000000..e70aa7ebf6 --- /dev/null +++ b/gui/src/monitoreddatamapper.cpp @@ -0,0 +1,39 @@ +#include "monitoreddatamapper.h" + +#include +#include +#include +#include + + +MonitoredDataMapper::MonitoredDataMapper(QObject *parent) : + QDataWidgetMapper(parent) +{ +} + + +void MonitoredDataMapper::addMapping(QWidget *widget, int section) +{ + QDataWidgetMapper::addMapping(widget, section); + addChangeMonitor(widget); +} + +void MonitoredDataMapper::addMapping(QWidget *widget, int section, const QByteArray &propertyName) +{ + QDataWidgetMapper::addMapping(widget, section, propertyName); + addChangeMonitor(widget); +} + +void MonitoredDataMapper::addChangeMonitor(QWidget *widget) +{ + /* Watch user property of widget for changes, and connect + the signal to our viewModified signal. + */ + QMetaProperty prop = widget->metaObject()->userProperty(); + int signal = prop.notifySignalIndex(); + int method = this->metaObject()->indexOfMethod("viewModified()"); + if(signal != -1 && method != -1) + { + QMetaObject::connect(widget, signal, this, method); + } +} diff --git a/gui/src/optionsdialog.cpp b/gui/src/optionsdialog.cpp index 4d0493a26e..8e7f403a7e 100644 --- a/gui/src/optionsdialog.cpp +++ b/gui/src/optionsdialog.cpp @@ -1,14 +1,42 @@ #include "optionsdialog.h" #include "optionsmodel.h" -#include "mainoptionspage.h" +#include "monitoreddatamapper.h" #include #include #include #include #include -#include -#include + +#include +#include +#include +#include +#include +#include + +/* First (currently only) page of options */ +class MainOptionsPage : public QWidget +{ +public: + explicit MainOptionsPage(QWidget *parent=0); + + void setMapper(MonitoredDataMapper *mapper); +private: + QCheckBox *bitcoin_at_startup; + QCheckBox *minimize_to_tray; + QCheckBox *map_port_upnp; + QCheckBox *minimize_on_close; + QCheckBox *connect_socks4; + QLineEdit *proxy_ip; + QLineEdit *proxy_port; + QLineEdit *fee_edit; + +signals: + +public slots: + +}; OptionsDialog::OptionsDialog(QWidget *parent): QDialog(parent), contents_widget(0), pages_widget(0), @@ -50,10 +78,13 @@ OptionsDialog::OptionsDialog(QWidget *parent): setWindowTitle(tr("Options")); /* Widget-to-option mapper */ - mapper = new QDataWidgetMapper(); + mapper = new MonitoredDataMapper(this); mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); mapper->setOrientation(Qt::Vertical); - connect(mapper->itemDelegate(), SIGNAL(commitData(QWidget*)), this, SLOT(enableApply())); + /* enable apply button when data modified */ + connect(mapper, SIGNAL(viewModified()), this, SLOT(enableApply())); + /* disable apply button when new data loaded */ + connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApply())); /* Event bindings */ connect(ok_button, SIGNAL(clicked()), this, SLOT(okClicked())); @@ -101,3 +132,93 @@ void OptionsDialog::enableApply() { apply_button->setEnabled(true); } + +void OptionsDialog::disableApply() +{ + apply_button->setEnabled(false); +} + +MainOptionsPage::MainOptionsPage(QWidget *parent): + QWidget(parent) +{ + QVBoxLayout *layout = new QVBoxLayout(); + + bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup")); + layout->addWidget(bitcoin_at_startup); + + minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar")); + layout->addWidget(minimize_to_tray); + + map_port_upnp = new QCheckBox(tr("Map port using &UPnP")); + layout->addWidget(map_port_upnp); + + minimize_on_close = new QCheckBox(tr("M&inimize on close")); + layout->addWidget(minimize_on_close); + + connect_socks4 = new QCheckBox(tr("&Connect through socks4 proxy:")); + layout->addWidget(connect_socks4); + + QHBoxLayout *proxy_hbox = new QHBoxLayout(); + proxy_hbox->addSpacing(18); + QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP: ")); + proxy_hbox->addWidget(proxy_ip_label); + proxy_ip = new QLineEdit(); + proxy_ip->setMaximumWidth(140); + proxy_ip->setEnabled(false); + proxy_ip->setValidator(new QRegExpValidator(QRegExp("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"), this)); + proxy_ip_label->setBuddy(proxy_ip); + proxy_hbox->addWidget(proxy_ip); + QLabel *proxy_port_label = new QLabel(tr("&Port: ")); + proxy_hbox->addWidget(proxy_port_label); + proxy_port = new QLineEdit(); + proxy_port->setMaximumWidth(55); + proxy_port->setValidator(new QIntValidator(0, 65535, this)); + proxy_port->setEnabled(false); + proxy_port_label->setBuddy(proxy_port); + proxy_hbox->addWidget(proxy_port); + proxy_hbox->addStretch(1); + + layout->addLayout(proxy_hbox); + QLabel *fee_help = new QLabel(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended.")); + fee_help->setWordWrap(true); + layout->addWidget(fee_help); + + QHBoxLayout *fee_hbox = new QHBoxLayout(); + fee_hbox->addSpacing(18); + QLabel *fee_label = new QLabel(tr("Pay transaction &fee")); + fee_hbox->addWidget(fee_label); + fee_edit = new QLineEdit(); + fee_edit->setMaximumWidth(70); + + QDoubleValidator *amountValidator = new QDoubleValidator(this); + amountValidator->setDecimals(8); + amountValidator->setBottom(0.0); + fee_edit->setValidator(amountValidator); + + fee_label->setBuddy(fee_edit); + fee_hbox->addWidget(fee_edit); + fee_hbox->addStretch(1); + + layout->addLayout(fee_hbox); + + layout->addStretch(1); /* Extra space at bottom */ + + setLayout(layout); + + connect(connect_socks4, SIGNAL(toggled(bool)), proxy_ip, SLOT(setEnabled(bool))); + connect(connect_socks4, SIGNAL(toggled(bool)), proxy_port, SLOT(setEnabled(bool))); +} + +void MainOptionsPage::setMapper(MonitoredDataMapper *mapper) +{ + /* Map model to widgets */ + mapper->addMapping(bitcoin_at_startup, OptionsModel::StartAtStartup); + mapper->addMapping(minimize_to_tray, OptionsModel::MinimizeToTray); + mapper->addMapping(map_port_upnp, OptionsModel::MapPortUPnP); + mapper->addMapping(minimize_on_close, OptionsModel::MinimizeOnClose); + mapper->addMapping(connect_socks4, OptionsModel::ConnectSOCKS4); + mapper->addMapping(proxy_ip, OptionsModel::ProxyIP); + mapper->addMapping(proxy_port, OptionsModel::ProxyPort); + mapper->addMapping(fee_edit, OptionsModel::Fee); +} + diff --git a/gui/src/optionsmodel.cpp b/gui/src/optionsmodel.cpp index e3287f3916..f653f67e53 100644 --- a/gui/src/optionsmodel.cpp +++ b/gui/src/optionsmodel.cpp @@ -15,10 +15,8 @@ int OptionsModel::rowCount(const QModelIndex & parent) const QVariant OptionsModel::data(const QModelIndex & index, int role) const { - qDebug() << "OptionsModel::data" << " " << index.row() << " " << role; if(role == Qt::EditRole) { - /* Delegate to specific column handlers */ switch(index.row()) { case StartAtStartup: @@ -46,12 +44,79 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role) { - qDebug() << "OptionsModel::setData" << " " << index.row() << "=" << value; + bool successful = true; /* set to false on parse error */ + if(role == Qt::EditRole) + { + switch(index.row()) + { + case StartAtStartup: + successful = false; /*TODO*/ + break; + case MinimizeToTray: + fMinimizeToTray = value.toBool(); + break; + case MapPortUPnP: + fUseUPnP = value.toBool(); + break; + case MinimizeOnClose: + fMinimizeOnClose = value.toBool(); + break; + case ConnectSOCKS4: + fUseProxy = value.toBool(); + break; + case ProxyIP: + { + /* Use CAddress to parse IP */ + CAddress addr(value.toString().toStdString() + ":1"); + if (addr.ip != INADDR_NONE) + { + addrProxy.ip = addr.ip; + } else { + successful = false; + } + } + break; + case ProxyPort: + { + int nPort = atoi(value.toString().toAscii().data()); + if (nPort > 0 && nPort < USHRT_MAX) + { + addrProxy.port = htons(nPort); + } else { + successful = false; + } + } + break; + case Fee: { + int64 retval; + if(ParseMoney(value.toString().toStdString(), retval)) + { + nTransactionFee = retval; + } else { + successful = false; /* parse error */ + } + } + break; + default: + break; + } + } emit dataChanged(index, index); - return true; + + return successful; } qint64 OptionsModel::getTransactionFee() { return nTransactionFee; } + +bool getMinimizeToTray() +{ + return fMinimizeToTray; +} + +bool getMinimizeOnClose() +{ + return fMinimizeOnClose; +} -- cgit v1.2.3 From 5d5990dc8f3d0b8e128a8d4cd30c1390c5767c88 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 1 Jun 2011 15:33:33 +0200 Subject: Impl->Priv --- gui/include/transactiontablemodel.h | 4 ++-- gui/src/transactiontablemodel.cpp | 22 ++++++++++------------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/gui/include/transactiontablemodel.h b/gui/include/transactiontablemodel.h index b02019d472..b484a5973a 100644 --- a/gui/include/transactiontablemodel.h +++ b/gui/include/transactiontablemodel.h @@ -4,7 +4,7 @@ #include #include -class TransactionTableImpl; +class TransactionTablePriv; class TransactionRecord; class TransactionTableModel : public QAbstractTableModel @@ -39,7 +39,7 @@ public: QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; private: QStringList columns; - TransactionTableImpl *impl; + TransactionTablePriv *priv; QVariant formatTxStatus(const TransactionRecord *wtx) const; QVariant formatTxDate(const TransactionRecord *wtx) const; diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index 57d618e9db..c7fb43edbe 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -14,10 +14,9 @@ const QString TransactionTableModel::Sent = "s"; const QString TransactionTableModel::Received = "r"; const QString TransactionTableModel::Other = "o"; -/* Private implementation, no need to pull this into header */ -class TransactionTableImpl +/* Private implementation */ +struct TransactionTablePriv { -public: /* Local cache of wallet. * As it is in the same order as the CWallet, by definition * this is sorted by sha256. @@ -93,11 +92,11 @@ static int column_alignments[] = { TransactionTableModel::TransactionTableModel(QObject *parent): QAbstractTableModel(parent), - impl(new TransactionTableImpl()) + priv(new TransactionTablePriv()) { columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); - impl->refreshWallet(); + priv->refreshWallet(); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); @@ -106,7 +105,7 @@ TransactionTableModel::TransactionTableModel(QObject *parent): TransactionTableModel::~TransactionTableModel() { - delete impl; + delete priv; } void TransactionTableModel::update() @@ -133,7 +132,7 @@ void TransactionTableModel::update() transactions that were added/removed. */ beginResetModel(); - impl->updateWallet(updated); + priv->updateWallet(updated); endResetModel(); } } @@ -141,7 +140,7 @@ void TransactionTableModel::update() int TransactionTableModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); - return impl->size(); + return priv->size(); } int TransactionTableModel::columnCount(const QModelIndex &parent) const @@ -370,14 +369,13 @@ Qt::ItemFlags TransactionTableModel::flags(const QModelIndex &index) const return QAbstractTableModel::flags(index); } - -QModelIndex TransactionTableModel::index ( int row, int column, const QModelIndex & parent ) const +QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent); - TransactionRecord *data = impl->index(row); + TransactionRecord *data = priv->index(row); if(data) { - return createIndex(row, column, impl->index(row)); + return createIndex(row, column, priv->index(row)); } else { return QModelIndex(); } -- cgit v1.2.3 From c51f051257e678b3fd26efdd84a89329420c96f5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 1 Jun 2011 15:33:51 +0200 Subject: Add addresses that we've sent to to the address book --- gui/src/clientmodel.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp index 641c515ef9..97b5f44fe7 100644 --- a/gui/src/clientmodel.cpp +++ b/gui/src/clientmodel.cpp @@ -106,6 +106,11 @@ ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payA return MiscError; } } + // Add addresses that we've sent to to the address book + std::string strAddress = payTo.toStdString(); + CRITICAL_BLOCK(cs_mapAddressBook) + if (!mapAddressBook.count(strAddress)) + SetAddressBookName(strAddress, ""); return OK; } -- cgit v1.2.3 From f96681c5e440e1ebbcb8c79d868a14c2c51c5298 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 1 Jun 2011 15:50:09 +0200 Subject: beginning of address model --- gui/include/addresstablemodel.h | 9 ++- gui/src/addresstablemodel.cpp | 124 +++++++++++++++++++++++++++++++++------- 2 files changed, 112 insertions(+), 21 deletions(-) diff --git a/gui/include/addresstablemodel.h b/gui/include/addresstablemodel.h index 97780c5428..81fad6db25 100644 --- a/gui/include/addresstablemodel.h +++ b/gui/include/addresstablemodel.h @@ -2,12 +2,16 @@ #define ADDRESSTABLEMODEL_H #include +#include + +class AddressTablePriv; class AddressTableModel : public QAbstractTableModel { Q_OBJECT public: explicit AddressTableModel(QObject *parent = 0); + ~AddressTableModel(); enum { Label = 0, /* User specified label */ @@ -25,7 +29,10 @@ public: int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; - Qt::ItemFlags flags(const QModelIndex &index) const; + QModelIndex index ( int row, int column, const QModelIndex & parent ) const; +private: + AddressTablePriv *priv; + QStringList columns; signals: public slots: diff --git a/gui/src/addresstablemodel.cpp b/gui/src/addresstablemodel.cpp index aec29fa202..d1f1a9c848 100644 --- a/gui/src/addresstablemodel.cpp +++ b/gui/src/addresstablemodel.cpp @@ -4,26 +4,90 @@ const QString AddressTableModel::Send = "S"; const QString AddressTableModel::Receive = "R"; +struct AddressTableEntry +{ + enum Type { + Sending, + Receiving + }; + Type type; + QString label; + QString address; + + AddressTableEntry() {} + AddressTableEntry(Type type, const QString &label, const QString &address): + type(type), label(label), address(address) {} +}; + +/* Private implementation */ +struct AddressTablePriv +{ + QList cachedAddressTable; + + void refreshAddressTable() + { + cachedAddressTable.clear(); + + } + + void updateAddressTable() + { + CRITICAL_BLOCK(cs_mapKeys) + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item, mapAddressBook) + { + std::string strAddress = item.first; + std::string strName = item.second; + uint160 hash160; + bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160)); + cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending, + QString::fromStdString(strName), + QString::fromStdString(strAddress))); + } + } + } + + + int size() + { + return cachedAddressTable.size(); + } + + AddressTableEntry *index(int idx) + { + if(idx >= 0 && idx < cachedAddressTable.size()) + { + return &cachedAddressTable[idx]; + } else { + return 0; + } + } +}; + AddressTableModel::AddressTableModel(QObject *parent) : - QAbstractTableModel(parent) + QAbstractTableModel(parent),priv(0) { + columns << tr("Label") << tr("Address"); + priv = new AddressTablePriv(); + priv->refreshAddressTable(); +} +AddressTableModel::~AddressTableModel() +{ + delete priv; } int AddressTableModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); - int retval = 0; - CRITICAL_BLOCK(cs_mapAddressBook) - { - retval = mapAddressBook.size(); - } - return retval; + return priv->size(); } int AddressTableModel::columnCount(const QModelIndex &parent) const { - return 2; + Q_UNUSED(parent); + return columns.length(); } QVariant AddressTableModel::data(const QModelIndex &index, int role) const @@ -31,20 +95,28 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const if(!index.isValid()) return QVariant(); + AddressTableEntry *rec = static_cast(index.internalPointer()); + if(role == Qt::DisplayRole) { /* index.row(), index.column() */ /* Return QString */ - if(index.column() == Address) - return "1PC9aZC4hNX2rmmrt7uHTfYAS3hRbph4UN" + QString::number(index.row()); - else - return "Description"; + switch(index.column()) + { + case Label: + return rec->label; + case Address: + return rec->address; + } } else if (role == TypeRole) { - switch(index.row() % 2) + switch(rec->type) { - case 0: return Send; - case 1: return Receive; + case AddressTableEntry::Sending: + return Send; + case AddressTableEntry::Receiving: + return Receive; + default: break; } } return QVariant(); @@ -52,13 +124,25 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const { + if(orientation == Qt::Horizontal) + { + if(role == Qt::DisplayRole) + { + return columns[section]; + } + } return QVariant(); } -Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const +QModelIndex AddressTableModel::index ( int row, int column, const QModelIndex & parent ) const { - if (!index.isValid()) - return Qt::ItemIsEnabled; - - return QAbstractTableModel::flags(index); + Q_UNUSED(parent); + AddressTableEntry *data = priv->index(row); + if(data) + { + return createIndex(row, column, priv->index(row)); + } else { + return QModelIndex(); + } } + -- cgit v1.2.3 From 9aef9bca3de748870d96d68c74fc423298b9829e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 1 Jun 2011 17:13:25 +0200 Subject: define PAIR_TYPE as std::pair --- core/include/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/include/util.h b/core/include/util.h index b1eabd52d5..eccdbd372d 100644 --- a/core/include/util.h +++ b/core/include/util.h @@ -65,7 +65,7 @@ typedef unsigned long long uint64; #endif // This is needed because the foreach macro can't get over the comma in pair -#define PAIRTYPE(t1, t2) pair +#define PAIRTYPE(t1, t2) std::pair // Used to bypass the rule against non-const reference to temporary // where it makes sense with wrappers such as CFlatData or CTxDB -- cgit v1.2.3 From 9357dcf68e7d9923c7537bec1bbaeec80f526e57 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 1 Jun 2011 17:42:31 +0200 Subject: update readme --- README.rst | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index ef33af0730..047af0d1be 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ Bitcoin-qt: Qt4 based GUI replacement for Bitcoin ================================================= **Warning** **Warning** **Warning** -Pre-alpha stuff! Developer only! +Pre-alpha stuff! Use on testnet only! This has been implemented: @@ -18,16 +18,15 @@ This has been implemented: - Send coins dialog: address and input validation -- Address book and transactions views +- Address book and transactions views and models -This has to be done: - -- Further integration of bitcoin core, so that it can actually connect - to the network, send commands and get feedback +- Sending coins -- Address book and transactions models: so that - something sensible is shown instead of dummy data :) +This has to be done: - Internationalization (convert WX language files) - Build on Windows + +- Details dialog for transactions (on double click) + -- cgit v1.2.3 From df6dfb4ab82da88103738ace69dca2d42fcb87b0 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 1 Jun 2011 18:24:22 +0200 Subject: readme fix --- README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.rst b/README.rst index 047af0d1be..715296431e 100644 --- a/README.rst +++ b/README.rst @@ -24,6 +24,12 @@ This has been implemented: This has to be done: +- Settings are not remembered between invocations yet + +- Minimize to tray / Minimize on close + +- Start at system start + - Internationalization (convert WX language files) - Build on Windows -- cgit v1.2.3 From ef1b844e7b444b07e708dcd9a1e0dc93510dade5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 1 Jun 2011 20:08:21 +0200 Subject: monospace font for bitcoin addresses --- gui/include/addresstablemodel.h | 4 +++- gui/include/guiutil.h | 3 +++ gui/src/addressbookdialog.cpp | 13 +++++++++++-- gui/src/addresstablemodel.cpp | 21 +++++++++++++++------ gui/src/bitcoingui.cpp | 2 +- gui/src/guiutil.cpp | 7 +++++++ 6 files changed, 40 insertions(+), 10 deletions(-) diff --git a/gui/include/addresstablemodel.h b/gui/include/addresstablemodel.h index 81fad6db25..6617bb3e89 100644 --- a/gui/include/addresstablemodel.h +++ b/gui/include/addresstablemodel.h @@ -29,7 +29,9 @@ public: int columnCount(const QModelIndex &parent) 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 ) const; + QModelIndex index(int row, int column, const QModelIndex & parent) const; + + void updateList(); private: AddressTablePriv *priv; QStringList columns; diff --git a/gui/include/guiutil.h b/gui/include/guiutil.h index a73eadc02a..eaa8199900 100644 --- a/gui/include/guiutil.h +++ b/gui/include/guiutil.h @@ -2,7 +2,10 @@ #define GUIUTIL_H #include +#include QString DateTimeStr(qint64 nTime); +/* Render bitcoin addresses in monospace font */ +QFont bitcoinAddressFont(); #endif // GUIUTIL_H diff --git a/gui/src/addressbookdialog.cpp b/gui/src/addressbookdialog.cpp index 4ca863bd20..ba6fdb51a0 100644 --- a/gui/src/addressbookdialog.cpp +++ b/gui/src/addressbookdialog.cpp @@ -5,6 +5,7 @@ #include "editaddressdialog.h" #include +#include #include AddressBookDialog::AddressBookDialog(QWidget *parent) : @@ -72,13 +73,21 @@ QTableView *AddressBookDialog::getCurrentTable() void AddressBookDialog::on_copyToClipboard_clicked() { - /* Copy currently selected address to clipboard */ + /* Copy currently selected address to clipboard + (or nothing, if nothing selected) + */ + QTableView *table = getCurrentTable(); + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + foreach (QModelIndex index, indexes) { + QVariant address = table->model()->data(index); + QApplication::clipboard()->setText(address.toString()); + } } void AddressBookDialog::on_editButton_clicked() { - /* Double click should trigger edit button */ + /* Double click triggers edit button */ EditAddressDialog dlg; dlg.exec(); } diff --git a/gui/src/addresstablemodel.cpp b/gui/src/addresstablemodel.cpp index d1f1a9c848..21a9044271 100644 --- a/gui/src/addresstablemodel.cpp +++ b/gui/src/addresstablemodel.cpp @@ -1,4 +1,5 @@ #include "addresstablemodel.h" +#include "guiutil.h" #include "main.h" const QString AddressTableModel::Send = "S"; @@ -28,10 +29,6 @@ struct AddressTablePriv { cachedAddressTable.clear(); - } - - void updateAddressTable() - { CRITICAL_BLOCK(cs_mapKeys) CRITICAL_BLOCK(cs_mapAddressBook) { @@ -48,7 +45,6 @@ struct AddressTablePriv } } - int size() { return cachedAddressTable.size(); @@ -108,6 +104,12 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const case Address: return rec->address; } + } else if (role == Qt::FontRole) + { + if(index.column() == Address) + { + return bitcoinAddressFont(); + } } else if (role == TypeRole) { switch(rec->type) @@ -134,7 +136,7 @@ QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, return QVariant(); } -QModelIndex AddressTableModel::index ( int row, int column, const QModelIndex & parent ) const +QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & parent) const { Q_UNUSED(parent); AddressTableEntry *data = priv->index(row); @@ -146,3 +148,10 @@ QModelIndex AddressTableModel::index ( int row, int column, const QModelIndex & } } +void AddressTableModel::updateList() +{ + /* Update internal model from Bitcoin core */ + beginResetModel(); + priv->refreshAddressTable(); + endResetModel(); +} diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index 66891222a8..0dc895dcc9 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -83,7 +83,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ labelBalance = new QLabel(); - labelBalance->setFont(QFont("Teletype")); + labelBalance->setFont(QFont("Monospace")); hbox_balance->addWidget(labelBalance); hbox_balance->addStretch(1); diff --git a/gui/src/guiutil.cpp b/gui/src/guiutil.cpp index d27a8e101c..59b4de305d 100644 --- a/gui/src/guiutil.cpp +++ b/gui/src/guiutil.cpp @@ -7,3 +7,10 @@ QString DateTimeStr(qint64 nTime) QDateTime date = QDateTime::fromMSecsSinceEpoch(nTime*1000); return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm"); } + +QFont bitcoinAddressFont() +{ + QFont font("Monospace"); + font.setStyleHint(QFont::TypeWriter); + return font; +} -- cgit v1.2.3 From e457b021421c9065c8677e7fb7d7cae0391bf1f8 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 2 Jun 2011 15:57:23 +0200 Subject: namespacing, user friendly base58 entry, addressbook work --- gui/forms/editaddressdialog.ui | 46 +++++++++++++++++++++++++---------- gui/include/bitcoinaddressvalidator.h | 2 ++ gui/include/editaddressdialog.h | 11 +++++++-- gui/include/guiutil.h | 22 ++++++++++++++--- gui/src/addressbookdialog.cpp | 16 ++++++++---- gui/src/addresstablemodel.cpp | 4 ++- gui/src/bitcoinaddressvalidator.cpp | 37 ++++++++++++++++++++++++++++ gui/src/bitcoingui.cpp | 2 ++ gui/src/editaddressdialog.cpp | 5 +++- gui/src/guiutil.cpp | 25 +++++++++++++++++-- gui/src/sendcoinsdialog.cpp | 11 +++------ gui/src/transactiontablemodel.cpp | 4 +-- 12 files changed, 147 insertions(+), 38 deletions(-) diff --git a/gui/forms/editaddressdialog.ui b/gui/forms/editaddressdialog.ui index 2d8aa880f6..763a0bb8e4 100644 --- a/gui/forms/editaddressdialog.ui +++ b/gui/forms/editaddressdialog.ui @@ -6,26 +6,46 @@ 0 0 - 400 - 300 + 458 + 113 - Dialog + Edit Address - - - Qt::Vertical - - - - 20 - 40 - + + + QFormLayout::AllNonFixedFieldsGrow - + + + + &Label + + + labelEdit + + + + + + + &Address + + + addressEdit + + + + + + + + + + diff --git a/gui/include/bitcoinaddressvalidator.h b/gui/include/bitcoinaddressvalidator.h index 8322eef739..c7b2eefc69 100644 --- a/gui/include/bitcoinaddressvalidator.h +++ b/gui/include/bitcoinaddressvalidator.h @@ -9,6 +9,8 @@ class BitcoinAddressValidator : public QRegExpValidator public: explicit BitcoinAddressValidator(QObject *parent = 0); + State validate(QString &input, int &pos) const; + static const int MaxAddressLength = 34; signals: diff --git a/gui/include/editaddressdialog.h b/gui/include/editaddressdialog.h index 650ed534a0..8e4a0388c2 100644 --- a/gui/include/editaddressdialog.h +++ b/gui/include/editaddressdialog.h @@ -12,8 +12,15 @@ class EditAddressDialog : public QDialog Q_OBJECT public: - explicit EditAddressDialog(QWidget *parent = 0); - ~EditAddressDialog(); + enum Mode { + NewReceivingAddress, + NewSendingAddress, + EditReceivingAddress, + EditSendingAddress + }; + + explicit EditAddressDialog(Mode mode, QWidget *parent = 0); + ~EditAddressDialog(); private: Ui::EditAddressDialog *ui; diff --git a/gui/include/guiutil.h b/gui/include/guiutil.h index eaa8199900..748e29bf37 100644 --- a/gui/include/guiutil.h +++ b/gui/include/guiutil.h @@ -2,10 +2,24 @@ #define GUIUTIL_H #include -#include -QString DateTimeStr(qint64 nTime); -/* Render bitcoin addresses in monospace font */ -QFont bitcoinAddressFont(); +QT_BEGIN_NAMESPACE +class QFont; +class QLineEdit; +class QWidget; +QT_END_NAMESPACE + +class GUIUtil +{ +public: + static QString DateTimeStr(qint64 nTime); + + /* Render bitcoin addresses in monospace font */ + static QFont bitcoinAddressFont(); + + static void setupAddressWidget(QLineEdit *widget, QWidget *parent); + + static void setupAmountWidget(QLineEdit *widget, QWidget *parent); +}; #endif // GUIUTIL_H diff --git a/gui/src/addressbookdialog.cpp b/gui/src/addressbookdialog.cpp index ba6fdb51a0..853da5852e 100644 --- a/gui/src/addressbookdialog.cpp +++ b/gui/src/addressbookdialog.cpp @@ -87,14 +87,20 @@ void AddressBookDialog::on_copyToClipboard_clicked() void AddressBookDialog::on_editButton_clicked() { - /* Double click triggers edit button */ - EditAddressDialog dlg; + /* Double click also triggers edit button */ + EditAddressDialog dlg( + ui->tabWidget->currentIndex() == SendingTab ? + EditAddressDialog::EditSendingAddress : + EditAddressDialog::EditReceivingAddress); dlg.exec(); } void AddressBookDialog::on_newAddressButton_clicked() { - EditAddressDialog dlg; + EditAddressDialog dlg( + ui->tabWidget->currentIndex() == SendingTab ? + EditAddressDialog::NewSendingAddress : + EditAddressDialog::NewReceivingAddress); dlg.exec(); } @@ -103,10 +109,10 @@ void AddressBookDialog::on_tabWidget_currentChanged(int index) switch(index) { case SendingTab: - ui->deleteButton->show(); + ui->deleteButton->setEnabled(true); break; case ReceivingTab: - ui->deleteButton->hide(); + ui->deleteButton->setEnabled(false); break; } } diff --git a/gui/src/addresstablemodel.cpp b/gui/src/addresstablemodel.cpp index 21a9044271..c375ca7555 100644 --- a/gui/src/addresstablemodel.cpp +++ b/gui/src/addresstablemodel.cpp @@ -2,6 +2,8 @@ #include "guiutil.h" #include "main.h" +#include + const QString AddressTableModel::Send = "S"; const QString AddressTableModel::Receive = "R"; @@ -108,7 +110,7 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const { if(index.column() == Address) { - return bitcoinAddressFont(); + return GUIUtil::bitcoinAddressFont(); } } else if (role == TypeRole) { diff --git a/gui/src/bitcoinaddressvalidator.cpp b/gui/src/bitcoinaddressvalidator.cpp index 8e71916391..bccf445757 100644 --- a/gui/src/bitcoinaddressvalidator.cpp +++ b/gui/src/bitcoinaddressvalidator.cpp @@ -2,7 +2,44 @@ #include "base58.h" +#include + +/* Base58 characters are: + "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + + This is: + - All numbers except for '0' + - All uppercase letters except for 'I' and 'O' + - All lowercase letters except for 'l' + + User friendly Base58 input can map + - 'l' and 'I' to '1' + - '0' and 'O' to 'o' +*/ + BitcoinAddressValidator::BitcoinAddressValidator(QObject *parent) : QRegExpValidator(QRegExp(QString("^[")+QString(pszBase58)+QString("]+")), parent) { } + +QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) const +{ + for(int idx=0; idxaddWidget(new QLabel(tr("Your Bitcoin Address:"))); address = new QLineEdit(); address->setReadOnly(true); + address->setFont(GUIUtil::bitcoinAddressFont()); hbox_address->addWidget(address); QPushButton *button_new = new QPushButton(tr("&New...")); diff --git a/gui/src/editaddressdialog.cpp b/gui/src/editaddressdialog.cpp index bd5559792a..0699b56321 100644 --- a/gui/src/editaddressdialog.cpp +++ b/gui/src/editaddressdialog.cpp @@ -1,11 +1,14 @@ #include "editaddressdialog.h" #include "ui_editaddressdialog.h" +#include "guiutil.h" -EditAddressDialog::EditAddressDialog(QWidget *parent) : +EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : QDialog(parent), ui(new Ui::EditAddressDialog) { ui->setupUi(this); + + GUIUtil::setupAddressWidget(ui->addressEdit, this); } EditAddressDialog::~EditAddressDialog() diff --git a/gui/src/guiutil.cpp b/gui/src/guiutil.cpp index 59b4de305d..d01f23d851 100644 --- a/gui/src/guiutil.cpp +++ b/gui/src/guiutil.cpp @@ -1,16 +1,37 @@ #include "guiutil.h" +#include "bitcoinaddressvalidator.h" +#include #include +#include +#include +#include -QString DateTimeStr(qint64 nTime) +QString GUIUtil::DateTimeStr(qint64 nTime) { QDateTime date = QDateTime::fromMSecsSinceEpoch(nTime*1000); return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm"); } -QFont bitcoinAddressFont() +QFont GUIUtil::bitcoinAddressFont() { QFont font("Monospace"); font.setStyleHint(QFont::TypeWriter); return font; } + +void GUIUtil::setupAddressWidget(QLineEdit *widget, QWidget *parent) +{ + widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength); + widget->setValidator(new BitcoinAddressValidator(parent)); + widget->setFont(bitcoinAddressFont()); +} + +void GUIUtil::setupAmountWidget(QLineEdit *widget, QWidget *parent) +{ + QDoubleValidator *amountValidator = new QDoubleValidator(parent); + amountValidator->setDecimals(8); + amountValidator->setBottom(0.0); + widget->setValidator(amountValidator); +} + diff --git a/gui/src/sendcoinsdialog.cpp b/gui/src/sendcoinsdialog.cpp index a6ab601592..b50ed99099 100644 --- a/gui/src/sendcoinsdialog.cpp +++ b/gui/src/sendcoinsdialog.cpp @@ -1,9 +1,9 @@ #include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" #include "clientmodel.h" +#include "guiutil.h" #include "addressbookdialog.h" -#include "bitcoinaddressvalidator.h" #include "optionsmodel.h" #include @@ -22,13 +22,8 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : { ui->setupUi(this); - /* Set up validators */ - ui->payTo->setMaxLength(BitcoinAddressValidator::MaxAddressLength); - ui->payTo->setValidator(new BitcoinAddressValidator(this)); - QDoubleValidator *amountValidator = new QDoubleValidator(this); - amountValidator->setDecimals(8); - amountValidator->setBottom(0.0); - ui->payAmount->setValidator(amountValidator); + GUIUtil::setupAddressWidget(ui->payTo, this); + GUIUtil::setupAmountWidget(ui->payAmount, this); /* Set initial address if provided */ if(!address.isEmpty()) diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index c7fb43edbe..e23f45dd0e 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -159,7 +159,7 @@ QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) con status = tr("Open for %n block(s)","",wtx->status.open_for); break; case TransactionStatus::OpenUntilDate: - status = tr("Open until ") + DateTimeStr(wtx->status.open_for); + status = tr("Open until ") + GUIUtil::DateTimeStr(wtx->status.open_for); break; case TransactionStatus::Offline: status = tr("%1/offline").arg(wtx->status.depth); @@ -179,7 +179,7 @@ QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const { if(wtx->time) { - return QVariant(DateTimeStr(wtx->time)); + return QVariant(GUIUtil::DateTimeStr(wtx->time)); } else { return QVariant(); } -- cgit v1.2.3 From 5c94371f9a9bf41a5544403ee87ac331c2b0b1c3 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 2 Jun 2011 17:37:03 +0200 Subject: resolve dependency issues --- bitcoin.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/bitcoin.pro b/bitcoin.pro index d34488dd48..e2f282a3f6 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -11,6 +11,7 @@ QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wno-invalid-offsetof - # WINDOWS defines, -DSSL, look at build system # Input +DEPENDPATH += gui/include core/include cryptopp/include core/include json/include HEADERS += gui/include/bitcoingui.h \ gui/include/transactiontablemodel.h \ gui/include/addresstablemodel.h \ -- cgit v1.2.3 From 44384a4602821216023ee63157ac6e376d1e9e10 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 2 Jun 2011 17:48:45 +0200 Subject: edit address dialog: basic data/widget binding --- gui/include/addresstablemodel.h | 1 + gui/include/editaddressdialog.h | 9 +++++++++ gui/src/addressbookdialog.cpp | 9 +++++++++ gui/src/addresstablemodel.cpp | 28 +++++++++++++++++++++++++--- gui/src/editaddressdialog.cpp | 41 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 84 insertions(+), 4 deletions(-) diff --git a/gui/include/addresstablemodel.h b/gui/include/addresstablemodel.h index 6617bb3e89..f9ccab4eb6 100644 --- a/gui/include/addresstablemodel.h +++ b/gui/include/addresstablemodel.h @@ -28,6 +28,7 @@ public: int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex & index, const QVariant & value, int role); QVariant headerData(int section, Qt::Orientation orientation, int role) const; QModelIndex index(int row, int column, const QModelIndex & parent) const; diff --git a/gui/include/editaddressdialog.h b/gui/include/editaddressdialog.h index 8e4a0388c2..3d8a5dcf8d 100644 --- a/gui/include/editaddressdialog.h +++ b/gui/include/editaddressdialog.h @@ -3,9 +3,14 @@ #include +QT_BEGIN_NAMESPACE +class QDataWidgetMapper; +QT_END_NAMESPACE + namespace Ui { class EditAddressDialog; } +class AddressTableModel; class EditAddressDialog : public QDialog { @@ -22,8 +27,12 @@ public: explicit EditAddressDialog(Mode mode, QWidget *parent = 0); ~EditAddressDialog(); + void setModel(AddressTableModel *model); + void loadRow(int row); + private: Ui::EditAddressDialog *ui; + QDataWidgetMapper *mapper; }; #endif // EDITADDRESSDIALOG_H diff --git a/gui/src/addressbookdialog.cpp b/gui/src/addressbookdialog.cpp index 853da5852e..3f8e38152a 100644 --- a/gui/src/addressbookdialog.cpp +++ b/gui/src/addressbookdialog.cpp @@ -87,11 +87,19 @@ void AddressBookDialog::on_copyToClipboard_clicked() void AddressBookDialog::on_editButton_clicked() { + QModelIndexList indexes = getCurrentTable()->selectionModel()->selectedRows(); + if(indexes.isEmpty()) + { + return; + } + /* Double click also triggers edit button */ EditAddressDialog dlg( ui->tabWidget->currentIndex() == SendingTab ? EditAddressDialog::EditSendingAddress : EditAddressDialog::EditReceivingAddress); + dlg.setModel(model); + dlg.loadRow(indexes.at(0).row()); dlg.exec(); } @@ -101,6 +109,7 @@ void AddressBookDialog::on_newAddressButton_clicked() ui->tabWidget->currentIndex() == SendingTab ? EditAddressDialog::NewSendingAddress : EditAddressDialog::NewReceivingAddress); + dlg.setModel(model); dlg.exec(); } diff --git a/gui/src/addresstablemodel.cpp b/gui/src/addresstablemodel.cpp index c375ca7555..1cacd08fee 100644 --- a/gui/src/addresstablemodel.cpp +++ b/gui/src/addresstablemodel.cpp @@ -95,10 +95,8 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const AddressTableEntry *rec = static_cast(index.internalPointer()); - if(role == Qt::DisplayRole) + if(role == Qt::DisplayRole || role == Qt::EditRole) { - /* index.row(), index.column() */ - /* Return QString */ switch(index.column()) { case Label: @@ -126,6 +124,30 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const return QVariant(); } +bool AddressTableModel::setData(const QModelIndex & index, const QVariant & value, int role) +{ + if(!index.isValid()) + return false; + + if(role == Qt::EditRole) + { + switch(index.column()) + { + case Label: + /* TODO */ + break; + case Address: + /* TODO */ + /* Double-check that we're not overwriting receiving address */ + /* Note that changing address changes index in map */ + break; + } + /* emit dataChanged(index, index); */ + return true; + } + return false; +} + QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if(orientation == Qt::Horizontal) diff --git a/gui/src/editaddressdialog.cpp b/gui/src/editaddressdialog.cpp index 0699b56321..6c0148d9d7 100644 --- a/gui/src/editaddressdialog.cpp +++ b/gui/src/editaddressdialog.cpp @@ -1,17 +1,56 @@ #include "editaddressdialog.h" #include "ui_editaddressdialog.h" +#include "addresstablemodel.h" #include "guiutil.h" +#include +#include + EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : QDialog(parent), - ui(new Ui::EditAddressDialog) + ui(new Ui::EditAddressDialog), mapper(0) { ui->setupUi(this); GUIUtil::setupAddressWidget(ui->addressEdit, this); + + switch(mode) + { + case NewReceivingAddress: + setWindowTitle(tr("New receiving address")); + ui->addressEdit->setEnabled(false); + break; + case NewSendingAddress: + setWindowTitle(tr("New sending address")); + break; + case EditReceivingAddress: + setWindowTitle(tr("Edit receiving address")); + ui->addressEdit->setReadOnly(true); + break; + case EditSendingAddress: + setWindowTitle(tr("Edit sending address")); + break; + } + + mapper = new QDataWidgetMapper(this); + } EditAddressDialog::~EditAddressDialog() { delete ui; } + +void EditAddressDialog::setModel(AddressTableModel *model) +{ + qDebug() << "setModel " << model; + mapper->setModel(model); + mapper->addMapping(ui->labelEdit, AddressTableModel::Label); + mapper->addMapping(ui->addressEdit, AddressTableModel::Address); +} + +void EditAddressDialog::loadRow(int row) +{ + qDebug() << "loadRow " << row; + mapper->setCurrentIndex(row); +} -- cgit v1.2.3 From dab03e34f5973d27797b7b415558fa75691a3830 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 3 Jun 2011 10:52:49 +0200 Subject: Make base58 validator explicit --- gui/include/bitcoinaddressvalidator.h | 5 ++++- gui/src/bitcoinaddressvalidator.cpp | 26 ++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/gui/include/bitcoinaddressvalidator.h b/gui/include/bitcoinaddressvalidator.h index c7b2eefc69..73f6ea1f61 100644 --- a/gui/include/bitcoinaddressvalidator.h +++ b/gui/include/bitcoinaddressvalidator.h @@ -3,7 +3,10 @@ #include -class BitcoinAddressValidator : public QRegExpValidator +/* Base48 entry widget validator. + Corrects near-miss characters and refuses characters that are no part of base48. + */ +class BitcoinAddressValidator : public QValidator { Q_OBJECT public: diff --git a/gui/src/bitcoinaddressvalidator.cpp b/gui/src/bitcoinaddressvalidator.cpp index bccf445757..761a266933 100644 --- a/gui/src/bitcoinaddressvalidator.cpp +++ b/gui/src/bitcoinaddressvalidator.cpp @@ -1,7 +1,5 @@ #include "bitcoinaddressvalidator.h" -#include "base58.h" - #include /* Base58 characters are: @@ -18,12 +16,13 @@ */ BitcoinAddressValidator::BitcoinAddressValidator(QObject *parent) : - QRegExpValidator(QRegExp(QString("^[")+QString(pszBase58)+QString("]+")), parent) + QValidator(parent) { } QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) const { + /* Correction */ for(int idx=0; idx= '0' && ch<='9') || + (ch >= 'a' && ch<='z') || + (ch >= 'A' && ch<='Z')) && + ch != 'l' && ch != 'I' && ch != '0' && ch != 'O') + { + /* Alphanumeric and not a 'forbidden' character */ + } + else + { + state = QValidator::Invalid; + } } - return QRegExpValidator::validate(input, pos); + + return state; } -- cgit v1.2.3 From 48208883de8397dbd13bec351e71fe62d5cd5db1 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 3 Jun 2011 15:16:11 +0200 Subject: Finish implementation of address book --- gui/include/addresstablemodel.h | 13 +++++++-- gui/include/editaddressdialog.h | 3 ++ gui/src/addressbookdialog.cpp | 16 +++++++---- gui/src/addresstablemodel.cpp | 64 ++++++++++++++++++++++++++++++++++++++--- gui/src/bitcoingui.cpp | 7 +---- gui/src/editaddressdialog.cpp | 32 +++++++++++++++++---- 6 files changed, 113 insertions(+), 22 deletions(-) diff --git a/gui/include/addresstablemodel.h b/gui/include/addresstablemodel.h index f9ccab4eb6..1890260971 100644 --- a/gui/include/addresstablemodel.h +++ b/gui/include/addresstablemodel.h @@ -13,10 +13,10 @@ public: explicit AddressTableModel(QObject *parent = 0); ~AddressTableModel(); - enum { + enum ColumnIndex { Label = 0, /* User specified label */ Address = 1 /* Bitcoin address */ - } ColumnIndex; + }; enum { TypeRole = Qt::UserRole @@ -25,13 +25,22 @@ public: static const QString Send; /* Send addres */ static const QString Receive; /* Receive address */ + /* Overridden methods from QAbstractTableModel */ int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; bool setData(const QModelIndex & index, const QVariant & value, int role); QVariant headerData(int section, Qt::Orientation orientation, int role) const; QModelIndex index(int row, int column, const QModelIndex & parent) const; + bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex()); + + /* Add an address to the model. + Returns true on success, false otherwise. + */ + bool addRow(const QString &type, const QString &label, const QString &address); + /* Update address list from core. Invalidates any indices. + */ void updateList(); private: AddressTablePriv *priv; diff --git a/gui/include/editaddressdialog.h b/gui/include/editaddressdialog.h index 3d8a5dcf8d..dd7766951b 100644 --- a/gui/include/editaddressdialog.h +++ b/gui/include/editaddressdialog.h @@ -29,10 +29,13 @@ public: void setModel(AddressTableModel *model); void loadRow(int row); + void saveCurrentRow(); private: Ui::EditAddressDialog *ui; QDataWidgetMapper *mapper; + Mode mode; + AddressTableModel *model; }; #endif // EDITADDRESSDIALOG_H diff --git a/gui/src/addressbookdialog.cpp b/gui/src/addressbookdialog.cpp index 3f8e38152a..9b9e9bbc8e 100644 --- a/gui/src/addressbookdialog.cpp +++ b/gui/src/addressbookdialog.cpp @@ -100,7 +100,10 @@ void AddressBookDialog::on_editButton_clicked() EditAddressDialog::EditReceivingAddress); dlg.setModel(model); dlg.loadRow(indexes.at(0).row()); - dlg.exec(); + if(dlg.exec()) + { + dlg.saveCurrentRow(); + } } void AddressBookDialog::on_newAddressButton_clicked() @@ -110,7 +113,10 @@ void AddressBookDialog::on_newAddressButton_clicked() EditAddressDialog::NewSendingAddress : EditAddressDialog::NewReceivingAddress); dlg.setModel(model); - dlg.exec(); + if(dlg.exec()) + { + dlg.saveCurrentRow(); + } } void AddressBookDialog::on_tabWidget_currentChanged(int index) @@ -130,9 +136,9 @@ void AddressBookDialog::on_deleteButton_clicked() { QTableView *table = getCurrentTable(); QModelIndexList indexes = table->selectionModel()->selectedRows(); - - foreach (QModelIndex index, indexes) { - table->model()->removeRow(index.row()); + if(!indexes.isEmpty()) + { + table->model()->removeRow(indexes.at(0).row()); } } diff --git a/gui/src/addresstablemodel.cpp b/gui/src/addresstablemodel.cpp index 1cacd08fee..fbb2eb528b 100644 --- a/gui/src/addresstablemodel.cpp +++ b/gui/src/addresstablemodel.cpp @@ -13,6 +13,7 @@ struct AddressTableEntry Sending, Receiving }; + Type type; QString label; QString address; @@ -128,21 +129,31 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu { if(!index.isValid()) return false; + AddressTableEntry *rec = static_cast(index.internalPointer()); if(role == Qt::EditRole) { switch(index.column()) { case Label: - /* TODO */ + SetAddressBookName(rec->address.toStdString(), value.toString().toStdString()); + rec->label = value.toString(); break; case Address: - /* TODO */ /* Double-check that we're not overwriting receiving address */ - /* Note that changing address changes index in map */ + if(rec->type == AddressTableEntry::Sending) + { + /* Remove old entry */ + CWalletDB().EraseName(rec->address.toStdString()); + /* Add new entry with new address */ + SetAddressBookName(value.toString().toStdString(), rec->label.toStdString()); + + rec->address = value.toString(); + } break; } - /* emit dataChanged(index, index); */ + emit dataChanged(index, index); + return true; } return false; @@ -179,3 +190,48 @@ void AddressTableModel::updateList() priv->refreshAddressTable(); endResetModel(); } + +bool AddressTableModel::addRow(const QString &type, const QString &label, const QString &address) +{ + std::string strLabel = label.toStdString(); + std::string strAddress = address.toStdString(); + + if(type == Send) + { + /* Check for duplicate */ + CRITICAL_BLOCK(cs_mapAddressBook) + { + if(mapAddressBook.count(strAddress)) + { + return false; + } + } + } else if(type == Receive) + { + /* Generate a new address to associate with given label */ + strAddress = PubKeyToAddress(GetKeyFromKeyPool()); + } else + { + return false; + } + /* Add entry and update list */ + SetAddressBookName(strAddress, strLabel); + updateList(); + return true; +} + +bool AddressTableModel::removeRows(int row, int count, const QModelIndex & parent) +{ + Q_UNUSED(parent); + AddressTableEntry *rec = priv->index(row); + if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving) + { + /* Can only remove one row at a time, and cannot remove rows not in model. + Also refuse to remove receiving addresses. + */ + return false; + } + CWalletDB().EraseName(rec->address.toStdString()); + updateList(); + return true; +} diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index a7f2368034..ba5b1d99a8 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -240,12 +240,7 @@ void BitcoinGUI::addressbookClicked() { AddressBookDialog dlg; dlg.setTab(AddressBookDialog::SendingTab); - /* if an address accepted, do a 'send' to specified address */ - if(dlg.exec()) - { - SendCoinsDialog send(0, dlg.getReturnValue()); - send.exec(); - } + dlg.exec(); } void BitcoinGUI::receivingAddressesClicked() diff --git a/gui/src/editaddressdialog.cpp b/gui/src/editaddressdialog.cpp index 6c0148d9d7..ddc7292cc6 100644 --- a/gui/src/editaddressdialog.cpp +++ b/gui/src/editaddressdialog.cpp @@ -4,11 +4,11 @@ #include "guiutil.h" #include -#include +#include EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : QDialog(parent), - ui(new Ui::EditAddressDialog), mapper(0) + ui(new Ui::EditAddressDialog), mapper(0), mode(mode), model(0) { ui->setupUi(this); @@ -33,7 +33,7 @@ EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : } mapper = new QDataWidgetMapper(this); - + mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); } EditAddressDialog::~EditAddressDialog() @@ -43,7 +43,7 @@ EditAddressDialog::~EditAddressDialog() void EditAddressDialog::setModel(AddressTableModel *model) { - qDebug() << "setModel " << model; + this->model = model; mapper->setModel(model); mapper->addMapping(ui->labelEdit, AddressTableModel::Label); mapper->addMapping(ui->addressEdit, AddressTableModel::Address); @@ -51,6 +51,28 @@ void EditAddressDialog::setModel(AddressTableModel *model) void EditAddressDialog::loadRow(int row) { - qDebug() << "loadRow " << row; mapper->setCurrentIndex(row); } + +void EditAddressDialog::saveCurrentRow() +{ + switch(mode) + { + case NewReceivingAddress: + case NewSendingAddress: + if(!model->addRow( + mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive, + ui->labelEdit->text(), + ui->addressEdit->text())) + { + QMessageBox::warning(this, windowTitle(), + tr("The address %1 is already in the address book.").arg(ui->addressEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); + } + break; + case EditReceivingAddress: + case EditSendingAddress: + mapper->submit(); + break; + } +} -- cgit v1.2.3 From 9d9a4e874db82e63a2b876c9f490be7247856282 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 3 Jun 2011 20:48:03 +0200 Subject: support incremental wallet updates --- gui/include/clientmodel.h | 3 + gui/include/transactiontablemodel.h | 2 + gui/src/clientmodel.cpp | 11 ++++ gui/src/transactiontablemodel.cpp | 112 +++++++++++++++++++++++++++++------- 4 files changed, 107 insertions(+), 21 deletions(-) diff --git a/gui/include/clientmodel.h b/gui/include/clientmodel.h index 49b3460978..01c0d70fc7 100644 --- a/gui/include/clientmodel.h +++ b/gui/include/clientmodel.h @@ -29,6 +29,9 @@ public: int getNumBlocks(); int getNumTransactions(); + /* Set default address */ + void setAddress(const QString &defaultAddress); + /* Send coins */ StatusCode sendCoins(const QString &payTo, qint64 payAmount); private: OptionsModel *options_model; diff --git a/gui/include/transactiontablemodel.h b/gui/include/transactiontablemodel.h index b484a5973a..70377ea0d5 100644 --- a/gui/include/transactiontablemodel.h +++ b/gui/include/transactiontablemodel.h @@ -49,6 +49,8 @@ private: private slots: void update(); + + friend class TransactionTablePriv; }; #endif diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp index 97b5f44fe7..7dcbc576a4 100644 --- a/gui/src/clientmodel.cpp +++ b/gui/src/clientmodel.cpp @@ -63,6 +63,17 @@ void ClientModel::update() emit numTransactionsChanged(getNumTransactions()); } +void ClientModel::setAddress(const QString &defaultAddress) +{ + uint160 hash160; + std::string strAddress = defaultAddress.toStdString(); + if (!AddressToHash160(strAddress, hash160)) + return; + if (!mapPubKeys.count(hash160)) + return; + CWalletDB().WriteDefaultKey(mapPubKeys[hash160]); +} + ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payAmount) { uint160 hash160 = 0; diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index e23f45dd0e..5bea3b598b 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -9,14 +9,39 @@ #include #include #include +#include const QString TransactionTableModel::Sent = "s"; const QString TransactionTableModel::Received = "r"; const QString TransactionTableModel::Other = "o"; +/* Comparison operator for sort/binary search of model tx list */ +struct TxLessThan +{ + bool operator()(const TransactionRecord &a, const TransactionRecord &b) const + { + return a.hash < b.hash; + } + bool operator()(const TransactionRecord &a, const uint256 &b) const + { + return a.hash < b; + } + bool operator()(const uint256 &a, const TransactionRecord &b) const + { + return a < b.hash; + } +}; + /* Private implementation */ struct TransactionTablePriv { + TransactionTablePriv(TransactionTableModel *parent): + parent(parent) + { + } + + TransactionTableModel *parent; + /* Local cache of wallet. * As it is in the same order as the CWallet, by definition * this is sorted by sha256. @@ -39,28 +64,79 @@ struct TransactionTablePriv } } - /* Update our model of the wallet. + /* Update our model of the wallet incrementally. Call with list of hashes of transactions that were added, removed or changed. */ void updateWallet(const QList &updated) { - /* TODO: update only transactions in updated, and only if - the transactions are really part of the visible wallet. - - Update status of the other transactions in the cache just in case, - because this call means that a new block came in. + /* Walk through updated transactions, update model as needed. */ qDebug() << "updateWallet"; - foreach(uint256 hash, updated) + + /* Sort update list, and iterate through it in reverse, so that model updates + can be emitted from end to beginning (so that earlier updates will not influence + the indices of latter ones). + */ + QList updated_sorted = updated; + qSort(updated_sorted); + + CRITICAL_BLOCK(cs_mapWallet) { - qDebug() << " " << QString::fromStdString(hash.ToString()); + for(int update_idx = updated_sorted.size()-1; update_idx >= 0; --update_idx) + { + const uint256 &hash = updated_sorted.at(update_idx); + /* Find transaction in wallet */ + std::map::iterator mi = mapWallet.find(hash); + bool inWallet = mi != mapWallet.end(); + /* Find bounds of this transaction in model */ + QList::iterator lower = qLowerBound( + cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); + QList::iterator upper = qUpperBound( + cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); + int lowerIndex = (lower - cachedWallet.begin()); + int upperIndex = (upper - cachedWallet.begin()); + + bool inModel = false; + if(lower != upper) + { + inModel = true; + } + + qDebug() << " " << QString::fromStdString(hash.ToString()) << inWallet << " " << inModel + << lowerIndex << "-" << upperIndex; + + if(inWallet && !inModel) + { + /* Added */ + QList toInsert = + TransactionRecord::decomposeTransaction(mi->second); + if(!toInsert.isEmpty()) /* only if something to insert */ + { + parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); + int insert_idx = lowerIndex; + foreach(const TransactionRecord &rec, toInsert) + { + cachedWallet.insert(insert_idx, rec); + insert_idx += 1; + } + parent->endInsertRows(); + } + } else if(!inWallet && inModel) + { + /* Removed */ + parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); + cachedWallet.erase(lower, upper); + parent->endRemoveRows(); + } else if(inWallet && inModel) + { + /* Updated */ + + } + } } - /* beginInsertRows(QModelIndex(), first, last) */ - /* endInsertRows */ - /* beginRemoveRows(QModelIndex(), first, last) */ - /* beginEndRows */ - - refreshWallet(); + /* TODO: invalidate status for all transactions + Use counter. Emit dataChanged for column. + */ } int size() @@ -92,7 +168,7 @@ static int column_alignments[] = { TransactionTableModel::TransactionTableModel(QObject *parent): QAbstractTableModel(parent), - priv(new TransactionTablePriv()) + priv(new TransactionTablePriv(this)) { columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); @@ -127,13 +203,7 @@ void TransactionTableModel::update() if(!updated.empty()) { - /* TODO: improve this, way too brute-force at the moment, - only update transactions that actually changed, and add/remove - transactions that were added/removed. - */ - beginResetModel(); priv->updateWallet(updated); - endResetModel(); } } -- cgit v1.2.3 From 2547f1f7e5dd6cb6397152047b67c4b2d4981c6b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 3 Jun 2011 21:03:20 +0200 Subject: create new address from main gui, move address book model to client model --- gui/include/addresstablemodel.h | 4 ++-- gui/include/clientmodel.h | 6 +++++- gui/include/editaddressdialog.h | 2 +- gui/src/addressbookdialog.cpp | 6 +++--- gui/src/addresstablemodel.cpp | 8 ++++---- gui/src/bitcoingui.cpp | 16 ++++++++++++++-- gui/src/clientmodel.cpp | 13 ++++++++++--- gui/src/editaddressdialog.cpp | 14 ++++++++++---- 8 files changed, 49 insertions(+), 20 deletions(-) diff --git a/gui/include/addresstablemodel.h b/gui/include/addresstablemodel.h index 1890260971..8799414334 100644 --- a/gui/include/addresstablemodel.h +++ b/gui/include/addresstablemodel.h @@ -35,9 +35,9 @@ public: bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex()); /* Add an address to the model. - Returns true on success, false otherwise. + Returns the added address on success, and an empty string otherwise. */ - bool addRow(const QString &type, const QString &label, const QString &address); + QString addRow(const QString &type, const QString &label, const QString &address); /* Update address list from core. Invalidates any indices. */ diff --git a/gui/include/clientmodel.h b/gui/include/clientmodel.h index 01c0d70fc7..d68b34fe96 100644 --- a/gui/include/clientmodel.h +++ b/gui/include/clientmodel.h @@ -2,7 +2,9 @@ #define CLIENTMODEL_H #include + class OptionsModel; +class AddressTableModel; class ClientModel : public QObject { @@ -22,6 +24,7 @@ public: }; OptionsModel *getOptionsModel(); + AddressTableModel *getAddressTableModel(); qint64 getBalance(); QString getAddress(); @@ -34,7 +37,8 @@ public: /* Send coins */ StatusCode sendCoins(const QString &payTo, qint64 payAmount); private: - OptionsModel *options_model; + OptionsModel *optionsModel; + AddressTableModel *addressTableModel; signals: void balanceChanged(qint64 balance); diff --git a/gui/include/editaddressdialog.h b/gui/include/editaddressdialog.h index dd7766951b..6f396d0457 100644 --- a/gui/include/editaddressdialog.h +++ b/gui/include/editaddressdialog.h @@ -29,7 +29,7 @@ public: void setModel(AddressTableModel *model); void loadRow(int row); - void saveCurrentRow(); + QString saveCurrentRow(); private: Ui::EditAddressDialog *ui; diff --git a/gui/src/addressbookdialog.cpp b/gui/src/addressbookdialog.cpp index 9b9e9bbc8e..35078d3aea 100644 --- a/gui/src/addressbookdialog.cpp +++ b/gui/src/addressbookdialog.cpp @@ -14,9 +14,6 @@ AddressBookDialog::AddressBookDialog(QWidget *parent) : model(0) { ui->setupUi(this); - - model = new AddressTableModel(this); - setModel(model); } AddressBookDialog::~AddressBookDialog() @@ -26,6 +23,9 @@ AddressBookDialog::~AddressBookDialog() void AddressBookDialog::setModel(AddressTableModel *model) { + /* Refresh list from core */ + model->updateList(); + /* Receive filter */ QSortFilterProxyModel *receive_model = new QSortFilterProxyModel(this); receive_model->setSourceModel(model); diff --git a/gui/src/addresstablemodel.cpp b/gui/src/addresstablemodel.cpp index fbb2eb528b..1cbc1b5d1d 100644 --- a/gui/src/addresstablemodel.cpp +++ b/gui/src/addresstablemodel.cpp @@ -191,7 +191,7 @@ void AddressTableModel::updateList() endResetModel(); } -bool AddressTableModel::addRow(const QString &type, const QString &label, const QString &address) +QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address) { std::string strLabel = label.toStdString(); std::string strAddress = address.toStdString(); @@ -203,7 +203,7 @@ bool AddressTableModel::addRow(const QString &type, const QString &label, const { if(mapAddressBook.count(strAddress)) { - return false; + return QString(); } } } else if(type == Receive) @@ -212,12 +212,12 @@ bool AddressTableModel::addRow(const QString &type, const QString &label, const strAddress = PubKeyToAddress(GetKeyFromKeyPool()); } else { - return false; + return QString(); } /* Add entry and update list */ SetAddressBookName(strAddress, strLabel); updateList(); - return true; + return QString::fromStdString(strAddress); } bool AddressTableModel::removeRows(int row, int count, const QModelIndex & parent) diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index ba5b1d99a8..5996496036 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -11,6 +11,7 @@ #include "aboutdialog.h" #include "clientmodel.h" #include "guiutil.h" +#include "editaddressdialog.h" #include "main.h" @@ -239,6 +240,7 @@ void BitcoinGUI::sendcoinsClicked() void BitcoinGUI::addressbookClicked() { AddressBookDialog dlg; + dlg.setModel(model->getAddressTableModel()); dlg.setTab(AddressBookDialog::SendingTab); dlg.exec(); } @@ -246,6 +248,7 @@ void BitcoinGUI::addressbookClicked() void BitcoinGUI::receivingAddressesClicked() { AddressBookDialog dlg; + dlg.setModel(model->getAddressTableModel()); dlg.setTab(AddressBookDialog::ReceivingTab); dlg.exec(); } @@ -265,8 +268,17 @@ void BitcoinGUI::aboutClicked() void BitcoinGUI::newAddressClicked() { - qDebug() << "New address clicked"; - /* TODO: generate new address */ + EditAddressDialog dlg(EditAddressDialog::NewReceivingAddress); + dlg.setModel(model->getAddressTableModel()); + if(dlg.exec()) + { + QString newAddress = dlg.saveCurrentRow(); + /* Set returned address as new default address */ + if(!newAddress.isEmpty()) + { + model->setAddress(newAddress); + } + } } void BitcoinGUI::copyClipboardClicked() diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp index 7dcbc576a4..497f8dc3d2 100644 --- a/gui/src/clientmodel.cpp +++ b/gui/src/clientmodel.cpp @@ -2,11 +2,12 @@ #include "main.h" #include "guiconstants.h" #include "optionsmodel.h" +#include "addresstablemodel.h" #include ClientModel::ClientModel(QObject *parent) : - QObject(parent), options_model(0) + QObject(parent), optionsModel(0), addressTableModel(0) { /* Until we build signal notifications into the bitcoin core, simply update everything using a timer. @@ -15,7 +16,8 @@ ClientModel::ClientModel(QObject *parent) : connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(MODEL_UPDATE_DELAY); - options_model = new OptionsModel(this); + optionsModel = new OptionsModel(this); + addressTableModel = new AddressTableModel(this); } qint64 ClientModel::getBalance() @@ -128,5 +130,10 @@ ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payA OptionsModel *ClientModel::getOptionsModel() { - return options_model; + return optionsModel; +} + +AddressTableModel *ClientModel::getAddressTableModel() +{ + return addressTableModel; } diff --git a/gui/src/editaddressdialog.cpp b/gui/src/editaddressdialog.cpp index ddc7292cc6..dd0541760b 100644 --- a/gui/src/editaddressdialog.cpp +++ b/gui/src/editaddressdialog.cpp @@ -54,16 +54,18 @@ void EditAddressDialog::loadRow(int row) mapper->setCurrentIndex(row); } -void EditAddressDialog::saveCurrentRow() +QString EditAddressDialog::saveCurrentRow() { + QString address; switch(mode) { case NewReceivingAddress: case NewSendingAddress: - if(!model->addRow( + address = model->addRow( mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive, ui->labelEdit->text(), - ui->addressEdit->text())) + ui->addressEdit->text()); + if(address.isEmpty()) { QMessageBox::warning(this, windowTitle(), tr("The address %1 is already in the address book.").arg(ui->addressEdit->text()), @@ -72,7 +74,11 @@ void EditAddressDialog::saveCurrentRow() break; case EditReceivingAddress: case EditSendingAddress: - mapper->submit(); + if(mapper->submit()) + { + address = ui->addressEdit->text(); + } break; } + return address; } -- cgit v1.2.3 From 64bca50d548f641aad463374963883e9c59d2d83 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 4 Jun 2011 21:41:31 +0200 Subject: update transaction status as new blocks come in --- gui/include/transactionrecord.h | 26 +++++-- gui/src/transactionrecord.cpp | 140 +++++++++++++++++++++----------------- gui/src/transactiontablemodel.cpp | 26 ++++++- 3 files changed, 124 insertions(+), 68 deletions(-) diff --git a/gui/include/transactionrecord.h b/gui/include/transactionrecord.h index d8aca76129..c082fffe9c 100644 --- a/gui/include/transactionrecord.h +++ b/gui/include/transactionrecord.h @@ -10,7 +10,7 @@ class TransactionStatus public: TransactionStatus(): confirmed(false), sortKey(""), maturity(Mature), - matures_in(0), status(Offline), depth(0), open_for(0) + matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1) { } enum Maturity @@ -40,6 +40,9 @@ public: Status status; int64 depth; int64 open_for; /* Timestamp if status==OpenUntilDate, otherwise number of blocks */ + + /* Current number of blocks (to know whether cached status is still valid. */ + int cur_num_blocks; }; class TransactionRecord @@ -57,21 +60,21 @@ public: }; TransactionRecord(): - hash(), time(0), type(Other), address(""), debit(0), credit(0) + hash(), time(0), type(Other), address(""), debit(0), credit(0), idx(0) { } - TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status): + TransactionRecord(uint256 hash, int64 time): hash(hash), time(time), type(Other), address(""), debit(0), - credit(0), status(status) + credit(0), idx(0) { } - TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status, + TransactionRecord(uint256 hash, int64 time, Type type, const std::string &address, int64 debit, int64 credit): hash(hash), time(time), type(type), address(address), debit(debit), credit(credit), - status(status) + idx(0) { } @@ -88,8 +91,19 @@ public: int64 debit; int64 credit; + /* Subtransaction index, for sort key */ + int idx; + /* Status: can change with block chain update */ TransactionStatus status; + + /* Update status from wallet tx. + */ + void updateStatus(const CWalletTx &wtx); + + /* Is a status update needed? + */ + bool statusUpdateNeeded(); }; #endif // TRANSACTIONRECORD_H diff --git a/gui/src/transactionrecord.cpp b/gui/src/transactionrecord.cpp index 9feca4434b..83c7d63515 100644 --- a/gui/src/transactionrecord.cpp +++ b/gui/src/transactionrecord.cpp @@ -38,47 +38,6 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx uint256 hash = wtx.GetHash(); std::map mapValue = wtx.mapValue; - // Find the block the tx is in - CBlockIndex* pindex = NULL; - std::map::iterator mi = mapBlockIndex.find(wtx.hashBlock); - if (mi != mapBlockIndex.end()) - pindex = (*mi).second; - - // Determine transaction status - TransactionStatus status; - // Sort order, unrecorded transactions sort to the top - status.sortKey = strprintf("%010d-%01d-%010u", - (pindex ? pindex->nHeight : INT_MAX), - (wtx.IsCoinBase() ? 1 : 0), - wtx.nTimeReceived); - status.confirmed = wtx.IsConfirmed(); - status.depth = wtx.GetDepthInMainChain(); - - if (!wtx.IsFinal()) - { - if (wtx.nLockTime < 500000000) - { - status.status = TransactionStatus::OpenUntilBlock; - status.open_for = nBestHeight - wtx.nLockTime; - } else { - status.status = TransactionStatus::OpenUntilDate; - status.open_for = wtx.nLockTime; - } - } - else - { - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) - { - status.status = TransactionStatus::Offline; - } else if (status.depth < 6) - { - status.status = TransactionStatus::Unconfirmed; - } else - { - status.status = TransactionStatus::HaveConfirmations; - } - } - if (showTransaction(wtx)) { if (nNet > 0 || wtx.IsCoinBase()) @@ -86,7 +45,7 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx // // Credit // - TransactionRecord sub(hash, nTime, status); + TransactionRecord sub(hash, nTime); sub.credit = nNet; @@ -97,25 +56,10 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx if (nCredit == 0) { - sub.status.maturity = TransactionStatus::Immature; - int64 nUnmatured = 0; BOOST_FOREACH(const CTxOut& txout, wtx.vout) nUnmatured += txout.GetCredit(); sub.credit = nUnmatured; - - if (wtx.IsInMainChain()) - { - sub.status.matures_in = wtx.GetBlocksToMaturity(); - - // Check if the block was requested by anyone - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) - sub.status.maturity = TransactionStatus::MaturesWarning; - } - else - { - sub.status.maturity = TransactionStatus::NotAccepted; - } } } else if (!mapValue["from"].empty() || !mapValue["message"].empty()) @@ -159,7 +103,7 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx // Payment to self int64 nChange = wtx.GetChange(); - parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::SendToSelf, "", + parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", -(nDebit - nChange), nCredit - nChange)); } else if (fAllFromMe) @@ -172,7 +116,8 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx for (int nOut = 0; nOut < wtx.vout.size(); nOut++) { const CTxOut& txout = wtx.vout[nOut]; - TransactionRecord sub(hash, nTime, status); + TransactionRecord sub(hash, nTime); + sub.idx = parts.size(); if (txout.IsMine()) { @@ -200,7 +145,6 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx nTxFee = 0; } sub.debit = -nValue; - sub.status.sortKey += strprintf("-%d", nOut); parts.append(sub); } @@ -214,10 +158,84 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx BOOST_FOREACH(const CTxIn& txin, wtx.vin) fAllMine = fAllMine && txin.IsMine(); - parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::Other, "", nNet, 0)); + parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0)); } } } return parts; } + +void TransactionRecord::updateStatus(const CWalletTx &wtx) +{ + // Determine transaction status + + // Find the block the tx is in + CBlockIndex* pindex = NULL; + std::map::iterator mi = mapBlockIndex.find(wtx.hashBlock); + if (mi != mapBlockIndex.end()) + pindex = (*mi).second; + + // Sort order, unrecorded transactions sort to the top + status.sortKey = strprintf("%010d-%01d-%010u-%03d", + (pindex ? pindex->nHeight : INT_MAX), + (wtx.IsCoinBase() ? 1 : 0), + wtx.nTimeReceived, + idx); + status.confirmed = wtx.IsConfirmed(); + status.depth = wtx.GetDepthInMainChain(); + status.cur_num_blocks = nBestHeight; + + if (!wtx.IsFinal()) + { + if (wtx.nLockTime < 500000000) + { + status.status = TransactionStatus::OpenUntilBlock; + status.open_for = nBestHeight - wtx.nLockTime; + } else { + status.status = TransactionStatus::OpenUntilDate; + status.open_for = wtx.nLockTime; + } + } + else + { + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + { + status.status = TransactionStatus::Offline; + } else if (status.depth < 6) + { + status.status = TransactionStatus::Unconfirmed; + } else + { + status.status = TransactionStatus::HaveConfirmations; + } + } + + // For generated transactions, determine maturity + if(type == TransactionRecord::Generated) + { + int64 nCredit = wtx.GetCredit(true); + if (nCredit == 0) + { + status.maturity = TransactionStatus::Immature; + + if (wtx.IsInMainChain()) + { + status.matures_in = wtx.GetBlocksToMaturity(); + + // Check if the block was requested by anyone + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + status.maturity = TransactionStatus::MaturesWarning; + } else { + status.maturity = TransactionStatus::NotAccepted; + } + } else { + status.maturity = TransactionStatus::Mature; + } + } +} + +bool TransactionRecord::statusUpdateNeeded() +{ + return status.cur_num_blocks != nBestHeight; +} diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index 5bea3b598b..0d50e6e2df 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -148,7 +148,25 @@ struct TransactionTablePriv { if(idx >= 0 && idx < cachedWallet.size()) { - return &cachedWallet[idx]; + TransactionRecord *rec = &cachedWallet[idx]; + + /* If a status update is needed (blocks came in since last check), + update the status of this transaction from the wallet. Otherwise, + simply re-use the cached status. + */ + if(rec->statusUpdateNeeded()) + { + CRITICAL_BLOCK(cs_mapWallet) + { + std::map::iterator mi = mapWallet.find(rec->hash); + + if(mi != mapWallet.end()) + { + rec->updateStatus(mi->second); + } + } + } + return rec; } else { return 0; } @@ -204,6 +222,12 @@ void TransactionTableModel::update() if(!updated.empty()) { priv->updateWallet(updated); + + /* Status (number of confirmations) and (possibly) description + columns changed for all rows. + */ + emit dataChanged(index(0, Status), index(priv->size()-1, Status)); + emit dataChanged(index(0, Description), index(priv->size()-1, Description)); } } -- cgit v1.2.3 From b9e41844c0344856e0778ee78e7b2a2d397a8268 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 4 Jun 2011 21:54:49 +0200 Subject: fix "send to" address book --- gui/src/addressbookdialog.cpp | 1 + gui/src/sendcoinsdialog.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/gui/src/addressbookdialog.cpp b/gui/src/addressbookdialog.cpp index 35078d3aea..0228eff461 100644 --- a/gui/src/addressbookdialog.cpp +++ b/gui/src/addressbookdialog.cpp @@ -23,6 +23,7 @@ AddressBookDialog::~AddressBookDialog() void AddressBookDialog::setModel(AddressTableModel *model) { + this->model = model; /* Refresh list from core */ model->updateList(); diff --git a/gui/src/sendcoinsdialog.cpp b/gui/src/sendcoinsdialog.cpp index b50ed99099..721ab14108 100644 --- a/gui/src/sendcoinsdialog.cpp +++ b/gui/src/sendcoinsdialog.cpp @@ -101,6 +101,8 @@ void SendCoinsDialog::on_pasteButton_clicked() void SendCoinsDialog::on_addressBookButton_clicked() { AddressBookDialog dlg; + dlg.setModel(model->getAddressTableModel()); + dlg.setTab(AddressBookDialog::SendingTab); dlg.exec(); ui->payTo->setText(dlg.getReturnValue()); } -- cgit v1.2.3 From 75ff9d841b59bb7c3114a65ec0e59a323143c85d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 4 Jun 2011 22:02:30 +0200 Subject: update most importent TODOs in readme --- README.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 715296431e..e3d6bd75e6 100644 --- a/README.rst +++ b/README.rst @@ -34,5 +34,8 @@ This has to be done: - Build on Windows -- Details dialog for transactions (on double click) +- Show details dialog for transactions (on double click) +- Display error messages/alerts from core + +- More thorough testing of the view with all the kinds of transactions (sendmany, generation) -- cgit v1.2.3 From a4b4cc290c4f75783014eb454f9862ddaf6edbf1 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 5 Jun 2011 11:04:14 +0200 Subject: comment update --- gui/src/clientmodel.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp index 497f8dc3d2..5f3517abdb 100644 --- a/gui/src/clientmodel.cpp +++ b/gui/src/clientmodel.cpp @@ -9,8 +9,8 @@ ClientModel::ClientModel(QObject *parent) : QObject(parent), optionsModel(0), addressTableModel(0) { - /* Until we build signal notifications into the bitcoin core, - simply update everything using a timer. + /* Until signal notifications is built into the bitcoin core, + simply update everything after polling using a timer. */ QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); @@ -58,6 +58,9 @@ int ClientModel::getNumTransactions() void ClientModel::update() { + /* Plainly emit all signals for now. To be precise this should check + wether the values actually changed first. + */ emit balanceChanged(getBalance()); emit addressChanged(getAddress()); emit numConnectionsChanged(getNumConnections()); -- cgit v1.2.3 From afacb3406d6e2ad24d0b45c56a4f5137302e1102 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 5 Jun 2011 11:04:14 +0200 Subject: comment update --- README.rst | 2 +- gui/src/clientmodel.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index e3d6bd75e6..eb209ce051 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ This has been implemented: - GUI only functionality (copy to clipboard, select address, address/transaction filter proxys) -- Bitcoin core is made compatible with Qt4, and linked against +- Bitcoin core is made compatible with Qt4 - Send coins dialog: address and input validation diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp index 497f8dc3d2..5f3517abdb 100644 --- a/gui/src/clientmodel.cpp +++ b/gui/src/clientmodel.cpp @@ -9,8 +9,8 @@ ClientModel::ClientModel(QObject *parent) : QObject(parent), optionsModel(0), addressTableModel(0) { - /* Until we build signal notifications into the bitcoin core, - simply update everything using a timer. + /* Until signal notifications is built into the bitcoin core, + simply update everything after polling using a timer. */ QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); @@ -58,6 +58,9 @@ int ClientModel::getNumTransactions() void ClientModel::update() { + /* Plainly emit all signals for now. To be precise this should check + wether the values actually changed first. + */ emit balanceChanged(getBalance()); emit addressChanged(getAddress()); emit numConnectionsChanged(getNumConnections()); -- cgit v1.2.3 From 4663e339b828fe955761de317528b9421cbdd6f4 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 5 Jun 2011 11:14:23 +0200 Subject: show actual version nr in about dialog --- gui/src/aboutdialog.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gui/src/aboutdialog.cpp b/gui/src/aboutdialog.cpp index 3d7a3f98af..13347961dd 100644 --- a/gui/src/aboutdialog.cpp +++ b/gui/src/aboutdialog.cpp @@ -1,11 +1,14 @@ #include "aboutdialog.h" #include "ui_aboutdialog.h" +#include "util.h" + AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDialog) { ui->setupUi(this); + ui->versionLabel->setText(QString::fromStdString(FormatFullVersion())); } AboutDialog::~AboutDialog() -- cgit v1.2.3 From e29b623db36e9c63b1d0796928129a5fad858f0e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 5 Jun 2011 11:45:42 +0200 Subject: save changed options in database --- README.rst | 4 ++-- gui/src/optionsmodel.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index eb209ce051..70d3c41860 100644 --- a/README.rst +++ b/README.rst @@ -20,12 +20,12 @@ This has been implemented: - Address book and transactions views and models +- Options dialog + - Sending coins This has to be done: -- Settings are not remembered between invocations yet - - Minimize to tray / Minimize on close - Start at system start diff --git a/gui/src/optionsmodel.cpp b/gui/src/optionsmodel.cpp index f653f67e53..98fd3b110d 100644 --- a/gui/src/optionsmodel.cpp +++ b/gui/src/optionsmodel.cpp @@ -1,5 +1,6 @@ #include "optionsmodel.h" #include "main.h" +#include "net.h" #include @@ -47,6 +48,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in bool successful = true; /* set to false on parse error */ if(role == Qt::EditRole) { + CWalletDB walletdb; switch(index.row()) { case StartAtStartup: @@ -54,15 +56,22 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in break; case MinimizeToTray: fMinimizeToTray = value.toBool(); + walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray); break; case MapPortUPnP: fUseUPnP = value.toBool(); + walletdb.WriteSetting("fUseUPnP", fUseUPnP); +#ifdef USE_UPNP + MapPort(fUseUPnP); +#endif break; case MinimizeOnClose: fMinimizeOnClose = value.toBool(); + walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose); break; case ConnectSOCKS4: fUseProxy = value.toBool(); + walletdb.WriteSetting("fUseProxy", fUseProxy); break; case ProxyIP: { @@ -71,6 +80,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in if (addr.ip != INADDR_NONE) { addrProxy.ip = addr.ip; + walletdb.WriteSetting("addrProxy", addrProxy); } else { successful = false; } @@ -82,6 +92,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in if (nPort > 0 && nPort < USHRT_MAX) { addrProxy.port = htons(nPort); + walletdb.WriteSetting("addrProxy", addrProxy); } else { successful = false; } @@ -92,6 +103,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in if(ParseMoney(value.toString().toStdString(), retval)) { nTransactionFee = retval; + walletdb.WriteSetting("nTransactionFee", nTransactionFee); } else { successful = false; /* parse error */ } -- cgit v1.2.3 From cddc003e70578bbc5537eb7944d49c8d721f0fb4 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 5 Jun 2011 12:43:18 +0200 Subject: Disable map upnp option if built without USE_UPNP --- gui/src/optionsdialog.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gui/src/optionsdialog.cpp b/gui/src/optionsdialog.cpp index 8e7f403a7e..1ec777c453 100644 --- a/gui/src/optionsdialog.cpp +++ b/gui/src/optionsdialog.cpp @@ -207,6 +207,10 @@ MainOptionsPage::MainOptionsPage(QWidget *parent): connect(connect_socks4, SIGNAL(toggled(bool)), proxy_ip, SLOT(setEnabled(bool))); connect(connect_socks4, SIGNAL(toggled(bool)), proxy_port, SLOT(setEnabled(bool))); + +#ifndef USE_UPNP + map_port_upnp->setDisabled(true); +#endif } void MainOptionsPage::setMapper(MonitoredDataMapper *mapper) -- cgit v1.2.3 From 352083cb2303002233fcb6dd740ff74d4e0f0240 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 5 Jun 2011 14:19:57 +0200 Subject: Implement Minimize to tray / Minimize on close --- README.rst | 2 -- gui/include/bitcoingui.h | 11 +++++-- gui/include/optionsmodel.h | 7 ++++- gui/src/bitcoin.cpp | 1 + gui/src/bitcoingui.cpp | 78 +++++++++++++++++++++++++++++++++++----------- gui/src/optionsmodel.cpp | 4 +-- 6 files changed, 77 insertions(+), 26 deletions(-) diff --git a/README.rst b/README.rst index 70d3c41860..bb14be60ed 100644 --- a/README.rst +++ b/README.rst @@ -26,8 +26,6 @@ This has been implemented: This has to be done: -- Minimize to tray / Minimize on close - - Start at system start - Internationalization (convert WX language files) diff --git a/gui/include/bitcoingui.h b/gui/include/bitcoingui.h index 955d7b4781..8b45dace0e 100644 --- a/gui/include/bitcoingui.h +++ b/gui/include/bitcoingui.h @@ -27,6 +27,11 @@ public: Sent = 2, Received = 3 } TabIndex; + +protected: + void changeEvent(QEvent *e); + void closeEvent(QCloseEvent *event); + private: TransactionTableModel *transaction_model; ClientModel *model; @@ -41,9 +46,9 @@ private: QAction *sendcoins; QAction *addressbook; QAction *about; - QAction *receiving_addresses; + QAction *receivingAddresses; QAction *options; - QAction *openBitCoin; + QAction *openBitcoin; QSystemTrayIcon *trayIcon; @@ -64,9 +69,9 @@ private slots: void optionsClicked(); void receivingAddressesClicked(); void aboutClicked(); - void newAddressClicked(); void copyClipboardClicked(); + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); void error(const QString &title, const QString &message); }; diff --git a/gui/include/optionsmodel.h b/gui/include/optionsmodel.h index 4fb6d25145..0124e2ab47 100644 --- a/gui/include/optionsmodel.h +++ b/gui/include/optionsmodel.h @@ -3,7 +3,12 @@ #include -/* Interface from QT to configuration data structure for bitcoin client */ +/* Interface from QT to configuration data structure for bitcoin client. + To QT, the options are presented as a list with the different options + laid out vertically. + This can be changed to a tree once the settings become sufficiently + complex. + */ class OptionsModel : public QAbstractListModel { Q_OBJECT diff --git a/gui/src/bitcoin.cpp b/gui/src/bitcoin.cpp index c843cc406e..663590d01c 100644 --- a/gui/src/bitcoin.cpp +++ b/gui/src/bitcoin.cpp @@ -11,6 +11,7 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); + app.setQuitOnLastWindowClosed(false); try { if(AppInit2(argc, argv)) diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index 5996496036..b2aee6b311 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -12,6 +12,7 @@ #include "clientmodel.h" #include "guiutil.h" #include "editaddressdialog.h" +#include "optionsmodel.h" #include "main.h" @@ -48,26 +49,26 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): createActions(); - /* Menus */ + // Menus QMenu *file = menuBar()->addMenu("&File"); file->addAction(sendcoins); file->addSeparator(); file->addAction(quit); QMenu *settings = menuBar()->addMenu("&Settings"); - settings->addAction(receiving_addresses); + settings->addAction(receivingAddresses); settings->addAction(options); QMenu *help = menuBar()->addMenu("&Help"); help->addAction(about); - /* Toolbar */ + // Toolbar QToolBar *toolbar = addToolBar("Main toolbar"); toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolbar->addAction(sendcoins); toolbar->addAction(addressbook); - /* Address:
: New... : Paste to clipboard */ + // Address:
: New... : Paste to clipboard QHBoxLayout *hbox_address = new QHBoxLayout(); hbox_address->addWidget(new QLabel(tr("Your Bitcoin Address:"))); address = new QLineEdit(); @@ -80,7 +81,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): hbox_address->addWidget(button_new); hbox_address->addWidget(button_clipboard); - /* Balance: */ + // Balance: QHBoxLayout *hbox_balance = new QHBoxLayout(); hbox_balance->addWidget(new QLabel(tr("Balance:"))); hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ @@ -93,16 +94,15 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QVBoxLayout *vbox = new QVBoxLayout(); vbox->addLayout(hbox_address); vbox->addLayout(hbox_balance); - - transaction_model = new TransactionTableModel(this); + transaction_model = new TransactionTableModel(this); vbox->addWidget(createTabs()); QWidget *centralwidget = new QWidget(this); centralwidget->setLayout(vbox); setCentralWidget(centralwidget); - /* Create status bar */ + // Create status bar statusBar(); labelConnections = new QLabel(); @@ -121,7 +121,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): statusBar()->addPermanentWidget(labelBlocks); statusBar()->addPermanentWidget(labelTransactions); - /* Action bindings */ + // Action bindings connect(button_new, SIGNAL(clicked()), this, SLOT(newAddressClicked())); connect(button_clipboard, SIGNAL(clicked()), this, SLOT(copyClipboardClicked())); @@ -134,22 +134,24 @@ void BitcoinGUI::createActions() sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); - receiving_addresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); + receivingAddresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); - openBitCoin = new QAction(QIcon(":/icons/bitcoin"), "Open Bitcoin", this); + openBitcoin = new QAction(QIcon(":/icons/bitcoin"), "Open &Bitcoin", this); connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked())); connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked())); - connect(receiving_addresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked())); + connect(receivingAddresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked())); connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); + connect(openBitcoin, SIGNAL(triggered()), this, SLOT(show())); } void BitcoinGUI::setModel(ClientModel *model) { this->model = model; + // Keep up to date with client setBalance(model->getBalance()); connect(model, SIGNAL(balanceChanged(qint64)), this, SLOT(setBalance(qint64))); @@ -165,14 +167,14 @@ void BitcoinGUI::setModel(ClientModel *model) setAddress(model->getAddress()); connect(model, SIGNAL(addressChanged(QString)), this, SLOT(setAddress(QString))); - /* Report errors from network/worker thread */ - connect(model, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); + // Report errors from network/worker thread + connect(model, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); } void BitcoinGUI::createTrayIcon() { QMenu *trayIconMenu = new QMenu(this); - trayIconMenu->addAction(openBitCoin); + trayIconMenu->addAction(openBitcoin); trayIconMenu->addAction(sendcoins); trayIconMenu->addAction(options); trayIconMenu->addSeparator(); @@ -181,9 +183,20 @@ void BitcoinGUI::createTrayIcon() trayIcon = new QSystemTrayIcon(this); trayIcon->setContextMenu(trayIconMenu); trayIcon->setIcon(QIcon(":/icons/toolbar")); + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); trayIcon->show(); } +void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if(reason == QSystemTrayIcon::DoubleClick) + { + // Doubleclick on system tray icon triggers "open bitcoin" + openBitcoin->trigger(); + } +} + QWidget *BitcoinGUI::createTabs() { QStringList tab_filters, tab_labels; @@ -235,6 +248,7 @@ void BitcoinGUI::sendcoinsClicked() SendCoinsDialog dlg; dlg.setModel(model); dlg.exec(); + qDebug() << "After close"; } void BitcoinGUI::addressbookClicked() @@ -273,7 +287,7 @@ void BitcoinGUI::newAddressClicked() if(dlg.exec()) { QString newAddress = dlg.saveCurrentRow(); - /* Set returned address as new default address */ + // Set returned address as new default addres if(!newAddress.isEmpty()) { model->setAddress(newAddress); @@ -283,7 +297,7 @@ void BitcoinGUI::newAddressClicked() void BitcoinGUI::copyClipboardClicked() { - /* Copy text in address to clipboard */ + // Copy text in address to clipboard QApplication::clipboard()->setText(address->text()); } @@ -314,8 +328,36 @@ void BitcoinGUI::setNumTransactions(int count) void BitcoinGUI::error(const QString &title, const QString &message) { - /* Report errors from network/worker thread */ + // Report errors from network/worker thread QMessageBox::critical(this, title, message, QMessageBox::Ok, QMessageBox::Ok); } + +void BitcoinGUI::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::WindowStateChange) + { + if(model->getOptionsModel()->getMinimizeToTray()) + { + if (isMinimized()) + { + hide(); + e->ignore(); + } else { + e->accept(); + } + } + } + QMainWindow::changeEvent(e); +} + +void BitcoinGUI::closeEvent(QCloseEvent *event) +{ + if(!model->getOptionsModel()->getMinimizeToTray() && + !model->getOptionsModel()->getMinimizeOnClose()) + { + qApp->quit(); + } + QMainWindow::closeEvent(event); +} diff --git a/gui/src/optionsmodel.cpp b/gui/src/optionsmodel.cpp index 98fd3b110d..37d5cb1580 100644 --- a/gui/src/optionsmodel.cpp +++ b/gui/src/optionsmodel.cpp @@ -123,12 +123,12 @@ qint64 OptionsModel::getTransactionFee() return nTransactionFee; } -bool getMinimizeToTray() +bool OptionsModel::getMinimizeToTray() { return fMinimizeToTray; } -bool getMinimizeOnClose() +bool OptionsModel::getMinimizeOnClose() { return fMinimizeOnClose; } -- cgit v1.2.3 From 467c31ea0a76860f0c3357670c8525f2d950e8d6 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 5 Jun 2011 16:03:29 +0200 Subject: show messages from core/net thread --- bitcoin.pro | 3 ++- core/include/externui.h | 45 ++++++++++++++++++++++++++++++++ core/include/headers.h | 2 +- gui/include/bitcoingui.h | 7 +++-- gui/include/clientmodel.h | 3 +++ gui/src/bitcoin.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++++++- gui/src/bitcoingui.cpp | 49 ++++++++++++++++++++++++----------- gui/src/clientmodel.cpp | 10 +++++++- 8 files changed, 163 insertions(+), 21 deletions(-) create mode 100644 core/include/externui.h diff --git a/bitcoin.pro b/bitcoin.pro index e2f282a3f6..9a3570aa58 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -62,7 +62,8 @@ HEADERS += gui/include/bitcoingui.h \ gui/include/transactionrecord.h \ gui/include/guiconstants.h \ gui/include/optionsmodel.h \ - gui/include/monitoreddatamapper.h + gui/include/monitoreddatamapper.h \ + core/include/externui.h SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/transactiontablemodel.cpp \ gui/src/addresstablemodel.cpp \ diff --git a/core/include/externui.h b/core/include/externui.h new file mode 100644 index 0000000000..e58ccc2242 --- /dev/null +++ b/core/include/externui.h @@ -0,0 +1,45 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_EXTERNUI_H +#define BITCOIN_EXTERNUI_H + +#include + +typedef void wxWindow; +#define wxYES 0x00000002 +#define wxOK 0x00000004 +#define wxNO 0x00000008 +#define wxYES_NO (wxYES|wxNO) +#define wxCANCEL 0x00000010 +#define wxAPPLY 0x00000020 +#define wxCLOSE 0x00000040 +#define wxOK_DEFAULT 0x00000000 +#define wxYES_DEFAULT 0x00000000 +#define wxNO_DEFAULT 0x00000080 +#define wxCANCEL_DEFAULT 0x80000000 +#define wxICON_EXCLAMATION 0x00000100 +#define wxICON_HAND 0x00000200 +#define wxICON_WARNING wxICON_EXCLAMATION +#define wxICON_ERROR wxICON_HAND +#define wxICON_QUESTION 0x00000400 +#define wxICON_INFORMATION 0x00000800 +#define wxICON_STOP wxICON_HAND +#define wxICON_ASTERISK wxICON_INFORMATION +#define wxICON_MASK (0x00000100|0x00000200|0x00000400|0x00000800) +#define wxFORWARD 0x00001000 +#define wxBACKWARD 0x00002000 +#define wxRESET 0x00004000 +#define wxHELP 0x00008000 +#define wxMORE 0x00010000 +#define wxSETUP 0x00020000 + +extern int MyMessageBox(const std::string& message, const std::string& caption="Message", int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1); +#define wxMessageBox MyMessageBox +extern int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1); +extern bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent); +extern void CalledSetStatusBar(const std::string& strText, int nField); +extern void UIThreadCall(boost::function0 fn); +extern void MainFrameRepaint(); + +#endif diff --git a/core/include/headers.h b/core/include/headers.h index d40c5ed0a9..33aeef3383 100644 --- a/core/include/headers.h +++ b/core/include/headers.h @@ -127,7 +127,7 @@ #include "uibase.h" #include "ui.h" #else -#include "noui.h" +#include "externui.h" #endif #include "init.h" diff --git a/gui/include/bitcoingui.h b/gui/include/bitcoingui.h index 8b45dace0e..c2c786b28f 100644 --- a/gui/include/bitcoingui.h +++ b/gui/include/bitcoingui.h @@ -11,6 +11,8 @@ class ClientModel; QT_BEGIN_NAMESPACE class QLabel; class QLineEdit; +class QTableView; +class QAbstractItemModel; QT_END_NAMESPACE class BitcoinGUI : public QMainWindow @@ -33,7 +35,6 @@ protected: void closeEvent(QCloseEvent *event); private: - TransactionTableModel *transaction_model; ClientModel *model; QLineEdit *address; @@ -51,10 +52,12 @@ private: QAction *openBitcoin; QSystemTrayIcon *trayIcon; + QList transactionViews; void createActions(); QWidget *createTabs(); void createTrayIcon(); + void setTabsModel(QAbstractItemModel *transaction_model); public slots: void setBalance(qint64 balance); @@ -62,6 +65,7 @@ public slots: void setNumConnections(int count); void setNumBlocks(int count); void setNumTransactions(int count); + void error(const QString &title, const QString &message); private slots: void sendcoinsClicked(); @@ -72,7 +76,6 @@ private slots: void newAddressClicked(); void copyClipboardClicked(); void trayIconActivated(QSystemTrayIcon::ActivationReason reason); - void error(const QString &title, const QString &message); }; #endif diff --git a/gui/include/clientmodel.h b/gui/include/clientmodel.h index d68b34fe96..09d1fc921e 100644 --- a/gui/include/clientmodel.h +++ b/gui/include/clientmodel.h @@ -5,6 +5,7 @@ class OptionsModel; class AddressTableModel; +class TransactionTableModel; class ClientModel : public QObject { @@ -25,6 +26,7 @@ public: OptionsModel *getOptionsModel(); AddressTableModel *getAddressTableModel(); + TransactionTableModel *getTransactionTableModel(); qint64 getBalance(); QString getAddress(); @@ -39,6 +41,7 @@ public: private: OptionsModel *optionsModel; AddressTableModel *addressTableModel; + TransactionTableModel *transactionTableModel; signals: void balanceChanged(qint64 balance); diff --git a/gui/src/bitcoin.cpp b/gui/src/bitcoin.cpp index 663590d01c..dc3e8070bb 100644 --- a/gui/src/bitcoin.cpp +++ b/gui/src/bitcoin.cpp @@ -5,22 +5,85 @@ #include "clientmodel.h" #include "util.h" #include "init.h" +#include "externui.h" #include +#include + +// Need a global reference to process net thread +BitcoinGUI *guiref; + +int MyMessageBox(const std::string& message, const std::string& caption, int style, wxWindow* parent, int x, int y) +{ + // Message from main thread + printf("MyMessageBox\n"); + if(guiref) + { + guiref->error(QString::fromStdString(caption), + QString::fromStdString(message)); + } + else + { + QMessageBox::critical(0, QString::fromStdString(caption), + QString::fromStdString(message), + QMessageBox::Ok, QMessageBox::Ok); + } + return 4; +} + +int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style, wxWindow* parent, int x, int y) +{ + // Message from network thread + if(guiref) + { + QMetaObject::invokeMethod(guiref, "error", Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(caption)), + Q_ARG(QString, QString::fromStdString(message))); + } + else + { + printf("%s: %s\n", caption.c_str(), message.c_str()); + fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); + } + return 4; +} + +bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent) +{ + // Query from network thread + // TODO + return true; +} + +void CalledSetStatusBar(const std::string& strText, int nField) +{ + // Only used for built-in mining, which is disabled, simple ignore +} + +void UIThreadCall(boost::function0 fn) +{ + // Only used for built-in mining, which is disabled, simple ignore +} + +void MainFrameRepaint() +{ +} int main(int argc, char *argv[]) { QApplication app(argc, argv); app.setQuitOnLastWindowClosed(false); + BitcoinGUI window; + guiref = &window; try { if(AppInit2(argc, argv)) { ClientModel model; - BitcoinGUI window; window.setModel(&model); window.show(); + guiref = 0; /* Depending on settings: QApplication::setQuitOnLastWindowClosed(false); */ int retval = app.exec(); diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index b2aee6b311..b687204629 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -95,7 +95,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): vbox->addLayout(hbox_address); vbox->addLayout(hbox_balance); - transaction_model = new TransactionTableModel(this); vbox->addWidget(createTabs()); QWidget *centralwidget = new QWidget(this); @@ -169,6 +168,9 @@ void BitcoinGUI::setModel(ClientModel *model) // Report errors from network/worker thread connect(model, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); + + // Put transaction list in tabs + setTabsModel(model->getTransactionTableModel()); } void BitcoinGUI::createTrayIcon() @@ -199,18 +201,32 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) QWidget *BitcoinGUI::createTabs() { - QStringList tab_filters, tab_labels; - tab_filters << "^." - << "^["+TransactionTableModel::Sent+TransactionTableModel::Received+"]" - << "^["+TransactionTableModel::Sent+"]" - << "^["+TransactionTableModel::Received+"]"; + QStringList tab_labels; tab_labels << tr("All transactions") << tr("Sent/Received") << tr("Sent") << tr("Received"); - QTabWidget *tabs = new QTabWidget(this); + QTabWidget *tabs = new QTabWidget(this); for(int i = 0; i < tab_labels.size(); ++i) + { + QTableView *view = new QTableView(this); + tabs->addTab(view, tab_labels.at(i)); + transactionViews.append(view); + } + + return tabs; +} + +void BitcoinGUI::setTabsModel(QAbstractItemModel *transaction_model) +{ + QStringList tab_filters; + tab_filters << "^." + << "^["+TransactionTableModel::Sent+TransactionTableModel::Received+"]" + << "^["+TransactionTableModel::Sent+"]" + << "^["+TransactionTableModel::Received+"]"; + + for(int i = 0; i < transactionViews.size(); ++i) { QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this); proxy_model->setSourceModel(transaction_model); @@ -219,7 +235,7 @@ QWidget *BitcoinGUI::createTabs() proxy_model->setFilterRegExp(QRegExp(tab_filters.at(i))); proxy_model->setSortRole(Qt::EditRole); - QTableView *transaction_table = new QTableView(this); + QTableView *transaction_table = transactionViews.at(i); transaction_table->setModel(proxy_model); transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); @@ -237,10 +253,7 @@ QWidget *BitcoinGUI::createTabs() TransactionTableModel::Debit, 79); transaction_table->horizontalHeader()->resizeSection( TransactionTableModel::Credit, 79); - - tabs->addTab(transaction_table, tab_labels.at(i)); } - return tabs; } void BitcoinGUI::sendcoinsClicked() @@ -248,7 +261,6 @@ void BitcoinGUI::sendcoinsClicked() SendCoinsDialog dlg; dlg.setModel(model); dlg.exec(); - qDebug() << "After close"; } void BitcoinGUI::addressbookClicked() @@ -329,9 +341,16 @@ void BitcoinGUI::setNumTransactions(int count) void BitcoinGUI::error(const QString &title, const QString &message) { // Report errors from network/worker thread - QMessageBox::critical(this, title, - message, - QMessageBox::Ok, QMessageBox::Ok); + if(trayIcon->supportsMessages()) + { + // Show as "balloon" message if possible + trayIcon->showMessage(title, message, QSystemTrayIcon::Critical); + } else { + // Fall back to old fashioned popup dialog if not + QMessageBox::critical(this, title, + message, + QMessageBox::Ok, QMessageBox::Ok); + } } void BitcoinGUI::changeEvent(QEvent *e) diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp index 5f3517abdb..8fd3599e97 100644 --- a/gui/src/clientmodel.cpp +++ b/gui/src/clientmodel.cpp @@ -3,11 +3,13 @@ #include "guiconstants.h" #include "optionsmodel.h" #include "addresstablemodel.h" +#include "transactiontablemodel.h" #include ClientModel::ClientModel(QObject *parent) : - QObject(parent), optionsModel(0), addressTableModel(0) + QObject(parent), optionsModel(0), addressTableModel(0), + transactionTableModel(0) { /* Until signal notifications is built into the bitcoin core, simply update everything after polling using a timer. @@ -18,6 +20,7 @@ ClientModel::ClientModel(QObject *parent) : optionsModel = new OptionsModel(this); addressTableModel = new AddressTableModel(this); + transactionTableModel = new TransactionTableModel(this); } qint64 ClientModel::getBalance() @@ -140,3 +143,8 @@ AddressTableModel *ClientModel::getAddressTableModel() { return addressTableModel; } + +TransactionTableModel *ClientModel::getTransactionTableModel() +{ + return transactionTableModel; +} -- cgit v1.2.3 From 00b8acdf49894546c2875ad77cf748ec4c920403 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 5 Jun 2011 16:24:23 +0200 Subject: fix --- gui/src/bitcoin.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gui/src/bitcoin.cpp b/gui/src/bitcoin.cpp index dc3e8070bb..a1b74d8798 100644 --- a/gui/src/bitcoin.cpp +++ b/gui/src/bitcoin.cpp @@ -10,7 +10,7 @@ #include #include -// Need a global reference to process net thread +// Need a global reference for the notifications to find the GUI BitcoinGUI *guiref; int MyMessageBox(const std::string& message, const std::string& caption, int style, wxWindow* parent, int x, int y) @@ -83,11 +83,10 @@ int main(int argc, char *argv[]) window.setModel(&model); window.show(); - guiref = 0; - /* Depending on settings: QApplication::setQuitOnLastWindowClosed(false); */ int retval = app.exec(); + guiref = 0; Shutdown(NULL); return retval; -- cgit v1.2.3 From b7726d924ef7082b1f1d532ba6f840bc7b267b47 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 5 Jun 2011 17:36:52 +0200 Subject: ask fee --- README.rst | 6 +++--- gui/include/bitcoingui.h | 1 + gui/src/bitcoin.cpp | 30 ++++++++++++++++++++++++------ gui/src/bitcoingui.cpp | 12 ++++++++++++ 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index bb14be60ed..9856370bc9 100644 --- a/README.rst +++ b/README.rst @@ -22,7 +22,9 @@ This has been implemented: - Options dialog -- Sending coins +- Sending coins (including ask for fee when needed) + +- Show messages from core This has to be done: @@ -34,6 +36,4 @@ This has to be done: - Show details dialog for transactions (on double click) -- Display error messages/alerts from core - - More thorough testing of the view with all the kinds of transactions (sendmany, generation) diff --git a/gui/include/bitcoingui.h b/gui/include/bitcoingui.h index c2c786b28f..ab7b4bbdb6 100644 --- a/gui/include/bitcoingui.h +++ b/gui/include/bitcoingui.h @@ -66,6 +66,7 @@ public slots: void setNumBlocks(int count); void setNumTransactions(int count); void error(const QString &title, const QString &message); + void askFee(qint64 nFeeRequired, bool *payFee); private slots: void sendcoinsClicked(); diff --git a/gui/src/bitcoin.cpp b/gui/src/bitcoin.cpp index a1b74d8798..6dbcb6c299 100644 --- a/gui/src/bitcoin.cpp +++ b/gui/src/bitcoin.cpp @@ -5,10 +5,12 @@ #include "clientmodel.h" #include "util.h" #include "init.h" +#include "main.h" #include "externui.h" #include #include +#include // Need a global reference for the notifications to find the GUI BitcoinGUI *guiref; @@ -16,7 +18,6 @@ BitcoinGUI *guiref; int MyMessageBox(const std::string& message, const std::string& caption, int style, wxWindow* parent, int x, int y) { // Message from main thread - printf("MyMessageBox\n"); if(guiref) { guiref->error(QString::fromStdString(caption), @@ -50,9 +51,26 @@ int ThreadSafeMessageBox(const std::string& message, const std::string& caption, bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent) { - // Query from network thread - // TODO - return true; + if(!guiref) + return false; + if(nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon) + return true; + bool payFee = false; + + /* Call slot on GUI thread. + If called from another thread, use a blocking QueuedConnection. + */ + Qt::ConnectionType connectionType = Qt::DirectConnection; + if(QThread::currentThread() != QCoreApplication::instance()->thread()) + { + connectionType = Qt::BlockingQueuedConnection; + } + + QMetaObject::invokeMethod(guiref, "askFee", connectionType, + Q_ARG(qint64, nFeeRequired), + Q_ARG(bool*, &payFee)); + + return payFee; } void CalledSetStatusBar(const std::string& strText, int nField) @@ -73,13 +91,13 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); app.setQuitOnLastWindowClosed(false); - BitcoinGUI window; - guiref = &window; try { if(AppInit2(argc, argv)) { + BitcoinGUI window; ClientModel model; + guiref = &window; window.setModel(&model); window.show(); diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index b687204629..70da0b2393 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -380,3 +380,15 @@ void BitcoinGUI::closeEvent(QCloseEvent *event) } QMainWindow::closeEvent(event); } + +void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) +{ + QString strMessage = + tr("This transaction is over the size limit. You can still send it for a fee of %1, " + "which goes to the nodes that process your transaction and helps to support the network. " + "Do you want to pay the fee?").arg(QString::fromStdString(FormatMoney(nFeeRequired))); + QMessageBox::StandardButton retval = QMessageBox::question( + this, tr("Sending..."), strMessage, + QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); + *payFee = (retval == QMessageBox::Yes); +} -- cgit v1.2.3 From 822f2e3ddfcc98a7633f71665fa9d7879bc63115 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 5 Jun 2011 19:15:15 +0200 Subject: update to newest git bitcoin core --- core/include/main.h | 19 +++++++++++-------- core/include/net.h | 24 ++++++++++++++++++++---- core/include/serialize.h | 2 +- core/include/util.h | 2 +- core/src/init.cpp | 2 +- core/src/irc.cpp | 17 ++++++++++++----- core/src/main.cpp | 25 +++++++++++++++++-------- core/src/net.cpp | 20 ++++++++++---------- core/src/rpc.cpp | 22 ++++++++++++++++++---- core/src/util.cpp | 4 ++-- 10 files changed, 93 insertions(+), 44 deletions(-) diff --git a/core/include/main.h b/core/include/main.h index 92b73fe5ad..8d9b39f718 100644 --- a/core/include/main.h +++ b/core/include/main.h @@ -29,7 +29,8 @@ static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; static const int64 COIN = 100000000; static const int64 CENT = 1000000; -static const int64 MIN_TX_FEE = 50000; +static const int64 MIN_TX_FEE = CENT; +static const int64 MIN_RELAY_TX_FEE = 50000; static const int64 MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } static const int COINBASE_MATURITY = 100; @@ -86,7 +87,7 @@ bool AddKey(const CKey& key); std::vector GenerateNewKey(); bool AddToWallet(const CWalletTx& wtxIn); void WalletUpdateSpent(const COutPoint& prevout); -int ScanForWalletTransactions(CBlockIndex* pindexStart); +int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); bool LoadBlockIndex(bool fAllowNew=true); void PrintBlockTree(); @@ -599,12 +600,14 @@ public: return dPriority > COIN * 144 / 250; } - int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true) const + int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, bool fForRelay=false) const { - // Base fee is 1 cent per kilobyte + // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE + int64 nBaseFee = fForRelay ? MIN_RELAY_TX_FEE : MIN_TX_FEE; + unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK); unsigned int nNewBlockSize = nBlockSize + nBytes; - int64 nMinFee = (1 + (int64)nBytes / 1000) * MIN_TX_FEE; + int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; if (fAllowFree) { @@ -623,11 +626,11 @@ public: } } - // To limit dust spam, require MIN_TX_FEE if any output is less than 0.01 - if (nMinFee < MIN_TX_FEE) + // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01 + if (nMinFee < nBaseFee) BOOST_FOREACH(const CTxOut& txout, vout) if (txout.nValue < CENT) - nMinFee = MIN_TX_FEE; + nMinFee = nBaseFee; // Raise the price as the block approaches full if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) diff --git a/core/include/net.h b/core/include/net.h index 6bbcd64e42..d1ded87232 100644 --- a/core/include/net.h +++ b/core/include/net.h @@ -283,13 +283,29 @@ public: return (memcmp(pchReserved, pchIPv4, sizeof(pchIPv4)) == 0); } + bool IsRFC1918() const + { + return IsIPv4() && (GetByte(3) == 10 || + (GetByte(3) == 192 && GetByte(2) == 168) || + (GetByte(3) == 172 && + (GetByte(2) >= 16 && GetByte(2) <= 31))); + } + + bool IsRFC3927() const + { + return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254); + } + + bool IsLocal() const + { + return IsIPv4() && (GetByte(3) == 127 || + GetByte(3) == 0); + } + bool IsRoutable() const { return IsValid() && - !(GetByte(3) == 10 || - (GetByte(3) == 192 && GetByte(2) == 168) || - GetByte(3) == 127 || - GetByte(3) == 0); + !(IsRFC1918() || IsRFC3927() || IsLocal()); } bool IsValid() const diff --git a/core/include/serialize.h b/core/include/serialize.h index 8e7677a2eb..0d66d6a956 100644 --- a/core/include/serialize.h +++ b/core/include/serialize.h @@ -33,7 +33,7 @@ class CDataStream; class CAutoFile; static const unsigned int MAX_SIZE = 0x02000000; -static const int VERSION = 32200; +static const int VERSION = 32300; static const char* pszSubVer = ""; static const bool VERSION_IS_BETA = true; diff --git a/core/include/util.h b/core/include/util.h index eccdbd372d..32a98e6264 100644 --- a/core/include/util.h +++ b/core/include/util.h @@ -197,7 +197,7 @@ std::string GetPidFile(); void CreatePidFile(std::string pidFile, pid_t pid); void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); #ifdef __WXMSW__ -string MyGetSpecialFolderPath(int nFolder, bool fCreate); +std::string MyGetSpecialFolderPath(int nFolder, bool fCreate); #endif std::string GetDefaultDataDir(); std::string GetDataDir(); diff --git a/core/src/init.cpp b/core/src/init.cpp index d40d29cf09..f7a1ce9ef1 100644 --- a/core/src/init.cpp +++ b/core/src/init.cpp @@ -386,7 +386,7 @@ bool AppInit2(int argc, char* argv[]) { printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); nStart = GetTimeMillis(); - ScanForWalletTransactions(pindexRescan); + ScanForWalletTransactions(pindexRescan, true); printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); } diff --git a/core/src/irc.cpp b/core/src/irc.cpp index 099d9e0735..a76374d143 100644 --- a/core/src/irc.cpp +++ b/core/src/irc.cpp @@ -265,11 +265,11 @@ void ThreadIRCSeed2(void* parg) while (!fShutdown) { //CAddress addrConnect("216.155.130.130:6667"); // chat.freenode.net - CAddress addrConnect("92.243.23.21:6667"); // irc.lfnet.org + CAddress addrConnect("92.243.23.21", 6667); // irc.lfnet.org if (!fTOR) { //struct hostent* phostent = gethostbyname("chat.freenode.net"); - CAddress addrIRC("irc.lfnet.org:6667", 0, true); + CAddress addrIRC("irc.lfnet.org", 6667, true); if (addrIRC.IsValid()) addrConnect = addrIRC; } @@ -339,9 +339,16 @@ void ThreadIRCSeed2(void* parg) Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str()); } } - - Send(hSocket, fTestNet ? "JOIN #bitcoinTEST\r" : "JOIN #bitcoin\r"); - Send(hSocket, fTestNet ? "WHO #bitcoinTEST\r" : "WHO #bitcoin\r"); + + if (fTestNet) { + Send(hSocket, "JOIN #bitcoinTEST\r"); + Send(hSocket, "WHO #bitcoinTEST\r"); + } else { + // randomly join #bitcoin00-#bitcoin99 + int channel_number = GetRandInt(100); + Send(hSocket, strprintf("JOIN #bitcoin%02d\r", channel_number).c_str()); + Send(hSocket, strprintf("WHO #bitcoin%02d\r", channel_number).c_str()); + } int64 nStart = GetTime(); string strLine; diff --git a/core/src/main.cpp b/core/src/main.cpp index 68b6b4ee1b..0456041ee5 100644 --- a/core/src/main.cpp +++ b/core/src/main.cpp @@ -731,13 +731,13 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi } // Don't accept it if it can't get into a block - if (nFees < GetMinFee(1000)) + if (nFees < GetMinFee(1000, true, true)) return error("AcceptToMemoryPool() : not enough fees"); // Continuously rate-limit free transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make other's transactions take longer to confirm. - if (nFees < MIN_TX_FEE) + if (nFees < MIN_RELAY_TX_FEE) { static CCriticalSection cs; static double dFreeCount; @@ -884,7 +884,7 @@ bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) return false; } -int ScanForWalletTransactions(CBlockIndex* pindexStart) +int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) { int ret = 0; @@ -897,7 +897,7 @@ int ScanForWalletTransactions(CBlockIndex* pindexStart) block.ReadFromDisk(pindex, true); BOOST_FOREACH(CTransaction& tx, block.vtx) { - if (AddToWalletIfInvolvingMe(tx, &block)) + if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) ret++; } pindex = pindex->pnext; @@ -3329,7 +3329,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Transaction fee required depends on block size bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority)); - int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree); + int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, true); // Connecting shouldn't fail due to dependency on other memory pool transactions // because we're already processing them in order of dependency @@ -3854,9 +3854,18 @@ bool CreateTransaction(const vector >& vecSend, CWalletTx& dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(); } - // Fill a vout back to self with any change - int64 nChange = nValueIn - nTotalValue; - if (nChange >= CENT) + int64 nChange = nValueIn - nValue - nFeeRet; + + // if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE + // or until nChange becomes zero + if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT) + { + int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet); + nChange -= nMoveToFee; + nFeeRet += nMoveToFee; + } + + if (nChange > 0) { // Note: We use a new key here to keep it from being obvious which side is the change. // The drawback is that by not reusing a previous key, the change may be lost if a diff --git a/core/src/net.cpp b/core/src/net.cpp index 1320781cb2..39360a334c 100644 --- a/core/src/net.cpp +++ b/core/src/net.cpp @@ -56,6 +56,10 @@ CAddress addrProxy("127.0.0.1",9050); +unsigned short GetListenPort() +{ + return (unsigned short)(GetArg("-port", GetDefaultPort())); +} void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) { @@ -84,8 +88,7 @@ bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)); #endif - bool fRoutable = !(addrConnect.GetByte(3) == 10 || (addrConnect.GetByte(3) == 192 && addrConnect.GetByte(2) == 168)); - bool fProxy = (fUseProxy && fRoutable); + bool fProxy = (fUseProxy && addrConnect.IsRoutable()); struct sockaddr_in sockaddr = (fProxy ? addrProxy.GetSockAddr() : addrConnect.GetSockAddr()); if (connect(hSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) @@ -965,7 +968,7 @@ void ThreadMapPort2(void* parg) printf("ThreadMapPort started\n"); char port[6]; - sprintf(port, "%d", GetDefaultPort()); + sprintf(port, "%d", GetListenPort()); const char * rootdescurl = 0; const char * multicastif = 0; @@ -1058,7 +1061,7 @@ void DNSAddressSeed() for (int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) { vector vaddr; - if (Lookup(strDNSSeed[seed_idx], vaddr, NODE_NETWORK, true)) + if (Lookup(strDNSSeed[seed_idx], vaddr, NODE_NETWORK, -1, true)) { BOOST_FOREACH (CAddress& addr, vaddr) { @@ -1435,14 +1438,11 @@ void ThreadMessageHandler2(void* parg) - - - bool BindListenPort(string& strError) { strError = ""; int nOne = 1; - addrLocalHost.port = htons(GetDefaultPort()); + addrLocalHost.port = htons(GetListenPort()); #ifdef __WXMSW__ // Initialize Windows Sockets @@ -1494,7 +1494,7 @@ bool BindListenPort(string& strError) memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sin_family = AF_INET; sockaddr.sin_addr.s_addr = INADDR_ANY; // bind to all IPs on this computer - sockaddr.sin_port = htons(GetDefaultPort()); + sockaddr.sin_port = htons(GetListenPort()); if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) { int nErr = WSAGetLastError(); @@ -1556,7 +1556,7 @@ void StartNode(void* parg) printf("ipv4 %s: %s\n", ifa->ifa_name, pszIP); // Take the first IP that isn't loopback 127.x.x.x - CAddress addr(*(unsigned int*)&s4->sin_addr, 0, nLocalServices); + CAddress addr(*(unsigned int*)&s4->sin_addr, GetListenPort(), nLocalServices); if (addr.IsValid() && addr.GetByte(3) != 127) { addrLocalHost = addr; diff --git a/core/src/rpc.cpp b/core/src/rpc.cpp index 9efcbbb15a..530bef4a43 100644 --- a/core/src/rpc.cpp +++ b/core/src/rpc.cpp @@ -199,12 +199,26 @@ double GetDifficulty() { // Floating point number that is a multiple of the minimum difficulty, // minimum difficulty = 1.0. + if (pindexBest == NULL) return 1.0; - int nShift = 256 - 32 - 31; // to fit in a uint - double dMinimum = (CBigNum().SetCompact(bnProofOfWorkLimit.GetCompact()) >> nShift).getuint(); - double dCurrently = (CBigNum().SetCompact(pindexBest->nBits) >> nShift).getuint(); - return dMinimum / dCurrently; + int nShift = (pindexBest->nBits >> 24) & 0xff; + + double dDiff = + (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff); + + while (nShift < 29) + { + dDiff *= 256.0; + nShift++; + } + while (nShift > 29) + { + dDiff /= 256.0; + nShift--; + } + + return dDiff; } Value getdifficulty(const Array& params, bool fHelp) diff --git a/core/src/util.cpp b/core/src/util.cpp index 4e93f625de..6199109289 100644 --- a/core/src/util.cpp +++ b/core/src/util.cpp @@ -271,7 +271,7 @@ string strprintf(const char* format, ...) if (ret >= 0 && ret < limit) break; if (p != buffer) - delete p; + delete[] p; limit *= 2; p = new char[limit]; if (p == NULL) @@ -279,7 +279,7 @@ string strprintf(const char* format, ...) } string str(p, p+ret); if (p != buffer) - delete p; + delete[] p; return str; } -- cgit v1.2.3 From 6717457390d6197410f7264af6c4182a7767bc99 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 5 Jun 2011 21:40:28 +0200 Subject: align "amount" input in send coins dialog to the right --- gui/forms/sendcoinsdialog.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gui/forms/sendcoinsdialog.ui b/gui/forms/sendcoinsdialog.ui index ce4edde4ed..b53e14a50d 100644 --- a/gui/forms/sendcoinsdialog.ui +++ b/gui/forms/sendcoinsdialog.ui @@ -57,6 +57,9 @@ 16777215 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + -- cgit v1.2.3 From 8e86dca256624d76022be3b461b736dfc1f87625 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 7 Jun 2011 18:59:01 +0200 Subject: consistent bracing style --- gui/src/addressbookdialog.cpp | 6 +++-- gui/src/addresstablemodel.cpp | 20 ++++++++++++----- gui/src/bitcoin.cpp | 3 ++- gui/src/bitcoingui.cpp | 8 +++++-- gui/src/clientmodel.cpp | 8 ++++++- gui/src/optionsmodel.cpp | 12 +++++++--- gui/src/transactionrecord.cpp | 29 +++++++++++++++++------- gui/src/transactiontablemodel.cpp | 47 ++++++++++++++++++++++++++++----------- 8 files changed, 97 insertions(+), 36 deletions(-) diff --git a/gui/src/addressbookdialog.cpp b/gui/src/addressbookdialog.cpp index 0228eff461..eaab66a202 100644 --- a/gui/src/addressbookdialog.cpp +++ b/gui/src/addressbookdialog.cpp @@ -80,7 +80,8 @@ void AddressBookDialog::on_copyToClipboard_clicked() QTableView *table = getCurrentTable(); QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - foreach (QModelIndex index, indexes) { + foreach (QModelIndex index, indexes) + { QVariant address = table->model()->data(index); QApplication::clipboard()->setText(address.toString()); } @@ -148,7 +149,8 @@ void AddressBookDialog::on_buttonBox_accepted() QTableView *table = getCurrentTable(); QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - foreach (QModelIndex index, indexes) { + foreach (QModelIndex index, indexes) + { QVariant address = table->model()->data(index); returnValue = address.toString(); } diff --git a/gui/src/addresstablemodel.cpp b/gui/src/addresstablemodel.cpp index 1cbc1b5d1d..91b87fb7f1 100644 --- a/gui/src/addresstablemodel.cpp +++ b/gui/src/addresstablemodel.cpp @@ -58,7 +58,9 @@ struct AddressTablePriv if(idx >= 0 && idx < cachedAddressTable.size()) { return &cachedAddressTable[idx]; - } else { + } + else + { return 0; } } @@ -105,13 +107,15 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const case Address: return rec->address; } - } else if (role == Qt::FontRole) + } + else if (role == Qt::FontRole) { if(index.column() == Address) { return GUIUtil::bitcoinAddressFont(); } - } else if (role == TypeRole) + } + else if (role == TypeRole) { switch(rec->type) { @@ -178,7 +182,9 @@ QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & pa if(data) { return createIndex(row, column, priv->index(row)); - } else { + } + else + { return QModelIndex(); } } @@ -206,11 +212,13 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con return QString(); } } - } else if(type == Receive) + } + else if(type == Receive) { /* Generate a new address to associate with given label */ strAddress = PubKeyToAddress(GetKeyFromKeyPool()); - } else + } + else { return QString(); } diff --git a/gui/src/bitcoin.cpp b/gui/src/bitcoin.cpp index 6dbcb6c299..e003267426 100644 --- a/gui/src/bitcoin.cpp +++ b/gui/src/bitcoin.cpp @@ -92,7 +92,8 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); app.setQuitOnLastWindowClosed(false); - try { + try + { if(AppInit2(argc, argv)) { BitcoinGUI window; diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index 70da0b2393..ed7d133a39 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -345,7 +345,9 @@ void BitcoinGUI::error(const QString &title, const QString &message) { // Show as "balloon" message if possible trayIcon->showMessage(title, message, QSystemTrayIcon::Critical); - } else { + } + else + { // Fall back to old fashioned popup dialog if not QMessageBox::critical(this, title, message, @@ -363,7 +365,9 @@ void BitcoinGUI::changeEvent(QEvent *e) { hide(); e->ignore(); - } else { + } + else + { e->accept(); } } diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp index 8fd3599e97..97391e0938 100644 --- a/gui/src/clientmodel.cpp +++ b/gui/src/clientmodel.cpp @@ -34,7 +34,9 @@ QString ClientModel::getAddress() if (CWalletDB("r").ReadDefaultKey(vchPubKey)) { return QString::fromStdString(PubKeyToAddress(vchPubKey)); - } else { + } + else + { return QString(); } } @@ -116,9 +118,13 @@ ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payA std::string strError = SendMoney(scriptPubKey, payAmount, wtx, true); if (strError == "") + { return OK; + } else if (strError == "ABORTED") + { return Aborted; + } else { emit error(tr("Sending..."), QString::fromStdString(strError)); diff --git a/gui/src/optionsmodel.cpp b/gui/src/optionsmodel.cpp index 37d5cb1580..1528fdf697 100644 --- a/gui/src/optionsmodel.cpp +++ b/gui/src/optionsmodel.cpp @@ -81,7 +81,9 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in { addrProxy.ip = addr.ip; walletdb.WriteSetting("addrProxy", addrProxy); - } else { + } + else + { successful = false; } } @@ -93,7 +95,9 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in { addrProxy.port = htons(nPort); walletdb.WriteSetting("addrProxy", addrProxy); - } else { + } + else + { successful = false; } } @@ -104,7 +108,9 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in { nTransactionFee = retval; walletdb.WriteSetting("nTransactionFee", nTransactionFee); - } else { + } + else + { successful = false; /* parse error */ } } diff --git a/gui/src/transactionrecord.cpp b/gui/src/transactionrecord.cpp index 83c7d63515..6c1f3a5e58 100644 --- a/gui/src/transactionrecord.cpp +++ b/gui/src/transactionrecord.cpp @@ -124,12 +124,15 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx // Ignore parts sent to self, as this is usually the change // from a transaction sent back to our own address. continue; - } else if (!mapValue["to"].empty()) + } + else if (!mapValue["to"].empty()) { // Sent to IP sub.type = TransactionRecord::SendToIP; sub.address = mapValue["to"]; - } else { + } + else + { // Sent to Bitcoin Address sub.type = TransactionRecord::SendToAddress; uint160 hash160; @@ -148,7 +151,9 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx parts.append(sub); } - } else { + } + else + { // // Mixed debit transaction, can't break down payees // @@ -192,7 +197,9 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) { status.status = TransactionStatus::OpenUntilBlock; status.open_for = nBestHeight - wtx.nLockTime; - } else { + } + else + { status.status = TransactionStatus::OpenUntilDate; status.open_for = wtx.nLockTime; } @@ -202,10 +209,12 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) { status.status = TransactionStatus::Offline; - } else if (status.depth < 6) + } + else if (status.depth < 6) { status.status = TransactionStatus::Unconfirmed; - } else + } + else { status.status = TransactionStatus::HaveConfirmations; } @@ -226,10 +235,14 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) // Check if the block was requested by anyone if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) status.maturity = TransactionStatus::MaturesWarning; - } else { + } + else + { status.maturity = TransactionStatus::NotAccepted; } - } else { + } + else + { status.maturity = TransactionStatus::Mature; } } diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index 0d50e6e2df..f99784794c 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -121,13 +121,15 @@ struct TransactionTablePriv } parent->endInsertRows(); } - } else if(!inWallet && inModel) + } + else if(!inWallet && inModel) { /* Removed */ parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); cachedWallet.erase(lower, upper); parent->endRemoveRows(); - } else if(inWallet && inModel) + } + else if(inWallet && inModel) { /* Updated */ @@ -167,7 +169,9 @@ struct TransactionTablePriv } } return rec; - } else { + } + else + { return 0; } } @@ -274,7 +278,9 @@ QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const if(wtx->time) { return QVariant(GUIUtil::DateTimeStr(wtx->time)); - } else { + } + else + { return QVariant(); } } @@ -296,7 +302,9 @@ std::string lookupAddress(const std::string &address) description += "(" + label + ")"; } else + { description += address; + } } return description; } @@ -354,7 +362,9 @@ QVariant TransactionTableModel::formatTxDebit(const TransactionRecord *wtx) cons str = QString("[") + str + QString("]"); } return QVariant(str); - } else { + } + else + { return QVariant(); } } @@ -369,7 +379,9 @@ QVariant TransactionTableModel::formatTxCredit(const TransactionRecord *wtx) con str = QString("[") + str + QString("]"); } return QVariant(str); - } else { + } + else + { return QVariant(); } } @@ -396,7 +408,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case Credit: return formatTxCredit(rec); } - } else if(role == Qt::EditRole) + } + else if(role == Qt::EditRole) { /* Edit role is used for sorting so return the real values */ switch(index.column()) @@ -412,19 +425,24 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case Credit: return rec->credit; } - } else if (role == Qt::TextAlignmentRole) + } + else if (role == Qt::TextAlignmentRole) { return column_alignments[index.column()]; - } else if (role == Qt::ForegroundRole) + } + else if (role == Qt::ForegroundRole) { /* Non-confirmed transactions are grey */ if(rec->status.confirmed) { return QColor(0, 0, 0); - } else { + } + else + { return QColor(128, 128, 128); } - } else if (role == TypeRole) + } + else if (role == TypeRole) { /* Role for filtering tabs by type */ switch(rec->type) @@ -450,7 +468,8 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat if(role == Qt::DisplayRole) { return columns[section]; - } else if (role == Qt::TextAlignmentRole) + } + else if (role == Qt::TextAlignmentRole) { return column_alignments[section]; } @@ -470,7 +489,9 @@ QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex if(data) { return createIndex(row, column, priv->index(row)); - } else { + } + else + { return QModelIndex(); } } -- cgit v1.2.3 From 66d536ed0765c6369738c2b35dd528dac3f5ee67 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 10 Jun 2011 15:05:51 +0200 Subject: transaction details dialog on doubleclick --- README.rst | 6 +- bitcoin.pro | 11 +- gui/forms/transactiondescdialog.ui | 67 ++++++++ gui/include/bitcoingui.h | 7 +- gui/include/transactiondesc.h | 15 ++ gui/include/transactiondescdialog.h | 25 +++ gui/include/transactiontablemodel.h | 3 +- gui/src/bitcoingui.cpp | 11 ++ gui/src/transactiondesc.cpp | 310 ++++++++++++++++++++++++++++++++++++ gui/src/transactiondescdialog.cpp | 20 +++ gui/src/transactiontablemodel.cpp | 24 ++- 11 files changed, 486 insertions(+), 13 deletions(-) create mode 100644 gui/forms/transactiondescdialog.ui create mode 100644 gui/include/transactiondesc.h create mode 100644 gui/include/transactiondescdialog.h create mode 100644 gui/src/transactiondesc.cpp create mode 100644 gui/src/transactiondescdialog.cpp diff --git a/README.rst b/README.rst index 9856370bc9..a1932d8419 100644 --- a/README.rst +++ b/README.rst @@ -24,7 +24,9 @@ This has been implemented: - Sending coins (including ask for fee when needed) -- Show messages from core +- Show error messages from core + +- Show details dialog for transactions (on double click) This has to be done: @@ -34,6 +36,4 @@ This has to be done: - Build on Windows -- Show details dialog for transactions (on double click) - - More thorough testing of the view with all the kinds of transactions (sendmany, generation) diff --git a/bitcoin.pro b/bitcoin.pro index 9a3570aa58..7758376f7c 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -63,7 +63,9 @@ HEADERS += gui/include/bitcoingui.h \ gui/include/guiconstants.h \ gui/include/optionsmodel.h \ gui/include/monitoreddatamapper.h \ - core/include/externui.h + core/include/externui.h \ + gui/include/transactiondesc.h \ + gui/include/transactiondescdialog.h SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/transactiontablemodel.cpp \ gui/src/addresstablemodel.cpp \ @@ -90,7 +92,9 @@ SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ gui/src/guiutil.cpp \ gui/src/transactionrecord.cpp \ gui/src/optionsmodel.cpp \ - gui/src/monitoreddatamapper.cpp + gui/src/monitoreddatamapper.cpp \ + gui/src/transactiondesc.cpp \ + gui/src/transactiondescdialog.cpp RESOURCES += \ gui/bitcoin.qrc @@ -99,4 +103,5 @@ FORMS += \ gui/forms/sendcoinsdialog.ui \ gui/forms/addressbookdialog.ui \ gui/forms/aboutdialog.ui \ - gui/forms/editaddressdialog.ui + gui/forms/editaddressdialog.ui \ + gui/forms/transactiondescdialog.ui diff --git a/gui/forms/transactiondescdialog.ui b/gui/forms/transactiondescdialog.ui new file mode 100644 index 0000000000..8a44734da7 --- /dev/null +++ b/gui/forms/transactiondescdialog.ui @@ -0,0 +1,67 @@ + + + TransactionDescDialog + + + + 0 + 0 + 400 + 300 + + + + Transaction details + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + TransactionDescDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TransactionDescDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/gui/include/bitcoingui.h b/gui/include/bitcoingui.h index ab7b4bbdb6..e18e2ff21a 100644 --- a/gui/include/bitcoingui.h +++ b/gui/include/bitcoingui.h @@ -4,7 +4,6 @@ #include #include -/* Forward declarations */ class TransactionTableModel; class ClientModel; @@ -13,6 +12,7 @@ class QLabel; class QLineEdit; class QTableView; class QAbstractItemModel; +class QModelIndex; QT_END_NAMESPACE class BitcoinGUI : public QMainWindow @@ -66,6 +66,10 @@ public slots: void setNumBlocks(int count); void setNumTransactions(int count); void error(const QString &title, const QString &message); + /* It is currently not possible to pass a return value to another thread through + BlockingQueuedConnection, so use an indirected pointer. + http://bugreports.qt.nokia.com/browse/QTBUG-10440 + */ void askFee(qint64 nFeeRequired, bool *payFee); private slots: @@ -77,6 +81,7 @@ private slots: void newAddressClicked(); void copyClipboardClicked(); void trayIconActivated(QSystemTrayIcon::ActivationReason reason); + void transactionDetails(const QModelIndex& idx); }; #endif diff --git a/gui/include/transactiondesc.h b/gui/include/transactiondesc.h new file mode 100644 index 0000000000..5a85949341 --- /dev/null +++ b/gui/include/transactiondesc.h @@ -0,0 +1,15 @@ +#ifndef TRANSACTIONDESC_H +#define TRANSACTIONDESC_H + +#include + +class CWalletTx; + +class TransactionDesc +{ +public: + /* Provide human-readable extended HTML description of a transaction */ + static std::string toHTML(CWalletTx &wtx); +}; + +#endif // TRANSACTIONDESC_H diff --git a/gui/include/transactiondescdialog.h b/gui/include/transactiondescdialog.h new file mode 100644 index 0000000000..4f8f754b2b --- /dev/null +++ b/gui/include/transactiondescdialog.h @@ -0,0 +1,25 @@ +#ifndef TRANSACTIONDESCDIALOG_H +#define TRANSACTIONDESCDIALOG_H + +#include + +namespace Ui { + class TransactionDescDialog; +} +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE + +class TransactionDescDialog : public QDialog +{ + Q_OBJECT + +public: + explicit TransactionDescDialog(const QModelIndex &idx, QWidget *parent = 0); + ~TransactionDescDialog(); + +private: + Ui::TransactionDescDialog *ui; +}; + +#endif // TRANSACTIONDESCDIALOG_H diff --git a/gui/include/transactiontablemodel.h b/gui/include/transactiontablemodel.h index 70377ea0d5..47e4e4cf14 100644 --- a/gui/include/transactiontablemodel.h +++ b/gui/include/transactiontablemodel.h @@ -23,7 +23,8 @@ public: } ColumnIndex; enum { - TypeRole = Qt::UserRole + TypeRole = Qt::UserRole, + LongDescriptionRole = Qt::UserRole+1 } RoleIndex; /* TypeRole values */ diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index ed7d133a39..c92a546ea4 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -13,6 +13,7 @@ #include "guiutil.h" #include "editaddressdialog.h" #include "optionsmodel.h" +#include "transactiondescdialog.h" #include "main.h" @@ -212,6 +213,8 @@ QWidget *BitcoinGUI::createTabs() { QTableView *view = new QTableView(this); tabs->addTab(view, tab_labels.at(i)); + + connect(view, SIGNAL(activated(const QModelIndex&)), this, SLOT(transactionDetails(const QModelIndex&))); transactionViews.append(view); } @@ -396,3 +399,11 @@ void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); *payFee = (retval == QMessageBox::Yes); } + +void BitcoinGUI::transactionDetails(const QModelIndex& idx) +{ + /* A transaction is doubleclicked */ + TransactionDescDialog dlg(idx); + dlg.exec(); +} + diff --git a/gui/src/transactiondesc.cpp b/gui/src/transactiondesc.cpp new file mode 100644 index 0000000000..4d8a55e99a --- /dev/null +++ b/gui/src/transactiondesc.cpp @@ -0,0 +1,310 @@ +#include + +#include "guiutil.h" +#include "main.h" + +#include + +/* Taken straight from ui.cpp + TODO: Convert to use QStrings, Qt::Escape and tr() + */ + +using namespace std; + +static string HtmlEscape(const char* psz, bool fMultiLine=false) +{ + int len = 0; + for (const char* p = psz; *p; p++) + { + if (*p == '<') len += 4; + else if (*p == '>') len += 4; + else if (*p == '&') len += 5; + else if (*p == '"') len += 6; + else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6; + else if (*p == '\n' && fMultiLine) len += 5; + else + len++; + } + string str; + str.reserve(len); + for (const char* p = psz; *p; p++) + { + if (*p == '<') str += "<"; + else if (*p == '>') str += ">"; + else if (*p == '&') str += "&"; + else if (*p == '"') str += """; + else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " "; + else if (*p == '\n' && fMultiLine) str += "
\n"; + else + str += *p; + } + return str; +} + +static string HtmlEscape(const string& str, bool fMultiLine=false) +{ + return HtmlEscape(str.c_str(), fMultiLine); +} + +static string FormatTxStatus(const CWalletTx& wtx) +{ + // Status + if (!wtx.IsFinal()) + { + if (wtx.nLockTime < 500000000) + return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime); + else + return strprintf(_("Open until %s"), GUIUtil::DateTimeStr(wtx.nLockTime).toStdString().c_str()); + } + else + { + int nDepth = wtx.GetDepthInMainChain(); + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + return strprintf(_("%d/offline?"), nDepth); + else if (nDepth < 6) + return strprintf(_("%d/unconfirmed"), nDepth); + else + return strprintf(_("%d confirmations"), nDepth); + } +} + +string TransactionDesc::toHTML(CWalletTx &wtx) +{ + string strHTML; + CRITICAL_BLOCK(cs_mapAddressBook) + { + strHTML.reserve(4000); + strHTML += ""; + + int64 nTime = wtx.GetTxTime(); + int64 nCredit = wtx.GetCredit(); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + + + + strHTML += _("Status: ") + FormatTxStatus(wtx); + int nRequests = wtx.GetRequestCount(); + if (nRequests != -1) + { + if (nRequests == 0) + strHTML += _(", has not been successfully broadcast yet"); + else if (nRequests == 1) + strHTML += strprintf(_(", broadcast through %d node"), nRequests); + else + strHTML += strprintf(_(", broadcast through %d nodes"), nRequests); + } + strHTML += "
"; + + strHTML += _("Date: ") + (nTime ? GUIUtil::DateTimeStr(nTime).toStdString() : "") + "
"; + + + // + // From + // + if (wtx.IsCoinBase()) + { + strHTML += _("Source: Generated
"); + } + else if (!wtx.mapValue["from"].empty()) + { + // Online transaction + if (!wtx.mapValue["from"].empty()) + strHTML += _("From: ") + HtmlEscape(wtx.mapValue["from"]) + "
"; + } + else + { + // Offline transaction + if (nNet > 0) + { + // Credit + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (txout.IsMine()) + { + vector vchPubKey; + if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) + { + string strAddress = PubKeyToAddress(vchPubKey); + if (mapAddressBook.count(strAddress)) + { + strHTML += string() + _("From: ") + _("unknown") + "
"; + strHTML += _("To: "); + strHTML += HtmlEscape(strAddress); + if (!mapAddressBook[strAddress].empty()) + strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")"; + else + strHTML += _(" (yours)"); + strHTML += "
"; + } + } + break; + } + } + } + } + + + // + // To + // + string strAddress; + if (!wtx.mapValue["to"].empty()) + { + // Online transaction + strAddress = wtx.mapValue["to"]; + strHTML += _("To: "); + if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty()) + strHTML += mapAddressBook[strAddress] + " "; + strHTML += HtmlEscape(strAddress) + "
"; + } + + + // + // Amount + // + if (wtx.IsCoinBase() && nCredit == 0) + { + // + // Coinbase + // + int64 nUnmatured = 0; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + nUnmatured += txout.GetCredit(); + strHTML += _("Credit: "); + if (wtx.IsInMainChain()) + strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity()); + else + strHTML += _("(not accepted)"); + strHTML += "
"; + } + else if (nNet > 0) + { + // + // Credit + // + strHTML += _("Credit: ") + FormatMoney(nNet) + "
"; + } + else + { + bool fAllFromMe = true; + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + fAllFromMe = fAllFromMe && txin.IsMine(); + + bool fAllToMe = true; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + fAllToMe = fAllToMe && txout.IsMine(); + + if (fAllFromMe) + { + // + // Debit + // + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (txout.IsMine()) + continue; + + if (wtx.mapValue["to"].empty()) + { + // Offline transaction + uint160 hash160; + if (ExtractHash160(txout.scriptPubKey, hash160)) + { + string strAddress = Hash160ToAddress(hash160); + strHTML += _("To: "); + if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty()) + strHTML += mapAddressBook[strAddress] + " "; + strHTML += strAddress; + strHTML += "
"; + } + } + + strHTML += _("Debit: ") + FormatMoney(-txout.nValue) + "
"; + } + + if (fAllToMe) + { + // Payment to self + int64 nChange = wtx.GetChange(); + int64 nValue = nCredit - nChange; + strHTML += _("Debit: ") + FormatMoney(-nValue) + "
"; + strHTML += _("Credit: ") + FormatMoney(nValue) + "
"; + } + + int64 nTxFee = nDebit - wtx.GetValueOut(); + if (nTxFee > 0) + strHTML += _("Transaction fee: ") + FormatMoney(-nTxFee) + "
"; + } + else + { + // + // Mixed debit transaction + // + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + if (txin.IsMine()) + strHTML += _("Debit: ") + FormatMoney(-txin.GetDebit()) + "
"; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.IsMine()) + strHTML += _("Credit: ") + FormatMoney(txout.GetCredit()) + "
"; + } + } + + strHTML += _("Net amount: ") + FormatMoney(nNet, true) + "
"; + + + // + // Message + // + if (!wtx.mapValue["message"].empty()) + strHTML += string() + "
" + _("Message:") + "
" + HtmlEscape(wtx.mapValue["message"], true) + "
"; + if (!wtx.mapValue["comment"].empty()) + strHTML += string() + "
" + _("Comment:") + "
" + HtmlEscape(wtx.mapValue["comment"], true) + "
"; + + if (wtx.IsCoinBase()) + strHTML += string() + "
" + _("Generated coins must wait 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, it will change to \"not accepted\" and not be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.") + "
"; + + + // + // Debug view + // + if (fDebug) + { + strHTML += "

debug print

"; + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + if (txin.IsMine()) + strHTML += "Debit: " + FormatMoney(-txin.GetDebit()) + "
"; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.IsMine()) + strHTML += "Credit: " + FormatMoney(txout.GetCredit()) + "
"; + + strHTML += "
Transaction:
"; + strHTML += HtmlEscape(wtx.ToString(), true); + + strHTML += "
Inputs:
"; + CRITICAL_BLOCK(cs_mapWallet) + { + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + { + COutPoint prevout = txin.prevout; + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (prevout.n < prev.vout.size()) + { + strHTML += HtmlEscape(prev.ToString(), true); + strHTML += "    " + FormatTxStatus(prev) + ", "; + strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "
"; + } + } + } + } + } + + + + strHTML += "
"; + } + return strHTML; +} diff --git a/gui/src/transactiondescdialog.cpp b/gui/src/transactiondescdialog.cpp new file mode 100644 index 0000000000..3bd4808cb6 --- /dev/null +++ b/gui/src/transactiondescdialog.cpp @@ -0,0 +1,20 @@ +#include "transactiondescdialog.h" +#include "ui_transactiondescdialog.h" + +#include "transactiontablemodel.h" + +#include + +TransactionDescDialog::TransactionDescDialog(const QModelIndex &idx, QWidget *parent) : + QDialog(parent), + ui(new Ui::TransactionDescDialog) +{ + ui->setupUi(this); + QString desc = idx.data(TransactionTableModel::LongDescriptionRole).toString(); + ui->detailText->setHtml(desc); +} + +TransactionDescDialog::~TransactionDescDialog() +{ + delete ui; +} diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp index f99784794c..8fe1839930 100644 --- a/gui/src/transactiontablemodel.cpp +++ b/gui/src/transactiontablemodel.cpp @@ -3,6 +3,7 @@ #include "transactionrecord.h" #include "guiconstants.h" #include "main.h" +#include "transactiondesc.h" #include #include @@ -131,14 +132,10 @@ struct TransactionTablePriv } else if(inWallet && inModel) { - /* Updated */ - + /* Updated -- nothing to do, status update will take care of this */ } } } - /* TODO: invalidate status for all transactions - Use counter. Emit dataChanged for column. - */ } int size() @@ -176,6 +173,19 @@ struct TransactionTablePriv } } + QString describe(TransactionRecord *rec) + { + CRITICAL_BLOCK(cs_mapWallet) + { + std::map::iterator mi = mapWallet.find(rec->hash); + if(mi != mapWallet.end()) + { + return QString::fromStdString(TransactionDesc::toHTML(mi->second)); + } + } + return QString(""); + } + }; /* Credit and Debit columns are right-aligned as they contain numbers */ @@ -458,6 +468,10 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return TransactionTableModel::Other; } } + else if (role == LongDescriptionRole) + { + return priv->describe(rec); + } return QVariant(); } -- cgit v1.2.3 From 5b0dc80bf720a78953a07cb9c71e5da8cefe5279 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 10 Jun 2011 16:16:43 +0200 Subject: use real copyright sign in about dialog --- gui/forms/aboutdialog.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/forms/aboutdialog.ui b/gui/forms/aboutdialog.ui index 88668d4da8..45915c85bf 100644 --- a/gui/forms/aboutdialog.ui +++ b/gui/forms/aboutdialog.ui @@ -82,7 +82,7 @@ - Copyright (c) 2009-2011 Bitcoin Developers + Copyright © 2009-2011 Bitcoin Developers This is experimental software. -- cgit v1.2.3 From c30075142f72058bd55d96057377e31a96e32b79 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 10 Jun 2011 20:06:59 +0200 Subject: address book edit: edit the right row --- gui/src/addressbookdialog.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gui/src/addressbookdialog.cpp b/gui/src/addressbookdialog.cpp index eaab66a202..90950a6441 100644 --- a/gui/src/addressbookdialog.cpp +++ b/gui/src/addressbookdialog.cpp @@ -82,7 +82,7 @@ void AddressBookDialog::on_copyToClipboard_clicked() foreach (QModelIndex index, indexes) { - QVariant address = table->model()->data(index); + QVariant address = index.data(); QApplication::clipboard()->setText(address.toString()); } } @@ -94,6 +94,9 @@ void AddressBookDialog::on_editButton_clicked() { return; } + /* Map selected index to source address book model */ + QAbstractProxyModel *proxy_model = static_cast(getCurrentTable()->model()); + QModelIndex selected = proxy_model->mapToSource(indexes.at(0)); /* Double click also triggers edit button */ EditAddressDialog dlg( @@ -101,7 +104,7 @@ void AddressBookDialog::on_editButton_clicked() EditAddressDialog::EditSendingAddress : EditAddressDialog::EditReceivingAddress); dlg.setModel(model); - dlg.loadRow(indexes.at(0).row()); + dlg.loadRow(selected.row()); if(dlg.exec()) { dlg.saveCurrentRow(); -- cgit v1.2.3 From a777f7b9b5225f4fe753ce149fe0dc3c9c52f999 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 10 Jun 2011 20:49:50 +0200 Subject: show transaction details on doubleclick --- gui/include/transactiontablemodel.h | 2 +- gui/src/bitcoingui.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/include/transactiontablemodel.h b/gui/include/transactiontablemodel.h index 47e4e4cf14..5974c0e7fe 100644 --- a/gui/include/transactiontablemodel.h +++ b/gui/include/transactiontablemodel.h @@ -37,7 +37,7 @@ public: QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const; - QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; private: QStringList columns; TransactionTablePriv *priv; diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index c92a546ea4..51dcf87dc8 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -214,7 +214,7 @@ QWidget *BitcoinGUI::createTabs() QTableView *view = new QTableView(this); tabs->addTab(view, tab_labels.at(i)); - connect(view, SIGNAL(activated(const QModelIndex&)), this, SLOT(transactionDetails(const QModelIndex&))); + connect(view, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(transactionDetails(const QModelIndex&))); transactionViews.append(view); } -- cgit v1.2.3 From 725d460e4b2a2d1d5e3786cb184ad87dcff72d2c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 10 Jun 2011 21:59:29 +0200 Subject: show notification balloon on incoming transaction --- gui/include/bitcoingui.h | 1 + gui/src/bitcoingui.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/gui/include/bitcoingui.h b/gui/include/bitcoingui.h index e18e2ff21a..96452ef18b 100644 --- a/gui/include/bitcoingui.h +++ b/gui/include/bitcoingui.h @@ -82,6 +82,7 @@ private slots: void copyClipboardClicked(); void trayIconActivated(QSystemTrayIcon::ActivationReason reason); void transactionDetails(const QModelIndex& idx); + void incomingTransaction(const QModelIndex & parent, int start, int end); }; #endif diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index 51dcf87dc8..96125ef078 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -257,6 +257,9 @@ void BitcoinGUI::setTabsModel(QAbstractItemModel *transaction_model) transaction_table->horizontalHeader()->resizeSection( TransactionTableModel::Credit, 79); } + + connect(transaction_model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(incomingTransaction(const QModelIndex &, int, int))); } void BitcoinGUI::sendcoinsClicked() @@ -407,3 +410,25 @@ void BitcoinGUI::transactionDetails(const QModelIndex& idx) dlg.exec(); } +void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) +{ + TransactionTableModel *ttm = model->getTransactionTableModel(); + qint64 credit = ttm->index(start, TransactionTableModel::Credit, parent) + .data(Qt::EditRole).toULongLong(); + qint64 debit = ttm->index(start, TransactionTableModel::Debit, parent) + .data(Qt::EditRole).toULongLong(); + if((credit+debit)>0) + { + /* On incoming transaction, make an info balloon */ + QString date = ttm->index(start, TransactionTableModel::Date, parent) + .data().toString(); + QString description = ttm->index(start, TransactionTableModel::Description, parent) + .data().toString(); + + trayIcon->showMessage(tr("Incoming transaction"), + "Date: " + date + "\n" + + "Amount: " + QString::fromStdString(FormatMoney(credit+debit, true)) + "\n" + + description, + QSystemTrayIcon::Information); + } +} -- cgit v1.2.3 From 5e1feddcb621d7f42b9eb02b68e1858978f550b4 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 11 Jun 2011 12:46:09 +0200 Subject: Add tooltips, make "amount" entry consistent in Options dialog --- gui/forms/addressbookdialog.ui | 20 +++++++++++++++++++- gui/forms/editaddressdialog.ui | 12 ++++++++++-- gui/forms/sendcoinsdialog.ui | 20 +++++++++++++++++++- gui/forms/transactiondescdialog.ui | 6 +++++- gui/src/bitcoingui.cpp | 18 ++++++++++++++++-- gui/src/guiutil.cpp | 1 + gui/src/optionsdialog.cpp | 18 ++++++++++++------ 7 files changed, 82 insertions(+), 13 deletions(-) diff --git a/gui/forms/addressbookdialog.ui b/gui/forms/addressbookdialog.ui index 761ea0fb91..2b3c69fb97 100644 --- a/gui/forms/addressbookdialog.ui +++ b/gui/forms/addressbookdialog.ui @@ -17,9 +17,12 @@ - 1 + 0 + + + Sending @@ -43,6 +46,9 @@ + + + Receiving @@ -97,6 +103,9 @@ + + Create a new address + &New Address... @@ -104,6 +113,9 @@ + + Copy the currently selected address to the system clipboard + &Copy to Clipboard @@ -111,6 +123,9 @@ + + Edit the currently selected address + &Edit... @@ -118,6 +133,9 @@ + + Delete the currently selected address from the list + &Delete diff --git a/gui/forms/editaddressdialog.ui b/gui/forms/editaddressdialog.ui index 763a0bb8e4..f0ba28a854 100644 --- a/gui/forms/editaddressdialog.ui +++ b/gui/forms/editaddressdialog.ui @@ -40,10 +40,18 @@ - + + + The label associated with this address book entry + + - + + + The address associated with this address book entry. This can only be modified for sending addresses. + + diff --git a/gui/forms/sendcoinsdialog.ui b/gui/forms/sendcoinsdialog.ui index b53e14a50d..595b7f40ff 100644 --- a/gui/forms/sendcoinsdialog.ui +++ b/gui/forms/sendcoinsdialog.ui @@ -7,7 +7,7 @@ 0 0 736 - 140 + 149 @@ -44,6 +44,9 @@
+ + The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + 34 @@ -57,6 +60,9 @@ 16777215 + + Amount of bitcoins to send (e.g. 0.05) + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -64,6 +70,9 @@ + + Paste address from system clipboard + &Paste @@ -74,6 +83,9 @@ + + Look up adress in address book + Address &Book... @@ -126,6 +138,9 @@ + + Confirm the send action + &Send @@ -146,6 +161,9 @@ 0 + + Abort the send action + QDialogButtonBox::Cancel diff --git a/gui/forms/transactiondescdialog.ui b/gui/forms/transactiondescdialog.ui index 8a44734da7..2f70a38214 100644 --- a/gui/forms/transactiondescdialog.ui +++ b/gui/forms/transactiondescdialog.ui @@ -15,7 +15,11 @@ - + + + This pane shows a detailed description of the transaction + + diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp index 96125ef078..c08476cec3 100644 --- a/gui/src/bitcoingui.cpp +++ b/gui/src/bitcoingui.cpp @@ -75,10 +75,13 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): address = new QLineEdit(); address->setReadOnly(true); address->setFont(GUIUtil::bitcoinAddressFont()); + address->setToolTip(tr("Your current default receiving address")); hbox_address->addWidget(address); QPushButton *button_new = new QPushButton(tr("&New...")); + button_new->setToolTip(tr("Create new receiving address")); QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); + button_clipboard->setToolTip(tr("Copy current receiving address to the system clipboard")); hbox_address->addWidget(button_new); hbox_address->addWidget(button_clipboard); @@ -89,6 +92,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): labelBalance = new QLabel(); labelBalance->setFont(QFont("Monospace")); + labelBalance->setToolTip(tr("Your current balance")); hbox_balance->addWidget(labelBalance); hbox_balance->addStretch(1); @@ -108,15 +112,18 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): labelConnections = new QLabel(); labelConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); labelConnections->setMinimumWidth(130); + labelConnections->setToolTip(tr("Number of connections to other clients")); labelBlocks = new QLabel(); labelBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); labelBlocks->setMinimumWidth(130); - + labelBlocks->setToolTip(tr("Number of blocks in the block chain")); + labelTransactions = new QLabel(); labelTransactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); labelTransactions->setMinimumWidth(130); - + labelTransactions->setToolTip(tr("Number of transactions in your wallet")); + statusBar()->addPermanentWidget(labelConnections); statusBar()->addPermanentWidget(labelBlocks); statusBar()->addPermanentWidget(labelTransactions); @@ -131,12 +138,19 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): void BitcoinGUI::createActions() { quit = new QAction(QIcon(":/icons/quit"), tr("&Exit"), this); + quit->setToolTip(tr("Quit application")); sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); + sendcoins->setToolTip(tr("Send coins to a bitcoin address")); addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); + addressbook->setToolTip(tr("Edit the list of stored addresses and labels")); about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); + about->setToolTip(tr("Show information about Bitcoin")); receivingAddresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); + receivingAddresses->setToolTip(tr("Show the list of receiving addresses and edit their labels")); options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); + options->setToolTip(tr("Modify configuration options for bitcoin")); openBitcoin = new QAction(QIcon(":/icons/bitcoin"), "Open &Bitcoin", this); + openBitcoin->setToolTip(tr("Show the Bitcoin window")); connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked())); diff --git a/gui/src/guiutil.cpp b/gui/src/guiutil.cpp index d01f23d851..a81cc14fe8 100644 --- a/gui/src/guiutil.cpp +++ b/gui/src/guiutil.cpp @@ -33,5 +33,6 @@ void GUIUtil::setupAmountWidget(QLineEdit *widget, QWidget *parent) amountValidator->setDecimals(8); amountValidator->setBottom(0.0); widget->setValidator(amountValidator); + widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter); } diff --git a/gui/src/optionsdialog.cpp b/gui/src/optionsdialog.cpp index 1ec777c453..d46b48fb29 100644 --- a/gui/src/optionsdialog.cpp +++ b/gui/src/optionsdialog.cpp @@ -1,6 +1,7 @@ #include "optionsdialog.h" #include "optionsmodel.h" #include "monitoreddatamapper.h" +#include "guiutil.h" #include #include @@ -144,18 +145,23 @@ MainOptionsPage::MainOptionsPage(QWidget *parent): QVBoxLayout *layout = new QVBoxLayout(); bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup")); + bitcoin_at_startup->setToolTip(tr("Automatically start Bitcoin after the computer is turned on")); layout->addWidget(bitcoin_at_startup); minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar")); + minimize_to_tray->setToolTip(tr("Show only a tray icon after minimizing the window")); layout->addWidget(minimize_to_tray); map_port_upnp = new QCheckBox(tr("Map port using &UPnP")); + map_port_upnp->setToolTip(tr("Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.")); layout->addWidget(map_port_upnp); minimize_on_close = new QCheckBox(tr("M&inimize on close")); + minimize_on_close->setToolTip(tr("Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu.")); layout->addWidget(minimize_on_close); - connect_socks4 = new QCheckBox(tr("&Connect through socks4 proxy:")); + connect_socks4 = new QCheckBox(tr("&Connect through SOCKS4 proxy:")); + connect_socks4->setToolTip(tr("Connect to the Bitcon network through a SOCKS4 proxy (e.g. when connecting through Tor)")); layout->addWidget(connect_socks4); QHBoxLayout *proxy_hbox = new QHBoxLayout(); @@ -166,6 +172,7 @@ MainOptionsPage::MainOptionsPage(QWidget *parent): proxy_ip->setMaximumWidth(140); proxy_ip->setEnabled(false); proxy_ip->setValidator(new QRegExpValidator(QRegExp("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"), this)); + proxy_ip->setToolTip(tr("IP address of the proxy (e.g. 127.0.0.1)")); proxy_ip_label->setBuddy(proxy_ip); proxy_hbox->addWidget(proxy_ip); QLabel *proxy_port_label = new QLabel(tr("&Port: ")); @@ -174,6 +181,7 @@ MainOptionsPage::MainOptionsPage(QWidget *parent): proxy_port->setMaximumWidth(55); proxy_port->setValidator(new QIntValidator(0, 65535, this)); proxy_port->setEnabled(false); + proxy_port->setToolTip(tr("Port of the proxy (e.g. 1234)")); proxy_port_label->setBuddy(proxy_port); proxy_hbox->addWidget(proxy_port); proxy_hbox->addStretch(1); @@ -188,12 +196,10 @@ MainOptionsPage::MainOptionsPage(QWidget *parent): QLabel *fee_label = new QLabel(tr("Pay transaction &fee")); fee_hbox->addWidget(fee_label); fee_edit = new QLineEdit(); - fee_edit->setMaximumWidth(70); + fee_edit->setMaximumWidth(100); + fee_edit->setToolTip(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended.")); - QDoubleValidator *amountValidator = new QDoubleValidator(this); - amountValidator->setDecimals(8); - amountValidator->setBottom(0.0); - fee_edit->setValidator(amountValidator); + GUIUtil::setupAmountWidget(fee_edit, this); fee_label->setBuddy(fee_edit); fee_hbox->addWidget(fee_edit); -- cgit v1.2.3 From 5813089e0b5ab9cff50192f0068d256eb2602316 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 11 Jun 2011 12:48:31 +0200 Subject: Somewhat confident now, tested on GNOME+KDE, with all types of transactions. Next step is integration into bitcoin tree. --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index a1932d8419..f24335b5c3 100644 --- a/README.rst +++ b/README.rst @@ -30,10 +30,10 @@ This has been implemented: This has to be done: +- Integrate with main bitcoin tree + - Start at system start - Internationalization (convert WX language files) - Build on Windows - -- More thorough testing of the view with all the kinds of transactions (sendmany, generation) -- cgit v1.2.3 From ba4081c1fcaddf361abd61b2721994eff5475bb3 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 11 Jun 2011 22:11:58 +0200 Subject: move back to original directory structure --- bitcoin.pro | 182 +- core/include/base58.h | 208 -- core/include/bignum.h | 534 --- core/include/db.h | 526 --- core/include/externui.h | 45 - core/include/headers.h | 147 - core/include/init.h | 11 - core/include/irc.h | 13 - core/include/key.h | 175 - core/include/main.h | 2066 ------------ core/include/net.h | 1065 ------ core/include/noui.h | 67 - core/include/rpc.h | 6 - core/include/script.h | 718 ---- core/include/serialize.h | 1271 ------- core/include/strlcpy.h | 86 - core/include/uint256.h | 765 ----- core/include/util.h | 680 ---- core/src/db.cpp | 1026 ------ core/src/init.cpp | 525 --- core/src/irc.cpp | 445 --- core/src/main.cpp | 4033 ----------------------- core/src/net.cpp | 1667 ---------- core/src/rpc.cpp | 2209 ------------- core/src/script.cpp | 1208 ------- core/src/util.cpp | 906 ----- cryptopp/include/cryptopp/config.h | 462 --- cryptopp/include/cryptopp/cpu.h | 263 -- cryptopp/include/cryptopp/cryptlib.h | 1668 ---------- cryptopp/include/cryptopp/iterhash.h | 29 - cryptopp/include/cryptopp/misc.h | 1134 ------- cryptopp/include/cryptopp/pch.h | 21 - cryptopp/include/cryptopp/secblock.h | 501 --- cryptopp/include/cryptopp/sha.h | 63 - cryptopp/include/cryptopp/simple.h | 1 - cryptopp/include/cryptopp/smartptr.h | 223 -- cryptopp/include/cryptopp/stdcpp.h | 27 - cryptopp/src/cpu.cpp | 199 -- cryptopp/src/sha.cpp | 899 ----- gui/bitcoin.qrc | 12 - gui/forms/aboutdialog.ui | 162 - gui/forms/addressbookdialog.ui | 196 -- gui/forms/editaddressdialog.ui | 105 - gui/forms/sendcoinsdialog.ui | 180 - gui/forms/transactiondescdialog.ui | 71 - gui/include/aboutdialog.h | 25 - gui/include/addressbookdialog.h | 47 - gui/include/addresstablemodel.h | 54 - gui/include/bitcoinaddressvalidator.h | 24 - gui/include/bitcoingui.h | 88 - gui/include/clientmodel.h | 61 - gui/include/editaddressdialog.h | 41 - gui/include/guiconstants.h | 11 - gui/include/guiutil.h | 25 - gui/include/monitoreddatamapper.h | 32 - gui/include/optionsdialog.h | 45 - gui/include/optionsmodel.h | 44 - gui/include/sendcoinsdialog.h | 32 - gui/include/transactiondesc.h | 15 - gui/include/transactiondescdialog.h | 25 - gui/include/transactionrecord.h | 109 - gui/include/transactiontablemodel.h | 58 - gui/res/icons/address-book.png | Bin 1211 -> 0 bytes gui/res/icons/bitcoin.png | Bin 1086 -> 0 bytes gui/res/icons/quit.png | Bin 876 -> 0 bytes gui/res/icons/send.png | Bin 1485 -> 0 bytes gui/res/icons/toolbar.png | Bin 968 -> 0 bytes gui/res/images/about.png | Bin 3488 -> 0 bytes gui/src/aboutdialog.cpp | 22 - gui/src/addressbookdialog.cpp | 168 - gui/src/addresstablemodel.cpp | 245 -- gui/src/bitcoin.cpp | 122 - gui/src/bitcoinaddressvalidator.cpp | 63 - gui/src/bitcoingui.cpp | 448 --- gui/src/clientmodel.cpp | 156 - gui/src/editaddressdialog.cpp | 84 - gui/src/guiutil.cpp | 38 - gui/src/monitoreddatamapper.cpp | 39 - gui/src/optionsdialog.cpp | 234 -- gui/src/optionsmodel.cpp | 140 - gui/src/sendcoinsdialog.cpp | 113 - gui/src/transactiondesc.cpp | 310 -- gui/src/transactiondescdialog.cpp | 20 - gui/src/transactionrecord.cpp | 254 -- gui/src/transactiontablemodel.cpp | 512 --- json/include/json/json_spirit.h | 18 - json/include/json/json_spirit_error_position.h | 54 - json/include/json/json_spirit_reader.h | 62 - json/include/json/json_spirit_reader_template.h | 612 ---- json/include/json/json_spirit_stream_reader.h | 70 - json/include/json/json_spirit_utils.h | 61 - json/include/json/json_spirit_value.h | 534 --- json/include/json/json_spirit_writer.h | 50 - json/include/json/json_spirit_writer_template.h | 248 -- json/src/json_spirit_reader.cpp | 137 - json/src/json_spirit_value.cpp | 8 - json/src/json_spirit_writer.cpp | 95 - src/base58.h | 208 ++ src/bignum.h | 534 +++ src/cryptopp/config.h | 462 +++ src/cryptopp/cpu.cpp | 199 ++ src/cryptopp/cpu.h | 263 ++ src/cryptopp/cryptlib.h | 1668 ++++++++++ src/cryptopp/iterhash.h | 29 + src/cryptopp/misc.h | 1134 +++++++ src/cryptopp/pch.h | 21 + src/cryptopp/secblock.h | 501 +++ src/cryptopp/sha.cpp | 899 +++++ src/cryptopp/sha.h | 63 + src/cryptopp/simple.h | 1 + src/cryptopp/smartptr.h | 223 ++ src/cryptopp/stdcpp.h | 27 + src/db.cpp | 1026 ++++++ src/db.h | 526 +++ src/externui.h | 45 + src/headers.h | 147 + src/init.cpp | 525 +++ src/init.h | 11 + src/irc.cpp | 445 +++ src/irc.h | 13 + src/json/json_spirit.h | 18 + src/json/json_spirit_error_position.h | 54 + src/json/json_spirit_reader.cpp | 137 + src/json/json_spirit_reader.h | 62 + src/json/json_spirit_reader_template.h | 612 ++++ src/json/json_spirit_stream_reader.h | 70 + src/json/json_spirit_utils.h | 61 + src/json/json_spirit_value.cpp | 8 + src/json/json_spirit_value.h | 534 +++ src/json/json_spirit_writer.cpp | 95 + src/json/json_spirit_writer.h | 50 + src/json/json_spirit_writer_template.h | 248 ++ src/key.h | 175 + src/main.cpp | 4033 +++++++++++++++++++++++ src/main.h | 2066 ++++++++++++ src/net.cpp | 1667 ++++++++++ src/net.h | 1065 ++++++ src/noui.h | 67 + src/qt/aboutdialog.cpp | 22 + src/qt/aboutdialog.h | 25 + src/qt/addressbookdialog.cpp | 168 + src/qt/addressbookdialog.h | 47 + src/qt/addresstablemodel.cpp | 245 ++ src/qt/addresstablemodel.h | 54 + src/qt/bitcoin.cpp | 122 + src/qt/bitcoin.qrc | 12 + src/qt/bitcoinaddressvalidator.cpp | 63 + src/qt/bitcoinaddressvalidator.h | 24 + src/qt/bitcoingui.cpp | 448 +++ src/qt/bitcoingui.h | 88 + src/qt/clientmodel.cpp | 156 + src/qt/clientmodel.h | 61 + src/qt/editaddressdialog.cpp | 84 + src/qt/editaddressdialog.h | 41 + src/qt/forms/aboutdialog.ui | 162 + src/qt/forms/addressbookdialog.ui | 196 ++ src/qt/forms/editaddressdialog.ui | 105 + src/qt/forms/sendcoinsdialog.ui | 180 + src/qt/forms/transactiondescdialog.ui | 71 + src/qt/guiconstants.h | 11 + src/qt/guiutil.cpp | 38 + src/qt/guiutil.h | 25 + src/qt/monitoreddatamapper.cpp | 39 + src/qt/monitoreddatamapper.h | 32 + src/qt/optionsdialog.cpp | 234 ++ src/qt/optionsdialog.h | 45 + src/qt/optionsmodel.cpp | 140 + src/qt/optionsmodel.h | 44 + src/qt/res/icons/address-book.png | Bin 0 -> 1211 bytes src/qt/res/icons/bitcoin.png | Bin 0 -> 1086 bytes src/qt/res/icons/quit.png | Bin 0 -> 876 bytes src/qt/res/icons/send.png | Bin 0 -> 1485 bytes src/qt/res/icons/toolbar.png | Bin 0 -> 968 bytes src/qt/res/images/about.png | Bin 0 -> 3488 bytes src/qt/sendcoinsdialog.cpp | 113 + src/qt/sendcoinsdialog.h | 32 + src/qt/transactiondesc.cpp | 310 ++ src/qt/transactiondesc.h | 15 + src/qt/transactiondescdialog.cpp | 20 + src/qt/transactiondescdialog.h | 25 + src/qt/transactionrecord.cpp | 254 ++ src/qt/transactionrecord.h | 109 + src/qt/transactiontablemodel.cpp | 512 +++ src/qt/transactiontablemodel.h | 58 + src/rpc.cpp | 2209 +++++++++++++ src/rpc.h | 6 + src/script.cpp | 1208 +++++++ src/script.h | 718 ++++ src/serialize.h | 1271 +++++++ src/strlcpy.h | 86 + src/uint256.h | 765 +++++ src/util.cpp | 906 +++++ src/util.h | 680 ++++ 193 files changed, 32362 insertions(+), 32362 deletions(-) delete mode 100644 core/include/base58.h delete mode 100644 core/include/bignum.h delete mode 100644 core/include/db.h delete mode 100644 core/include/externui.h delete mode 100644 core/include/headers.h delete mode 100644 core/include/init.h delete mode 100644 core/include/irc.h delete mode 100644 core/include/key.h delete mode 100644 core/include/main.h delete mode 100644 core/include/net.h delete mode 100644 core/include/noui.h delete mode 100644 core/include/rpc.h delete mode 100644 core/include/script.h delete mode 100644 core/include/serialize.h delete mode 100644 core/include/strlcpy.h delete mode 100644 core/include/uint256.h delete mode 100644 core/include/util.h delete mode 100644 core/src/db.cpp delete mode 100644 core/src/init.cpp delete mode 100644 core/src/irc.cpp delete mode 100644 core/src/main.cpp delete mode 100644 core/src/net.cpp delete mode 100644 core/src/rpc.cpp delete mode 100644 core/src/script.cpp delete mode 100644 core/src/util.cpp delete mode 100644 cryptopp/include/cryptopp/config.h delete mode 100644 cryptopp/include/cryptopp/cpu.h delete mode 100644 cryptopp/include/cryptopp/cryptlib.h delete mode 100644 cryptopp/include/cryptopp/iterhash.h delete mode 100644 cryptopp/include/cryptopp/misc.h delete mode 100644 cryptopp/include/cryptopp/pch.h delete mode 100644 cryptopp/include/cryptopp/secblock.h delete mode 100644 cryptopp/include/cryptopp/sha.h delete mode 100644 cryptopp/include/cryptopp/simple.h delete mode 100644 cryptopp/include/cryptopp/smartptr.h delete mode 100644 cryptopp/include/cryptopp/stdcpp.h delete mode 100644 cryptopp/src/cpu.cpp delete mode 100644 cryptopp/src/sha.cpp delete mode 100644 gui/bitcoin.qrc delete mode 100644 gui/forms/aboutdialog.ui delete mode 100644 gui/forms/addressbookdialog.ui delete mode 100644 gui/forms/editaddressdialog.ui delete mode 100644 gui/forms/sendcoinsdialog.ui delete mode 100644 gui/forms/transactiondescdialog.ui delete mode 100644 gui/include/aboutdialog.h delete mode 100644 gui/include/addressbookdialog.h delete mode 100644 gui/include/addresstablemodel.h delete mode 100644 gui/include/bitcoinaddressvalidator.h delete mode 100644 gui/include/bitcoingui.h delete mode 100644 gui/include/clientmodel.h delete mode 100644 gui/include/editaddressdialog.h delete mode 100644 gui/include/guiconstants.h delete mode 100644 gui/include/guiutil.h delete mode 100644 gui/include/monitoreddatamapper.h delete mode 100644 gui/include/optionsdialog.h delete mode 100644 gui/include/optionsmodel.h delete mode 100644 gui/include/sendcoinsdialog.h delete mode 100644 gui/include/transactiondesc.h delete mode 100644 gui/include/transactiondescdialog.h delete mode 100644 gui/include/transactionrecord.h delete mode 100644 gui/include/transactiontablemodel.h delete mode 100644 gui/res/icons/address-book.png delete mode 100644 gui/res/icons/bitcoin.png delete mode 100644 gui/res/icons/quit.png delete mode 100644 gui/res/icons/send.png delete mode 100644 gui/res/icons/toolbar.png delete mode 100644 gui/res/images/about.png delete mode 100644 gui/src/aboutdialog.cpp delete mode 100644 gui/src/addressbookdialog.cpp delete mode 100644 gui/src/addresstablemodel.cpp delete mode 100644 gui/src/bitcoin.cpp delete mode 100644 gui/src/bitcoinaddressvalidator.cpp delete mode 100644 gui/src/bitcoingui.cpp delete mode 100644 gui/src/clientmodel.cpp delete mode 100644 gui/src/editaddressdialog.cpp delete mode 100644 gui/src/guiutil.cpp delete mode 100644 gui/src/monitoreddatamapper.cpp delete mode 100644 gui/src/optionsdialog.cpp delete mode 100644 gui/src/optionsmodel.cpp delete mode 100644 gui/src/sendcoinsdialog.cpp delete mode 100644 gui/src/transactiondesc.cpp delete mode 100644 gui/src/transactiondescdialog.cpp delete mode 100644 gui/src/transactionrecord.cpp delete mode 100644 gui/src/transactiontablemodel.cpp delete mode 100644 json/include/json/json_spirit.h delete mode 100644 json/include/json/json_spirit_error_position.h delete mode 100644 json/include/json/json_spirit_reader.h delete mode 100644 json/include/json/json_spirit_reader_template.h delete mode 100644 json/include/json/json_spirit_stream_reader.h delete mode 100644 json/include/json/json_spirit_utils.h delete mode 100644 json/include/json/json_spirit_value.h delete mode 100644 json/include/json/json_spirit_writer.h delete mode 100644 json/include/json/json_spirit_writer_template.h delete mode 100644 json/src/json_spirit_reader.cpp delete mode 100644 json/src/json_spirit_value.cpp delete mode 100644 json/src/json_spirit_writer.cpp create mode 100644 src/base58.h create mode 100644 src/bignum.h create mode 100644 src/cryptopp/config.h create mode 100644 src/cryptopp/cpu.cpp create mode 100644 src/cryptopp/cpu.h create mode 100644 src/cryptopp/cryptlib.h create mode 100644 src/cryptopp/iterhash.h create mode 100644 src/cryptopp/misc.h create mode 100644 src/cryptopp/pch.h create mode 100644 src/cryptopp/secblock.h create mode 100644 src/cryptopp/sha.cpp create mode 100644 src/cryptopp/sha.h create mode 100644 src/cryptopp/simple.h create mode 100644 src/cryptopp/smartptr.h create mode 100644 src/cryptopp/stdcpp.h create mode 100644 src/db.cpp create mode 100644 src/db.h create mode 100644 src/externui.h create mode 100644 src/headers.h create mode 100644 src/init.cpp create mode 100644 src/init.h create mode 100644 src/irc.cpp create mode 100644 src/irc.h create mode 100644 src/json/json_spirit.h create mode 100644 src/json/json_spirit_error_position.h create mode 100644 src/json/json_spirit_reader.cpp create mode 100644 src/json/json_spirit_reader.h create mode 100644 src/json/json_spirit_reader_template.h create mode 100644 src/json/json_spirit_stream_reader.h create mode 100644 src/json/json_spirit_utils.h create mode 100644 src/json/json_spirit_value.cpp create mode 100644 src/json/json_spirit_value.h create mode 100644 src/json/json_spirit_writer.cpp create mode 100644 src/json/json_spirit_writer.h create mode 100644 src/json/json_spirit_writer_template.h create mode 100644 src/key.h create mode 100644 src/main.cpp create mode 100644 src/main.h create mode 100644 src/net.cpp create mode 100644 src/net.h create mode 100644 src/noui.h create mode 100644 src/qt/aboutdialog.cpp create mode 100644 src/qt/aboutdialog.h create mode 100644 src/qt/addressbookdialog.cpp create mode 100644 src/qt/addressbookdialog.h create mode 100644 src/qt/addresstablemodel.cpp create mode 100644 src/qt/addresstablemodel.h create mode 100644 src/qt/bitcoin.cpp create mode 100644 src/qt/bitcoin.qrc create mode 100644 src/qt/bitcoinaddressvalidator.cpp create mode 100644 src/qt/bitcoinaddressvalidator.h create mode 100644 src/qt/bitcoingui.cpp create mode 100644 src/qt/bitcoingui.h create mode 100644 src/qt/clientmodel.cpp create mode 100644 src/qt/clientmodel.h create mode 100644 src/qt/editaddressdialog.cpp create mode 100644 src/qt/editaddressdialog.h create mode 100644 src/qt/forms/aboutdialog.ui create mode 100644 src/qt/forms/addressbookdialog.ui create mode 100644 src/qt/forms/editaddressdialog.ui create mode 100644 src/qt/forms/sendcoinsdialog.ui create mode 100644 src/qt/forms/transactiondescdialog.ui create mode 100644 src/qt/guiconstants.h create mode 100644 src/qt/guiutil.cpp create mode 100644 src/qt/guiutil.h create mode 100644 src/qt/monitoreddatamapper.cpp create mode 100644 src/qt/monitoreddatamapper.h create mode 100644 src/qt/optionsdialog.cpp create mode 100644 src/qt/optionsdialog.h create mode 100644 src/qt/optionsmodel.cpp create mode 100644 src/qt/optionsmodel.h create mode 100644 src/qt/res/icons/address-book.png create mode 100644 src/qt/res/icons/bitcoin.png create mode 100644 src/qt/res/icons/quit.png create mode 100644 src/qt/res/icons/send.png create mode 100644 src/qt/res/icons/toolbar.png create mode 100644 src/qt/res/images/about.png create mode 100644 src/qt/sendcoinsdialog.cpp create mode 100644 src/qt/sendcoinsdialog.h create mode 100644 src/qt/transactiondesc.cpp create mode 100644 src/qt/transactiondesc.h create mode 100644 src/qt/transactiondescdialog.cpp create mode 100644 src/qt/transactiondescdialog.h create mode 100644 src/qt/transactionrecord.cpp create mode 100644 src/qt/transactionrecord.h create mode 100644 src/qt/transactiontablemodel.cpp create mode 100644 src/qt/transactiontablemodel.h create mode 100644 src/rpc.cpp create mode 100644 src/rpc.h create mode 100644 src/script.cpp create mode 100644 src/script.h create mode 100644 src/serialize.h create mode 100644 src/strlcpy.h create mode 100644 src/uint256.h create mode 100644 src/util.cpp create mode 100644 src/util.h diff --git a/bitcoin.pro b/bitcoin.pro index 7758376f7c..dd9ea7ff33 100644 --- a/bitcoin.pro +++ b/bitcoin.pro @@ -1,7 +1,7 @@ TEMPLATE = app TARGET = DEPENDPATH += . -INCLUDEPATH += gui/include core/include cryptopp/include json/include +INCLUDEPATH += src src/json src/cryptopp src/qt unix:LIBS += -lssl -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 @@ -11,97 +11,97 @@ QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wno-invalid-offsetof - # WINDOWS defines, -DSSL, look at build system # Input -DEPENDPATH += gui/include core/include cryptopp/include core/include json/include -HEADERS += gui/include/bitcoingui.h \ - gui/include/transactiontablemodel.h \ - gui/include/addresstablemodel.h \ - gui/include/optionsdialog.h \ - gui/include/sendcoinsdialog.h \ - gui/include/addressbookdialog.h \ - gui/include/aboutdialog.h \ - gui/include/editaddressdialog.h \ - gui/include/bitcoinaddressvalidator.h \ - core/include/base58.h \ - core/include/bignum.h \ - core/include/util.h \ - core/include/uint256.h \ - core/include/serialize.h \ - cryptopp/include/cryptopp/stdcpp.h \ - cryptopp/include/cryptopp/smartptr.h \ - cryptopp/include/cryptopp/simple.h \ - cryptopp/include/cryptopp/sha.h \ - cryptopp/include/cryptopp/secblock.h \ - cryptopp/include/cryptopp/pch.h \ - cryptopp/include/cryptopp/misc.h \ - cryptopp/include/cryptopp/iterhash.h \ - cryptopp/include/cryptopp/cryptlib.h \ - cryptopp/include/cryptopp/cpu.h \ - cryptopp/include/cryptopp/config.h \ - core/include/strlcpy.h \ - core/include/main.h \ - core/include/net.h \ - core/include/key.h \ - core/include/db.h \ - core/include/script.h \ - core/include/noui.h \ - core/include/init.h \ - core/include/headers.h \ - core/include/irc.h \ - json/include/json/json_spirit_writer_template.h \ - json/include/json/json_spirit_writer.h \ - json/include/json/json_spirit_value.h \ - json/include/json/json_spirit_utils.h \ - json/include/json/json_spirit_stream_reader.h \ - json/include/json/json_spirit_reader_template.h \ - json/include/json/json_spirit_reader.h \ - json/include/json/json_spirit_error_position.h \ - json/include/json/json_spirit.h \ - core/include/rpc.h \ - gui/include/clientmodel.h \ - gui/include/guiutil.h \ - gui/include/transactionrecord.h \ - gui/include/guiconstants.h \ - gui/include/optionsmodel.h \ - gui/include/monitoreddatamapper.h \ - core/include/externui.h \ - gui/include/transactiondesc.h \ - gui/include/transactiondescdialog.h -SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \ - gui/src/transactiontablemodel.cpp \ - gui/src/addresstablemodel.cpp \ - gui/src/optionsdialog.cpp \ - gui/src/sendcoinsdialog.cpp \ - gui/src/addressbookdialog.cpp \ - gui/src/aboutdialog.cpp \ - gui/src/editaddressdialog.cpp \ - gui/src/bitcoinaddressvalidator.cpp \ - cryptopp/src/sha.cpp \ - cryptopp/src/cpu.cpp \ - core/src/util.cpp \ - core/src/script.cpp \ - core/src/main.cpp \ - core/src/init.cpp \ - core/src/rpc.cpp \ - core/src/net.cpp \ - core/src/irc.cpp \ - core/src/db.cpp \ - json/src/json_spirit_writer.cpp \ - json/src/json_spirit_value.cpp \ - json/src/json_spirit_reader.cpp \ - gui/src/clientmodel.cpp \ - gui/src/guiutil.cpp \ - gui/src/transactionrecord.cpp \ - gui/src/optionsmodel.cpp \ - gui/src/monitoreddatamapper.cpp \ - gui/src/transactiondesc.cpp \ - gui/src/transactiondescdialog.cpp +DEPENDPATH += src/qt src src/cryptopp src json/include +HEADERS += src/qt/bitcoingui.h \ + src/qt/transactiontablemodel.h \ + src/qt/addresstablemodel.h \ + src/qt/optionsdialog.h \ + src/qt/sendcoinsdialog.h \ + src/qt/addressbookdialog.h \ + src/qt/aboutdialog.h \ + src/qt/editaddressdialog.h \ + src/qt/bitcoinaddressvalidator.h \ + src/base58.h \ + src/bignum.h \ + src/util.h \ + src/uint256.h \ + src/serialize.h \ + src/cryptopp/stdcpp.h \ + src/cryptopp/smartptr.h \ + src/cryptopp/simple.h \ + src/cryptopp/sha.h \ + src/cryptopp/secblock.h \ + src/cryptopp/pch.h \ + src/cryptopp/misc.h \ + src/cryptopp/iterhash.h \ + src/cryptopp/cryptlib.h \ + src/cryptopp/cpu.h \ + src/cryptopp/config.h \ + src/strlcpy.h \ + src/main.h \ + src/net.h \ + src/key.h \ + src/db.h \ + src/script.h \ + src/noui.h \ + src/init.h \ + src/headers.h \ + src/irc.h \ + src/json/json_spirit_writer_template.h \ + src/json/json_spirit_writer.h \ + src/json/json_spirit_value.h \ + src/json/json_spirit_utils.h \ + src/json/json_spirit_stream_reader.h \ + src/json/json_spirit_reader_template.h \ + src/json/json_spirit_reader.h \ + src/json/json_spirit_error_position.h \ + src/json/json_spirit.h \ + src/rpc.h \ + src/qt/clientmodel.h \ + src/qt/guiutil.h \ + src/qt/transactionrecord.h \ + src/qt/guiconstants.h \ + src/qt/optionsmodel.h \ + src/qt/monitoreddatamapper.h \ + src/externui.h \ + src/qt/transactiondesc.h \ + src/qt/transactiondescdialog.h +SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ + src/qt/transactiontablemodel.cpp \ + src/qt/addresstablemodel.cpp \ + src/qt/optionsdialog.cpp \ + src/qt/sendcoinsdialog.cpp \ + src/qt/addressbookdialog.cpp \ + src/qt/aboutdialog.cpp \ + src/qt/editaddressdialog.cpp \ + src/qt/bitcoinaddressvalidator.cpp \ + src/cryptopp/sha.cpp \ + src/cryptopp/cpu.cpp \ + src/util.cpp \ + src/script.cpp \ + src/main.cpp \ + src/init.cpp \ + src/rpc.cpp \ + src/net.cpp \ + src/irc.cpp \ + src/db.cpp \ + src/json/json_spirit_writer.cpp \ + src/json/json_spirit_value.cpp \ + src/json/json_spirit_reader.cpp \ + src/qt/clientmodel.cpp \ + src/qt/guiutil.cpp \ + src/qt/transactionrecord.cpp \ + src/qt/optionsmodel.cpp \ + src/qt/monitoreddatamapper.cpp \ + src/qt/transactiondesc.cpp \ + src/qt/transactiondescdialog.cpp RESOURCES += \ - gui/bitcoin.qrc + src/qt/bitcoin.qrc FORMS += \ - gui/forms/sendcoinsdialog.ui \ - gui/forms/addressbookdialog.ui \ - gui/forms/aboutdialog.ui \ - gui/forms/editaddressdialog.ui \ - gui/forms/transactiondescdialog.ui + src/qt/forms/sendcoinsdialog.ui \ + src/qt/forms/addressbookdialog.ui \ + src/qt/forms/aboutdialog.ui \ + src/qt/forms/editaddressdialog.ui \ + src/qt/forms/transactiondescdialog.ui diff --git a/core/include/base58.h b/core/include/base58.h deleted file mode 100644 index 580bd3fc63..0000000000 --- a/core/include/base58.h +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. - - -// -// Why base-58 instead of standard base-64 encoding? -// - Don't want 0OIl characters that look the same in some fonts and -// could be used to create visually identical looking account numbers. -// - A string with non-alphanumeric characters is not as easily accepted as an account number. -// - E-mail usually won't line-break if there's no punctuation to break at. -// - Doubleclicking selects the whole number as one word if it's all alphanumeric. -// -#ifndef BITCOIN_BASE58_H -#define BITCOIN_BASE58_H - -#include -#include -#include "bignum.h" - -static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - - -inline std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) -{ - CAutoBN_CTX pctx; - CBigNum bn58 = 58; - CBigNum bn0 = 0; - - // Convert big endian data to little endian - // Extra zero at the end make sure bignum will interpret as a positive number - std::vector vchTmp(pend-pbegin+1, 0); - reverse_copy(pbegin, pend, vchTmp.begin()); - - // Convert little endian data to bignum - CBigNum bn; - bn.setvch(vchTmp); - - // Convert bignum to std::string - std::string str; - str.reserve((pend - pbegin) * 138 / 100 + 1); - CBigNum dv; - CBigNum rem; - while (bn > bn0) - { - if (!BN_div(&dv, &rem, &bn, &bn58, pctx)) - throw bignum_error("EncodeBase58 : BN_div failed"); - bn = dv; - unsigned int c = rem.getulong(); - str += pszBase58[c]; - } - - // Leading zeroes encoded as base58 zeros - for (const unsigned char* p = pbegin; p < pend && *p == 0; p++) - str += pszBase58[0]; - - // Convert little endian std::string to big endian - reverse(str.begin(), str.end()); - return str; -} - -inline std::string EncodeBase58(const std::vector& vch) -{ - return EncodeBase58(&vch[0], &vch[0] + vch.size()); -} - -inline bool DecodeBase58(const char* psz, std::vector& vchRet) -{ - CAutoBN_CTX pctx; - vchRet.clear(); - CBigNum bn58 = 58; - CBigNum bn = 0; - CBigNum bnChar; - while (isspace(*psz)) - psz++; - - // Convert big endian string to bignum - for (const char* p = psz; *p; p++) - { - const char* p1 = strchr(pszBase58, *p); - if (p1 == NULL) - { - while (isspace(*p)) - p++; - if (*p != '\0') - return false; - break; - } - bnChar.setulong(p1 - pszBase58); - if (!BN_mul(&bn, &bn, &bn58, pctx)) - throw bignum_error("DecodeBase58 : BN_mul failed"); - bn += bnChar; - } - - // Get bignum as little endian data - std::vector vchTmp = bn.getvch(); - - // Trim off sign byte if present - if (vchTmp.size() >= 2 && vchTmp.end()[-1] == 0 && vchTmp.end()[-2] >= 0x80) - vchTmp.erase(vchTmp.end()-1); - - // Restore leading zeros - int nLeadingZeros = 0; - for (const char* p = psz; *p == pszBase58[0]; p++) - nLeadingZeros++; - vchRet.assign(nLeadingZeros + vchTmp.size(), 0); - - // Convert little endian data to big endian - reverse_copy(vchTmp.begin(), vchTmp.end(), vchRet.end() - vchTmp.size()); - return true; -} - -inline bool DecodeBase58(const std::string& str, std::vector& vchRet) -{ - return DecodeBase58(str.c_str(), vchRet); -} - - - - - -inline std::string EncodeBase58Check(const std::vector& vchIn) -{ - // add 4-byte hash check to the end - std::vector vch(vchIn); - uint256 hash = Hash(vch.begin(), vch.end()); - vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4); - return EncodeBase58(vch); -} - -inline bool DecodeBase58Check(const char* psz, std::vector& vchRet) -{ - if (!DecodeBase58(psz, vchRet)) - return false; - if (vchRet.size() < 4) - { - vchRet.clear(); - return false; - } - uint256 hash = Hash(vchRet.begin(), vchRet.end()-4); - if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) - { - vchRet.clear(); - return false; - } - vchRet.resize(vchRet.size()-4); - return true; -} - -inline bool DecodeBase58Check(const std::string& str, std::vector& vchRet) -{ - return DecodeBase58Check(str.c_str(), vchRet); -} - - - - - - -#define ADDRESSVERSION ((unsigned char)(fTestNet ? 111 : 0)) - -inline std::string Hash160ToAddress(uint160 hash160) -{ - // add 1-byte version number to the front - std::vector vch(1, ADDRESSVERSION); - vch.insert(vch.end(), UBEGIN(hash160), UEND(hash160)); - return EncodeBase58Check(vch); -} - -inline bool AddressToHash160(const char* psz, uint160& hash160Ret) -{ - std::vector vch; - if (!DecodeBase58Check(psz, vch)) - return false; - if (vch.empty()) - return false; - unsigned char nVersion = vch[0]; - if (vch.size() != sizeof(hash160Ret) + 1) - return false; - memcpy(&hash160Ret, &vch[1], sizeof(hash160Ret)); - return (nVersion <= ADDRESSVERSION); -} - -inline bool AddressToHash160(const std::string& str, uint160& hash160Ret) -{ - return AddressToHash160(str.c_str(), hash160Ret); -} - -inline bool IsValidBitcoinAddress(const char* psz) -{ - uint160 hash160; - return AddressToHash160(psz, hash160); -} - -inline bool IsValidBitcoinAddress(const std::string& str) -{ - return IsValidBitcoinAddress(str.c_str()); -} - - - - -inline std::string PubKeyToAddress(const std::vector& vchPubKey) -{ - return Hash160ToAddress(Hash160(vchPubKey)); -} - -#endif diff --git a/core/include/bignum.h b/core/include/bignum.h deleted file mode 100644 index 5b4c78e7fa..0000000000 --- a/core/include/bignum.h +++ /dev/null @@ -1,534 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_BIGNUM_H -#define BITCOIN_BIGNUM_H - -#include -#include -#include - -#include "util.h" - -class bignum_error : public std::runtime_error -{ -public: - explicit bignum_error(const std::string& str) : std::runtime_error(str) {} -}; - - - -class CAutoBN_CTX -{ -protected: - BN_CTX* pctx; - BN_CTX* operator=(BN_CTX* pnew) { return pctx = pnew; } - -public: - CAutoBN_CTX() - { - pctx = BN_CTX_new(); - if (pctx == NULL) - throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned NULL"); - } - - ~CAutoBN_CTX() - { - if (pctx != NULL) - BN_CTX_free(pctx); - } - - operator BN_CTX*() { return pctx; } - BN_CTX& operator*() { return *pctx; } - BN_CTX** operator&() { return &pctx; } - bool operator!() { return (pctx == NULL); } -}; - - - -class CBigNum : public BIGNUM -{ -public: - CBigNum() - { - BN_init(this); - } - - CBigNum(const CBigNum& b) - { - BN_init(this); - if (!BN_copy(this, &b)) - { - BN_clear_free(this); - throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_copy failed"); - } - } - - CBigNum& operator=(const CBigNum& b) - { - if (!BN_copy(this, &b)) - throw bignum_error("CBigNum::operator= : BN_copy failed"); - return (*this); - } - - ~CBigNum() - { - BN_clear_free(this); - } - - CBigNum(char n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(short n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(int n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(long n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(int64 n) { BN_init(this); setint64(n); } - CBigNum(unsigned char n) { BN_init(this); setulong(n); } - CBigNum(unsigned short n) { BN_init(this); setulong(n); } - CBigNum(unsigned int n) { BN_init(this); setulong(n); } - CBigNum(unsigned long n) { BN_init(this); setulong(n); } - CBigNum(uint64 n) { BN_init(this); setuint64(n); } - explicit CBigNum(uint256 n) { BN_init(this); setuint256(n); } - - explicit CBigNum(const std::vector& vch) - { - BN_init(this); - setvch(vch); - } - - void setulong(unsigned long n) - { - if (!BN_set_word(this, n)) - throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed"); - } - - unsigned long getulong() const - { - return BN_get_word(this); - } - - unsigned int getuint() const - { - return BN_get_word(this); - } - - int getint() const - { - unsigned long n = BN_get_word(this); - if (!BN_is_negative(this)) - return (n > INT_MAX ? INT_MAX : n); - else - return (n > INT_MAX ? INT_MIN : -(int)n); - } - - void setint64(int64 n) - { - unsigned char pch[sizeof(n) + 6]; - unsigned char* p = pch + 4; - bool fNegative = false; - if (n < (int64)0) - { - n = -n; - fNegative = true; - } - bool fLeadingZeroes = true; - for (int i = 0; i < 8; i++) - { - unsigned char c = (n >> 56) & 0xff; - n <<= 8; - if (fLeadingZeroes) - { - if (c == 0) - continue; - if (c & 0x80) - *p++ = (fNegative ? 0x80 : 0); - else if (fNegative) - c |= 0x80; - fLeadingZeroes = false; - } - *p++ = c; - } - unsigned int nSize = p - (pch + 4); - pch[0] = (nSize >> 24) & 0xff; - pch[1] = (nSize >> 16) & 0xff; - pch[2] = (nSize >> 8) & 0xff; - pch[3] = (nSize) & 0xff; - BN_mpi2bn(pch, p - pch, this); - } - - void setuint64(uint64 n) - { - unsigned char pch[sizeof(n) + 6]; - unsigned char* p = pch + 4; - bool fLeadingZeroes = true; - for (int i = 0; i < 8; i++) - { - unsigned char c = (n >> 56) & 0xff; - n <<= 8; - if (fLeadingZeroes) - { - if (c == 0) - continue; - if (c & 0x80) - *p++ = 0; - fLeadingZeroes = false; - } - *p++ = c; - } - unsigned int nSize = p - (pch + 4); - pch[0] = (nSize >> 24) & 0xff; - pch[1] = (nSize >> 16) & 0xff; - pch[2] = (nSize >> 8) & 0xff; - pch[3] = (nSize) & 0xff; - BN_mpi2bn(pch, p - pch, this); - } - - void setuint256(uint256 n) - { - unsigned char pch[sizeof(n) + 6]; - unsigned char* p = pch + 4; - bool fLeadingZeroes = true; - unsigned char* pbegin = (unsigned char*)&n; - unsigned char* psrc = pbegin + sizeof(n); - while (psrc != pbegin) - { - unsigned char c = *(--psrc); - if (fLeadingZeroes) - { - if (c == 0) - continue; - if (c & 0x80) - *p++ = 0; - fLeadingZeroes = false; - } - *p++ = c; - } - unsigned int nSize = p - (pch + 4); - pch[0] = (nSize >> 24) & 0xff; - pch[1] = (nSize >> 16) & 0xff; - pch[2] = (nSize >> 8) & 0xff; - pch[3] = (nSize >> 0) & 0xff; - BN_mpi2bn(pch, p - pch, this); - } - - uint256 getuint256() - { - unsigned int nSize = BN_bn2mpi(this, NULL); - if (nSize < 4) - return 0; - std::vector vch(nSize); - BN_bn2mpi(this, &vch[0]); - if (vch.size() > 4) - vch[4] &= 0x7f; - uint256 n = 0; - for (int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) - ((unsigned char*)&n)[i] = vch[j]; - return n; - } - - void setvch(const std::vector& vch) - { - std::vector vch2(vch.size() + 4); - unsigned int nSize = vch.size(); - vch2[0] = (nSize >> 24) & 0xff; - vch2[1] = (nSize >> 16) & 0xff; - vch2[2] = (nSize >> 8) & 0xff; - vch2[3] = (nSize >> 0) & 0xff; - reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4); - BN_mpi2bn(&vch2[0], vch2.size(), this); - } - - std::vector getvch() const - { - unsigned int nSize = BN_bn2mpi(this, NULL); - if (nSize < 4) - return std::vector(); - std::vector vch(nSize); - BN_bn2mpi(this, &vch[0]); - vch.erase(vch.begin(), vch.begin() + 4); - reverse(vch.begin(), vch.end()); - return vch; - } - - CBigNum& SetCompact(unsigned int nCompact) - { - unsigned int nSize = nCompact >> 24; - std::vector vch(4 + nSize); - vch[3] = nSize; - if (nSize >= 1) vch[4] = (nCompact >> 16) & 0xff; - if (nSize >= 2) vch[5] = (nCompact >> 8) & 0xff; - if (nSize >= 3) vch[6] = (nCompact >> 0) & 0xff; - BN_mpi2bn(&vch[0], vch.size(), this); - return *this; - } - - unsigned int GetCompact() const - { - unsigned int nSize = BN_bn2mpi(this, NULL); - std::vector vch(nSize); - nSize -= 4; - BN_bn2mpi(this, &vch[0]); - unsigned int nCompact = nSize << 24; - if (nSize >= 1) nCompact |= (vch[4] << 16); - if (nSize >= 2) nCompact |= (vch[5] << 8); - if (nSize >= 3) nCompact |= (vch[6] << 0); - return nCompact; - } - - void SetHex(const std::string& str) - { - // skip 0x - const char* psz = str.c_str(); - while (isspace(*psz)) - psz++; - bool fNegative = false; - if (*psz == '-') - { - fNegative = true; - psz++; - } - if (psz[0] == '0' && tolower(psz[1]) == 'x') - psz += 2; - while (isspace(*psz)) - psz++; - - // hex string to bignum - static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; - *this = 0; - while (isxdigit(*psz)) - { - *this <<= 4; - int n = phexdigit[*psz++]; - *this += n; - } - if (fNegative) - *this = 0 - *this; - } - - std::string ToString(int nBase=10) const - { - CAutoBN_CTX pctx; - CBigNum bnBase = nBase; - CBigNum bn0 = 0; - std::string str; - CBigNum bn = *this; - BN_set_negative(&bn, false); - CBigNum dv; - CBigNum rem; - if (BN_cmp(&bn, &bn0) == 0) - return "0"; - while (BN_cmp(&bn, &bn0) > 0) - { - if (!BN_div(&dv, &rem, &bn, &bnBase, pctx)) - throw bignum_error("CBigNum::ToString() : BN_div failed"); - bn = dv; - unsigned int c = rem.getulong(); - str += "0123456789abcdef"[c]; - } - if (BN_is_negative(this)) - str += "-"; - reverse(str.begin(), str.end()); - return str; - } - - std::string GetHex() const - { - return ToString(16); - } - - unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const - { - return ::GetSerializeSize(getvch(), nType, nVersion); - } - - template - void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const - { - ::Serialize(s, getvch(), nType, nVersion); - } - - template - void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) - { - std::vector vch; - ::Unserialize(s, vch, nType, nVersion); - setvch(vch); - } - - - bool operator!() const - { - return BN_is_zero(this); - } - - CBigNum& operator+=(const CBigNum& b) - { - if (!BN_add(this, this, &b)) - throw bignum_error("CBigNum::operator+= : BN_add failed"); - return *this; - } - - CBigNum& operator-=(const CBigNum& b) - { - *this = *this - b; - return *this; - } - - CBigNum& operator*=(const CBigNum& b) - { - CAutoBN_CTX pctx; - if (!BN_mul(this, this, &b, pctx)) - throw bignum_error("CBigNum::operator*= : BN_mul failed"); - return *this; - } - - CBigNum& operator/=(const CBigNum& b) - { - *this = *this / b; - return *this; - } - - CBigNum& operator%=(const CBigNum& b) - { - *this = *this % b; - return *this; - } - - CBigNum& operator<<=(unsigned int shift) - { - if (!BN_lshift(this, this, shift)) - throw bignum_error("CBigNum:operator<<= : BN_lshift failed"); - return *this; - } - - CBigNum& operator>>=(unsigned int shift) - { - // Note: BN_rshift segfaults on 64-bit if 2^shift is greater than the number - // if built on ubuntu 9.04 or 9.10, probably depends on version of openssl - CBigNum a = 1; - a <<= shift; - if (BN_cmp(&a, this) > 0) - { - *this = 0; - return *this; - } - - if (!BN_rshift(this, this, shift)) - throw bignum_error("CBigNum:operator>>= : BN_rshift failed"); - return *this; - } - - - CBigNum& operator++() - { - // prefix operator - if (!BN_add(this, this, BN_value_one())) - throw bignum_error("CBigNum::operator++ : BN_add failed"); - return *this; - } - - const CBigNum operator++(int) - { - // postfix operator - const CBigNum ret = *this; - ++(*this); - return ret; - } - - CBigNum& operator--() - { - // prefix operator - CBigNum r; - if (!BN_sub(&r, this, BN_value_one())) - throw bignum_error("CBigNum::operator-- : BN_sub failed"); - *this = r; - return *this; - } - - const CBigNum operator--(int) - { - // postfix operator - const CBigNum ret = *this; - --(*this); - return ret; - } - - - friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b); - friend inline const CBigNum operator/(const CBigNum& a, const CBigNum& b); - friend inline const CBigNum operator%(const CBigNum& a, const CBigNum& b); -}; - - - -inline const CBigNum operator+(const CBigNum& a, const CBigNum& b) -{ - CBigNum r; - if (!BN_add(&r, &a, &b)) - throw bignum_error("CBigNum::operator+ : BN_add failed"); - return r; -} - -inline const CBigNum operator-(const CBigNum& a, const CBigNum& b) -{ - CBigNum r; - if (!BN_sub(&r, &a, &b)) - throw bignum_error("CBigNum::operator- : BN_sub failed"); - return r; -} - -inline const CBigNum operator-(const CBigNum& a) -{ - CBigNum r(a); - BN_set_negative(&r, !BN_is_negative(&r)); - return r; -} - -inline const CBigNum operator*(const CBigNum& a, const CBigNum& b) -{ - CAutoBN_CTX pctx; - CBigNum r; - if (!BN_mul(&r, &a, &b, pctx)) - throw bignum_error("CBigNum::operator* : BN_mul failed"); - return r; -} - -inline const CBigNum operator/(const CBigNum& a, const CBigNum& b) -{ - CAutoBN_CTX pctx; - CBigNum r; - if (!BN_div(&r, NULL, &a, &b, pctx)) - throw bignum_error("CBigNum::operator/ : BN_div failed"); - return r; -} - -inline const CBigNum operator%(const CBigNum& a, const CBigNum& b) -{ - CAutoBN_CTX pctx; - CBigNum r; - if (!BN_mod(&r, &a, &b, pctx)) - throw bignum_error("CBigNum::operator% : BN_div failed"); - return r; -} - -inline const CBigNum operator<<(const CBigNum& a, unsigned int shift) -{ - CBigNum r; - if (!BN_lshift(&r, &a, shift)) - throw bignum_error("CBigNum:operator<< : BN_lshift failed"); - return r; -} - -inline const CBigNum operator>>(const CBigNum& a, unsigned int shift) -{ - CBigNum r = a; - r >>= shift; - return r; -} - -inline bool operator==(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) == 0); } -inline bool operator!=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) != 0); } -inline bool operator<=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) <= 0); } -inline bool operator>=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) >= 0); } -inline bool operator<(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) < 0); } -inline bool operator>(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) > 0); } - -#endif diff --git a/core/include/db.h b/core/include/db.h deleted file mode 100644 index 9826194ed0..0000000000 --- a/core/include/db.h +++ /dev/null @@ -1,526 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_DB_H -#define BITCOIN_DB_H - -#include "key.h" - -#include -#include -#include - -#include - -class CTransaction; -class CTxIndex; -class CDiskBlockIndex; -class CDiskTxPos; -class COutPoint; -class CUser; -class CReview; -class CAddress; -class CWalletTx; -class CAccount; -class CAccountingEntry; -class CBlockLocator; - -extern std::map mapAddressBook; -extern CCriticalSection cs_mapAddressBook; -extern std::vector vchDefaultKey; -extern bool fClient; -extern int nBestHeight; - - -extern unsigned int nWalletDBUpdated; -extern DbEnv dbenv; - - -extern void DBFlush(bool fShutdown); -extern std::vector GetKeyFromKeyPool(); -extern int64 GetOldestKeyPoolTime(); - - - - -class CDB -{ -protected: - Db* pdb; - std::string strFile; - std::vector vTxn; - bool fReadOnly; - - explicit CDB(const char* pszFile, const char* pszMode="r+"); - ~CDB() { Close(); } -public: - void Close(); -private: - CDB(const CDB&); - void operator=(const CDB&); - -protected: - template - bool Read(const K& key, T& value) - { - if (!pdb) - return false; - - // Key - CDataStream ssKey(SER_DISK); - ssKey.reserve(1000); - ssKey << key; - Dbt datKey(&ssKey[0], ssKey.size()); - - // Read - Dbt datValue; - datValue.set_flags(DB_DBT_MALLOC); - int ret = pdb->get(GetTxn(), &datKey, &datValue, 0); - memset(datKey.get_data(), 0, datKey.get_size()); - if (datValue.get_data() == NULL) - return false; - - // Unserialize value - CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK); - ssValue >> value; - - // Clear and free memory - memset(datValue.get_data(), 0, datValue.get_size()); - free(datValue.get_data()); - return (ret == 0); - } - - template - bool Write(const K& key, const T& value, bool fOverwrite=true) - { - if (!pdb) - return false; - if (fReadOnly) - assert(("Write called on database in read-only mode", false)); - - // Key - CDataStream ssKey(SER_DISK); - ssKey.reserve(1000); - ssKey << key; - Dbt datKey(&ssKey[0], ssKey.size()); - - // Value - CDataStream ssValue(SER_DISK); - ssValue.reserve(10000); - ssValue << value; - Dbt datValue(&ssValue[0], ssValue.size()); - - // Write - int ret = pdb->put(GetTxn(), &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); - - // Clear memory in case it was a private key - memset(datKey.get_data(), 0, datKey.get_size()); - memset(datValue.get_data(), 0, datValue.get_size()); - return (ret == 0); - } - - template - bool Erase(const K& key) - { - if (!pdb) - return false; - if (fReadOnly) - assert(("Erase called on database in read-only mode", false)); - - // Key - CDataStream ssKey(SER_DISK); - ssKey.reserve(1000); - ssKey << key; - Dbt datKey(&ssKey[0], ssKey.size()); - - // Erase - int ret = pdb->del(GetTxn(), &datKey, 0); - - // Clear memory - memset(datKey.get_data(), 0, datKey.get_size()); - return (ret == 0 || ret == DB_NOTFOUND); - } - - template - bool Exists(const K& key) - { - if (!pdb) - return false; - - // Key - CDataStream ssKey(SER_DISK); - ssKey.reserve(1000); - ssKey << key; - Dbt datKey(&ssKey[0], ssKey.size()); - - // Exists - int ret = pdb->exists(GetTxn(), &datKey, 0); - - // Clear memory - memset(datKey.get_data(), 0, datKey.get_size()); - return (ret == 0); - } - - Dbc* GetCursor() - { - if (!pdb) - return NULL; - Dbc* pcursor = NULL; - int ret = pdb->cursor(NULL, &pcursor, 0); - if (ret != 0) - return NULL; - return pcursor; - } - - int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags=DB_NEXT) - { - // Read at cursor - Dbt datKey; - if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) - { - datKey.set_data(&ssKey[0]); - datKey.set_size(ssKey.size()); - } - Dbt datValue; - if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) - { - datValue.set_data(&ssValue[0]); - datValue.set_size(ssValue.size()); - } - datKey.set_flags(DB_DBT_MALLOC); - datValue.set_flags(DB_DBT_MALLOC); - int ret = pcursor->get(&datKey, &datValue, fFlags); - if (ret != 0) - return ret; - else if (datKey.get_data() == NULL || datValue.get_data() == NULL) - return 99999; - - // Convert to streams - ssKey.SetType(SER_DISK); - ssKey.clear(); - ssKey.write((char*)datKey.get_data(), datKey.get_size()); - ssValue.SetType(SER_DISK); - ssValue.clear(); - ssValue.write((char*)datValue.get_data(), datValue.get_size()); - - // Clear and free memory - memset(datKey.get_data(), 0, datKey.get_size()); - memset(datValue.get_data(), 0, datValue.get_size()); - free(datKey.get_data()); - free(datValue.get_data()); - return 0; - } - - DbTxn* GetTxn() - { - if (!vTxn.empty()) - return vTxn.back(); - else - return NULL; - } - -public: - bool TxnBegin() - { - if (!pdb) - return false; - DbTxn* ptxn = NULL; - int ret = dbenv.txn_begin(GetTxn(), &ptxn, DB_TXN_NOSYNC); - if (!ptxn || ret != 0) - return false; - vTxn.push_back(ptxn); - return true; - } - - bool TxnCommit() - { - if (!pdb) - return false; - if (vTxn.empty()) - return false; - int ret = vTxn.back()->commit(0); - vTxn.pop_back(); - return (ret == 0); - } - - bool TxnAbort() - { - if (!pdb) - return false; - if (vTxn.empty()) - return false; - int ret = vTxn.back()->abort(); - vTxn.pop_back(); - return (ret == 0); - } - - bool ReadVersion(int& nVersion) - { - nVersion = 0; - return Read(std::string("version"), nVersion); - } - - bool WriteVersion(int nVersion) - { - return Write(std::string("version"), nVersion); - } -}; - - - - - - - - -class CTxDB : public CDB -{ -public: - CTxDB(const char* pszMode="r+") : CDB("blkindex.dat", pszMode) { } -private: - CTxDB(const CTxDB&); - void operator=(const CTxDB&); -public: - bool ReadTxIndex(uint256 hash, CTxIndex& txindex); - bool UpdateTxIndex(uint256 hash, const CTxIndex& txindex); - bool AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight); - bool EraseTxIndex(const CTransaction& tx); - bool ContainsTx(uint256 hash); - bool ReadOwnerTxes(uint160 hash160, int nHeight, std::vector& vtx); - bool ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex); - bool ReadDiskTx(uint256 hash, CTransaction& tx); - bool ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex); - bool ReadDiskTx(COutPoint outpoint, CTransaction& tx); - bool WriteBlockIndex(const CDiskBlockIndex& blockindex); - bool EraseBlockIndex(uint256 hash); - bool ReadHashBestChain(uint256& hashBestChain); - bool WriteHashBestChain(uint256 hashBestChain); - bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork); - bool WriteBestInvalidWork(CBigNum bnBestInvalidWork); - bool LoadBlockIndex(); -}; - - - - - -class CAddrDB : public CDB -{ -public: - CAddrDB(const char* pszMode="r+") : CDB("addr.dat", pszMode) { } -private: - CAddrDB(const CAddrDB&); - void operator=(const CAddrDB&); -public: - bool WriteAddress(const CAddress& addr); - bool EraseAddress(const CAddress& addr); - bool LoadAddresses(); -}; - -bool LoadAddresses(); - - - - - - -class CKeyPool -{ -public: - int64 nTime; - std::vector vchPubKey; - - CKeyPool() - { - nTime = GetTime(); - } - - CKeyPool(const std::vector& vchPubKeyIn) - { - nTime = GetTime(); - vchPubKey = vchPubKeyIn; - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(nTime); - READWRITE(vchPubKey); - ) -}; - - - - -class CWalletDB : public CDB -{ -public: - CWalletDB(const char* pszMode="r+") : CDB("wallet.dat", pszMode) - { - } -private: - CWalletDB(const CWalletDB&); - void operator=(const CWalletDB&); -public: - bool ReadName(const std::string& strAddress, std::string& strName) - { - strName = ""; - return Read(std::make_pair(std::string("name"), strAddress), strName); - } - - bool WriteName(const std::string& strAddress, const std::string& strName) - { - CRITICAL_BLOCK(cs_mapAddressBook) - mapAddressBook[strAddress] = strName; - nWalletDBUpdated++; - return Write(std::make_pair(std::string("name"), strAddress), strName); - } - - bool EraseName(const std::string& strAddress) - { - // This should only be used for sending addresses, never for receiving addresses, - // receiving addresses must always have an address book entry if they're not change return. - CRITICAL_BLOCK(cs_mapAddressBook) - mapAddressBook.erase(strAddress); - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("name"), strAddress)); - } - - bool ReadTx(uint256 hash, CWalletTx& wtx) - { - return Read(std::make_pair(std::string("tx"), hash), wtx); - } - - bool WriteTx(uint256 hash, const CWalletTx& wtx) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("tx"), hash), wtx); - } - - bool EraseTx(uint256 hash) - { - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("tx"), hash)); - } - - bool ReadKey(const std::vector& vchPubKey, CPrivKey& vchPrivKey) - { - vchPrivKey.clear(); - return Read(std::make_pair(std::string("key"), vchPubKey), vchPrivKey); - } - - bool WriteKey(const std::vector& vchPubKey, const CPrivKey& vchPrivKey) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false); - } - - bool WriteBestBlock(const CBlockLocator& locator) - { - nWalletDBUpdated++; - return Write(std::string("bestblock"), locator); - } - - bool ReadBestBlock(CBlockLocator& locator) - { - return Read(std::string("bestblock"), locator); - } - - bool ReadDefaultKey(std::vector& vchPubKey) - { - vchPubKey.clear(); - return Read(std::string("defaultkey"), vchPubKey); - } - - bool WriteDefaultKey(const std::vector& vchPubKey) - { - vchDefaultKey = vchPubKey; - nWalletDBUpdated++; - return Write(std::string("defaultkey"), vchPubKey); - } - - template - bool ReadSetting(const std::string& strKey, T& value) - { - return Read(std::make_pair(std::string("setting"), strKey), value); - } - - template - bool WriteSetting(const std::string& strKey, const T& value) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("setting"), strKey), value); - } - - bool ReadAccount(const std::string& strAccount, CAccount& account); - bool WriteAccount(const std::string& strAccount, const CAccount& account); - bool WriteAccountingEntry(const CAccountingEntry& acentry); - int64 GetAccountCreditDebit(const std::string& strAccount); - void ListAccountCreditDebit(const std::string& strAccount, std::list& acentries); - - bool LoadWallet(); -protected: - void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool); - void KeepKey(int64 nIndex); - static void ReturnKey(int64 nIndex); - friend class CReserveKey; - friend std::vector GetKeyFromKeyPool(); - friend int64 GetOldestKeyPoolTime(); -}; - -bool LoadWallet(bool& fFirstRunRet); -void BackupWallet(const std::string& strDest); - -inline bool SetAddressBookName(const std::string& strAddress, const std::string& strName) -{ - return CWalletDB().WriteName(strAddress, strName); -} - -class CReserveKey -{ -protected: - int64 nIndex; - std::vector vchPubKey; -public: - CReserveKey() - { - nIndex = -1; - } - - ~CReserveKey() - { - if (!fShutdown) - ReturnKey(); - } - - std::vector GetReservedKey() - { - if (nIndex == -1) - { - CKeyPool keypool; - CWalletDB().ReserveKeyFromKeyPool(nIndex, keypool); - vchPubKey = keypool.vchPubKey; - } - assert(!vchPubKey.empty()); - return vchPubKey; - } - - void KeepKey() - { - if (nIndex != -1) - CWalletDB().KeepKey(nIndex); - nIndex = -1; - vchPubKey.clear(); - } - - void ReturnKey() - { - if (nIndex != -1) - CWalletDB::ReturnKey(nIndex); - nIndex = -1; - vchPubKey.clear(); - } -}; - -#endif diff --git a/core/include/externui.h b/core/include/externui.h deleted file mode 100644 index e58ccc2242..0000000000 --- a/core/include/externui.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_EXTERNUI_H -#define BITCOIN_EXTERNUI_H - -#include - -typedef void wxWindow; -#define wxYES 0x00000002 -#define wxOK 0x00000004 -#define wxNO 0x00000008 -#define wxYES_NO (wxYES|wxNO) -#define wxCANCEL 0x00000010 -#define wxAPPLY 0x00000020 -#define wxCLOSE 0x00000040 -#define wxOK_DEFAULT 0x00000000 -#define wxYES_DEFAULT 0x00000000 -#define wxNO_DEFAULT 0x00000080 -#define wxCANCEL_DEFAULT 0x80000000 -#define wxICON_EXCLAMATION 0x00000100 -#define wxICON_HAND 0x00000200 -#define wxICON_WARNING wxICON_EXCLAMATION -#define wxICON_ERROR wxICON_HAND -#define wxICON_QUESTION 0x00000400 -#define wxICON_INFORMATION 0x00000800 -#define wxICON_STOP wxICON_HAND -#define wxICON_ASTERISK wxICON_INFORMATION -#define wxICON_MASK (0x00000100|0x00000200|0x00000400|0x00000800) -#define wxFORWARD 0x00001000 -#define wxBACKWARD 0x00002000 -#define wxRESET 0x00004000 -#define wxHELP 0x00008000 -#define wxMORE 0x00010000 -#define wxSETUP 0x00020000 - -extern int MyMessageBox(const std::string& message, const std::string& caption="Message", int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1); -#define wxMessageBox MyMessageBox -extern int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1); -extern bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent); -extern void CalledSetStatusBar(const std::string& strText, int nField); -extern void UIThreadCall(boost::function0 fn); -extern void MainFrameRepaint(); - -#endif diff --git a/core/include/headers.h b/core/include/headers.h deleted file mode 100644 index 33aeef3383..0000000000 --- a/core/include/headers.h +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. - -#ifdef _MSC_VER -#pragma warning(disable:4786) -#pragma warning(disable:4804) -#pragma warning(disable:4805) -#pragma warning(disable:4717) -#endif -#ifdef _WIN32_WINNT -#undef _WIN32_WINNT -#endif -#define _WIN32_WINNT 0x0500 -#ifdef _WIN32_IE -#undef _WIN32_IE -#endif -#define _WIN32_IE 0x0400 -#define WIN32_LEAN_AND_MEAN 1 -#define __STDC_LIMIT_MACROS // to enable UINT64_MAX from stdint.h -#if (defined(__unix__) || defined(unix)) && !defined(USG) -#include // to get BSD define -#endif -#ifdef __WXMAC_OSX__ -#ifndef BSD -#define BSD 1 -#endif -#endif -#ifdef GUI -#include -#include -#include -#include -#include -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __WXMSW__ -#include -#include -#include -#include -#include -#include -#include -#include -#else -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif -#ifdef BSD -#include -#endif - - -#pragma hdrstop - -#include "strlcpy.h" -#include "serialize.h" -#include "uint256.h" -#include "util.h" -#include "key.h" -#include "bignum.h" -#include "base58.h" -#include "script.h" -#include "db.h" -#include "net.h" -#include "irc.h" -#include "main.h" -#include "rpc.h" -#ifdef GUI -#include "uibase.h" -#include "ui.h" -#else -#include "externui.h" -#endif -#include "init.h" - -#ifdef GUI -#include "xpm/addressbook16.xpm" -#include "xpm/addressbook20.xpm" -#include "xpm/bitcoin16.xpm" -#include "xpm/bitcoin20.xpm" -#include "xpm/bitcoin32.xpm" -#include "xpm/bitcoin48.xpm" -#include "xpm/bitcoin80.xpm" -#include "xpm/check.xpm" -#include "xpm/send16.xpm" -#include "xpm/send16noshadow.xpm" -#include "xpm/send20.xpm" -#include "xpm/about.xpm" -#endif diff --git a/core/include/init.h b/core/include/init.h deleted file mode 100644 index 61b2728576..0000000000 --- a/core/include/init.h +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_INIT_H -#define BITCOIN_INIT_H - -void Shutdown(void* parg); -bool AppInit(int argc, char* argv[]); -bool AppInit2(int argc, char* argv[]); - -#endif diff --git a/core/include/irc.h b/core/include/irc.h deleted file mode 100644 index 18e53597f6..0000000000 --- a/core/include/irc.h +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_IRC_H -#define BITCOIN_IRC_H - -bool RecvLine(SOCKET hSocket, std::string& strLine); -void ThreadIRCSeed(void* parg); - -extern int nGotIRCAddresses; -extern bool fGotExternalIP; - -#endif diff --git a/core/include/key.h b/core/include/key.h deleted file mode 100644 index c973d6eb82..0000000000 --- a/core/include/key.h +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_KEY_H -#define BITCOIN_KEY_H - -#include -#include -#include - -// secp160k1 -// const unsigned int PRIVATE_KEY_SIZE = 192; -// const unsigned int PUBLIC_KEY_SIZE = 41; -// const unsigned int SIGNATURE_SIZE = 48; -// -// secp192k1 -// const unsigned int PRIVATE_KEY_SIZE = 222; -// const unsigned int PUBLIC_KEY_SIZE = 49; -// const unsigned int SIGNATURE_SIZE = 57; -// -// secp224k1 -// const unsigned int PRIVATE_KEY_SIZE = 250; -// const unsigned int PUBLIC_KEY_SIZE = 57; -// const unsigned int SIGNATURE_SIZE = 66; -// -// secp256k1: -// const unsigned int PRIVATE_KEY_SIZE = 279; -// const unsigned int PUBLIC_KEY_SIZE = 65; -// const unsigned int SIGNATURE_SIZE = 72; -// -// see www.keylength.com -// script supports up to 75 for single byte push - - - -class key_error : public std::runtime_error -{ -public: - explicit key_error(const std::string& str) : std::runtime_error(str) {} -}; - - -// secure_allocator is defined in serialize.h -typedef std::vector > CPrivKey; - - - -class CKey -{ -protected: - EC_KEY* pkey; - bool fSet; - -public: - CKey() - { - pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if (pkey == NULL) - throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed"); - fSet = false; - } - - CKey(const CKey& b) - { - pkey = EC_KEY_dup(b.pkey); - if (pkey == NULL) - throw key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed"); - fSet = b.fSet; - } - - CKey& operator=(const CKey& b) - { - if (!EC_KEY_copy(pkey, b.pkey)) - throw key_error("CKey::operator=(const CKey&) : EC_KEY_copy failed"); - fSet = b.fSet; - return (*this); - } - - ~CKey() - { - EC_KEY_free(pkey); - } - - bool IsNull() const - { - return !fSet; - } - - void MakeNewKey() - { - if (!EC_KEY_generate_key(pkey)) - throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed"); - fSet = true; - } - - bool SetPrivKey(const CPrivKey& vchPrivKey) - { - const unsigned char* pbegin = &vchPrivKey[0]; - if (!d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size())) - return false; - fSet = true; - return true; - } - - CPrivKey GetPrivKey() const - { - unsigned int nSize = i2d_ECPrivateKey(pkey, NULL); - if (!nSize) - throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed"); - CPrivKey vchPrivKey(nSize, 0); - unsigned char* pbegin = &vchPrivKey[0]; - if (i2d_ECPrivateKey(pkey, &pbegin) != nSize) - throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size"); - return vchPrivKey; - } - - bool SetPubKey(const std::vector& vchPubKey) - { - const unsigned char* pbegin = &vchPubKey[0]; - if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size())) - return false; - fSet = true; - return true; - } - - std::vector GetPubKey() const - { - unsigned int nSize = i2o_ECPublicKey(pkey, NULL); - if (!nSize) - throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed"); - std::vector vchPubKey(nSize, 0); - unsigned char* pbegin = &vchPubKey[0]; - if (i2o_ECPublicKey(pkey, &pbegin) != nSize) - throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size"); - return vchPubKey; - } - - bool Sign(uint256 hash, std::vector& vchSig) - { - vchSig.clear(); - unsigned char pchSig[10000]; - unsigned int nSize = 0; - if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), pchSig, &nSize, pkey)) - return false; - vchSig.resize(nSize); - memcpy(&vchSig[0], pchSig, nSize); - return true; - } - - bool Verify(uint256 hash, const std::vector& vchSig) - { - // -1 = error, 0 = bad sig, 1 = good - if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) - return false; - return true; - } - - static bool Sign(const CPrivKey& vchPrivKey, uint256 hash, std::vector& vchSig) - { - CKey key; - if (!key.SetPrivKey(vchPrivKey)) - return false; - return key.Sign(hash, vchSig); - } - - static bool Verify(const std::vector& vchPubKey, uint256 hash, const std::vector& vchSig) - { - CKey key; - if (!key.SetPubKey(vchPubKey)) - return false; - return key.Verify(hash, vchSig); - } -}; - -#endif diff --git a/core/include/main.h b/core/include/main.h deleted file mode 100644 index 8d9b39f718..0000000000 --- a/core/include/main.h +++ /dev/null @@ -1,2066 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_MAIN_H -#define BITCOIN_MAIN_H - -#include "bignum.h" -#include "net.h" -#include "key.h" -#include "db.h" -#include "script.h" - -#include - -class COutPoint; -class CInPoint; -class CDiskTxPos; -class CCoinBase; -class CTxIn; -class CTxOut; -class CTransaction; -class CBlock; -class CBlockIndex; -class CWalletTx; -class CKeyItem; - -static const unsigned int MAX_BLOCK_SIZE = 1000000; -static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; -static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; -static const int64 COIN = 100000000; -static const int64 CENT = 1000000; -static const int64 MIN_TX_FEE = CENT; -static const int64 MIN_RELAY_TX_FEE = 50000; -static const int64 MAX_MONEY = 21000000 * COIN; -inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } -static const int COINBASE_MATURITY = 100; -#ifdef USE_UPNP -static const int fHaveUPnP = true; -#else -static const int fHaveUPnP = false; -#endif - - - - - - -extern CCriticalSection cs_main; -extern std::map mapBlockIndex; -extern uint256 hashGenesisBlock; -extern CBigNum bnProofOfWorkLimit; -extern CBlockIndex* pindexGenesisBlock; -extern int nBestHeight; -extern CBigNum bnBestChainWork; -extern CBigNum bnBestInvalidWork; -extern uint256 hashBestChain; -extern CBlockIndex* pindexBest; -extern unsigned int nTransactionsUpdated; -extern std::map mapRequestCount; -extern CCriticalSection cs_mapRequestCount; -extern std::map mapAddressBook; -extern CCriticalSection cs_mapAddressBook; -extern std::vector vchDefaultKey; -extern double dHashesPerSec; -extern int64 nHPSTimerStart; - -// Settings -extern int fGenerateBitcoins; -extern int64 nTransactionFee; -extern CAddress addrIncoming; -extern int fLimitProcessors; -extern int nLimitProcessors; -extern int fMinimizeToTray; -extern int fMinimizeOnClose; -extern int fUseUPnP; - - - - - - - -bool CheckDiskSpace(uint64 nAdditionalBytes=0); -FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); -FILE* AppendBlockFile(unsigned int& nFileRet); -bool AddKey(const CKey& key); -std::vector GenerateNewKey(); -bool AddToWallet(const CWalletTx& wtxIn); -void WalletUpdateSpent(const COutPoint& prevout); -int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); -void ReacceptWalletTransactions(); -bool LoadBlockIndex(bool fAllowNew=true); -void PrintBlockTree(); -bool ProcessMessages(CNode* pfrom); -bool ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vRecv); -bool SendMessages(CNode* pto, bool fSendTrickle); -int64 GetBalance(); -bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); -bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); -bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); -bool BroadcastTransaction(CWalletTx& wtxNew); -std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); -std::string SendMoneyToBitcoinAddress(std::string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); -void GenerateBitcoins(bool fGenerate); -void ThreadBitcoinMiner(void* parg); -CBlock* CreateNewBlock(CReserveKey& reservekey); -void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, int64& nPrevTime); -void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); -bool CheckWork(CBlock* pblock, CReserveKey& reservekey); -void BitcoinMiner(); -bool CheckProofOfWork(uint256 hash, unsigned int nBits); -bool IsInitialBlockDownload(); -std::string GetWarnings(std::string strFor); - - - - - - - - - - - - -class CDiskTxPos -{ -public: - unsigned int nFile; - unsigned int nBlockPos; - unsigned int nTxPos; - - CDiskTxPos() - { - SetNull(); - } - - CDiskTxPos(unsigned int nFileIn, unsigned int nBlockPosIn, unsigned int nTxPosIn) - { - nFile = nFileIn; - nBlockPos = nBlockPosIn; - nTxPos = nTxPosIn; - } - - IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) - void SetNull() { nFile = -1; nBlockPos = 0; nTxPos = 0; } - bool IsNull() const { return (nFile == -1); } - - friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b) - { - return (a.nFile == b.nFile && - a.nBlockPos == b.nBlockPos && - a.nTxPos == b.nTxPos); - } - - friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b) - { - return !(a == b); - } - - std::string ToString() const - { - if (IsNull()) - return strprintf("null"); - else - return strprintf("(nFile=%d, nBlockPos=%d, nTxPos=%d)", nFile, nBlockPos, nTxPos); - } - - void print() const - { - printf("%s", ToString().c_str()); - } -}; - - - - -class CInPoint -{ -public: - CTransaction* ptx; - unsigned int n; - - CInPoint() { SetNull(); } - CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } - void SetNull() { ptx = NULL; n = -1; } - bool IsNull() const { return (ptx == NULL && n == -1); } -}; - - - - -class COutPoint -{ -public: - uint256 hash; - unsigned int n; - - COutPoint() { SetNull(); } - COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; } - IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) - void SetNull() { hash = 0; n = -1; } - bool IsNull() const { return (hash == 0 && n == -1); } - - friend bool operator<(const COutPoint& a, const COutPoint& b) - { - return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); - } - - friend bool operator==(const COutPoint& a, const COutPoint& b) - { - return (a.hash == b.hash && a.n == b.n); - } - - friend bool operator!=(const COutPoint& a, const COutPoint& b) - { - return !(a == b); - } - - std::string ToString() const - { - return strprintf("COutPoint(%s, %d)", hash.ToString().substr(0,10).c_str(), n); - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - - -// -// An input of a transaction. It contains the location of the previous -// transaction's output that it claims and a signature that matches the -// output's public key. -// -class CTxIn -{ -public: - COutPoint prevout; - CScript scriptSig; - unsigned int nSequence; - - CTxIn() - { - nSequence = UINT_MAX; - } - - explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX) - { - prevout = prevoutIn; - scriptSig = scriptSigIn; - nSequence = nSequenceIn; - } - - CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX) - { - prevout = COutPoint(hashPrevTx, nOut); - scriptSig = scriptSigIn; - nSequence = nSequenceIn; - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(prevout); - READWRITE(scriptSig); - READWRITE(nSequence); - ) - - bool IsFinal() const - { - return (nSequence == UINT_MAX); - } - - friend bool operator==(const CTxIn& a, const CTxIn& b) - { - return (a.prevout == b.prevout && - a.scriptSig == b.scriptSig && - a.nSequence == b.nSequence); - } - - friend bool operator!=(const CTxIn& a, const CTxIn& b) - { - return !(a == b); - } - - std::string ToString() const - { - std::string str; - str += strprintf("CTxIn("); - str += prevout.ToString(); - if (prevout.IsNull()) - str += strprintf(", coinbase %s", HexStr(scriptSig).c_str()); - else - str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str()); - if (nSequence != UINT_MAX) - str += strprintf(", nSequence=%u", nSequence); - str += ")"; - return str; - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } - - bool IsMine() const; - int64 GetDebit() const; -}; - - - - -// -// An output of a transaction. It contains the public key that the next input -// must be able to sign with to claim it. -// -class CTxOut -{ -public: - int64 nValue; - CScript scriptPubKey; - - CTxOut() - { - SetNull(); - } - - CTxOut(int64 nValueIn, CScript scriptPubKeyIn) - { - nValue = nValueIn; - scriptPubKey = scriptPubKeyIn; - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(nValue); - READWRITE(scriptPubKey); - ) - - void SetNull() - { - nValue = -1; - scriptPubKey.clear(); - } - - bool IsNull() - { - return (nValue == -1); - } - - uint256 GetHash() const - { - return SerializeHash(*this); - } - - bool IsMine() const - { - return ::IsMine(scriptPubKey); - } - - int64 GetCredit() const - { - if (!MoneyRange(nValue)) - throw std::runtime_error("CTxOut::GetCredit() : value out of range"); - return (IsMine() ? nValue : 0); - } - - bool IsChange() const - { - // On a debit transaction, a txout that's mine but isn't in the address book is change - std::vector vchPubKey; - if (ExtractPubKey(scriptPubKey, true, vchPubKey)) - CRITICAL_BLOCK(cs_mapAddressBook) - if (!mapAddressBook.count(PubKeyToAddress(vchPubKey))) - return true; - return false; - } - - int64 GetChange() const - { - if (!MoneyRange(nValue)) - throw std::runtime_error("CTxOut::GetChange() : value out of range"); - return (IsChange() ? nValue : 0); - } - - friend bool operator==(const CTxOut& a, const CTxOut& b) - { - return (a.nValue == b.nValue && - a.scriptPubKey == b.scriptPubKey); - } - - friend bool operator!=(const CTxOut& a, const CTxOut& b) - { - return !(a == b); - } - - std::string ToString() const - { - if (scriptPubKey.size() < 6) - return "CTxOut(error)"; - return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - - -// -// The basic transaction that is broadcasted on the network and contained in -// blocks. A transaction can contain multiple inputs and outputs. -// -class CTransaction -{ -public: - int nVersion; - std::vector vin; - std::vector vout; - unsigned int nLockTime; - - - CTransaction() - { - SetNull(); - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(vin); - READWRITE(vout); - READWRITE(nLockTime); - ) - - void SetNull() - { - nVersion = 1; - vin.clear(); - vout.clear(); - nLockTime = 0; - } - - bool IsNull() const - { - return (vin.empty() && vout.empty()); - } - - uint256 GetHash() const - { - return SerializeHash(*this); - } - - bool IsFinal(int nBlockHeight=0, int64 nBlockTime=0) const - { - // Time based nLockTime implemented in 0.1.6 - if (nLockTime == 0) - return true; - if (nBlockHeight == 0) - nBlockHeight = nBestHeight; - if (nBlockTime == 0) - nBlockTime = GetAdjustedTime(); - if ((int64)nLockTime < (nLockTime < 500000000 ? (int64)nBlockHeight : nBlockTime)) - return true; - BOOST_FOREACH(const CTxIn& txin, vin) - if (!txin.IsFinal()) - return false; - return true; - } - - bool IsNewerThan(const CTransaction& old) const - { - if (vin.size() != old.vin.size()) - return false; - for (int i = 0; i < vin.size(); i++) - if (vin[i].prevout != old.vin[i].prevout) - return false; - - bool fNewer = false; - unsigned int nLowest = UINT_MAX; - for (int i = 0; i < vin.size(); i++) - { - if (vin[i].nSequence != old.vin[i].nSequence) - { - if (vin[i].nSequence <= nLowest) - { - fNewer = false; - nLowest = vin[i].nSequence; - } - if (old.vin[i].nSequence < nLowest) - { - fNewer = true; - nLowest = old.vin[i].nSequence; - } - } - } - return fNewer; - } - - bool IsCoinBase() const - { - return (vin.size() == 1 && vin[0].prevout.IsNull()); - } - - int GetSigOpCount() const - { - int n = 0; - BOOST_FOREACH(const CTxIn& txin, vin) - n += txin.scriptSig.GetSigOpCount(); - BOOST_FOREACH(const CTxOut& txout, vout) - n += txout.scriptPubKey.GetSigOpCount(); - return n; - } - - bool IsStandard() const - { - BOOST_FOREACH(const CTxIn& txin, vin) - if (!txin.scriptSig.IsPushOnly()) - return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str()); - BOOST_FOREACH(const CTxOut& txout, vout) - if (!::IsStandard(txout.scriptPubKey)) - return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str()); - return true; - } - - bool IsMine() const - { - BOOST_FOREACH(const CTxOut& txout, vout) - if (txout.IsMine()) - return true; - return false; - } - - bool IsFromMe() const - { - return (GetDebit() > 0); - } - - int64 GetDebit() const - { - int64 nDebit = 0; - BOOST_FOREACH(const CTxIn& txin, vin) - { - nDebit += txin.GetDebit(); - if (!MoneyRange(nDebit)) - throw std::runtime_error("CTransaction::GetDebit() : value out of range"); - } - return nDebit; - } - - int64 GetCredit() const - { - int64 nCredit = 0; - BOOST_FOREACH(const CTxOut& txout, vout) - { - nCredit += txout.GetCredit(); - if (!MoneyRange(nCredit)) - throw std::runtime_error("CTransaction::GetCredit() : value out of range"); - } - return nCredit; - } - - int64 GetChange() const - { - if (IsCoinBase()) - return 0; - int64 nChange = 0; - BOOST_FOREACH(const CTxOut& txout, vout) - { - nChange += txout.GetChange(); - if (!MoneyRange(nChange)) - throw std::runtime_error("CTransaction::GetChange() : value out of range"); - } - return nChange; - } - - int64 GetValueOut() const - { - int64 nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, vout) - { - nValueOut += txout.nValue; - if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) - throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); - } - return nValueOut; - } - - static bool AllowFree(double dPriority) - { - // Large (in bytes) low-priority (new, small-coin) transactions - // need a fee. - return dPriority > COIN * 144 / 250; - } - - int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, bool fForRelay=false) const - { - // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE - int64 nBaseFee = fForRelay ? MIN_RELAY_TX_FEE : MIN_TX_FEE; - - unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK); - unsigned int nNewBlockSize = nBlockSize + nBytes; - int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; - - if (fAllowFree) - { - if (nBlockSize == 1) - { - // Transactions under 10K are free - // (about 4500bc if made of 50bc inputs) - if (nBytes < 10000) - nMinFee = 0; - } - else - { - // Free transaction area - if (nNewBlockSize < 27000) - nMinFee = 0; - } - } - - // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01 - if (nMinFee < nBaseFee) - BOOST_FOREACH(const CTxOut& txout, vout) - if (txout.nValue < CENT) - nMinFee = nBaseFee; - - // Raise the price as the block approaches full - if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) - { - if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) - return MAX_MONEY; - nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); - } - - if (!MoneyRange(nMinFee)) - nMinFee = MAX_MONEY; - return nMinFee; - } - - - bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL) - { - CAutoFile filein = OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb"); - if (!filein) - return error("CTransaction::ReadFromDisk() : OpenBlockFile failed"); - - // Read transaction - if (fseek(filein, pos.nTxPos, SEEK_SET) != 0) - return error("CTransaction::ReadFromDisk() : fseek failed"); - filein >> *this; - - // Return file pointer - if (pfileRet) - { - if (fseek(filein, pos.nTxPos, SEEK_SET) != 0) - return error("CTransaction::ReadFromDisk() : second fseek failed"); - *pfileRet = filein.release(); - } - return true; - } - - friend bool operator==(const CTransaction& a, const CTransaction& b) - { - return (a.nVersion == b.nVersion && - a.vin == b.vin && - a.vout == b.vout && - a.nLockTime == b.nLockTime); - } - - friend bool operator!=(const CTransaction& a, const CTransaction& b) - { - return !(a == b); - } - - - std::string ToString() const - { - std::string str; - str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n", - GetHash().ToString().substr(0,10).c_str(), - nVersion, - vin.size(), - vout.size(), - nLockTime); - for (int i = 0; i < vin.size(); i++) - str += " " + vin[i].ToString() + "\n"; - for (int i = 0; i < vout.size(); i++) - str += " " + vout[i].ToString() + "\n"; - return str; - } - - void print() const - { - printf("%s", ToString().c_str()); - } - - - bool ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet); - bool ReadFromDisk(CTxDB& txdb, COutPoint prevout); - bool ReadFromDisk(COutPoint prevout); - bool DisconnectInputs(CTxDB& txdb); - bool ConnectInputs(CTxDB& txdb, std::map& mapTestPool, CDiskTxPos posThisTx, - CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0); - bool ClientConnectInputs(); - bool CheckTransaction() const; - bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); - bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL) - { - CTxDB txdb("r"); - return AcceptToMemoryPool(txdb, fCheckInputs, pfMissingInputs); - } -protected: - bool AddToMemoryPoolUnchecked(); -public: - bool RemoveFromMemoryPool(); -}; - - - - - -// -// A transaction with a merkle branch linking it to the block chain -// -class CMerkleTx : public CTransaction -{ -public: - uint256 hashBlock; - std::vector vMerkleBranch; - int nIndex; - - // memory only - mutable char fMerkleVerified; - - - CMerkleTx() - { - Init(); - } - - CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) - { - Init(); - } - - void Init() - { - hashBlock = 0; - nIndex = -1; - fMerkleVerified = false; - } - - - IMPLEMENT_SERIALIZE - ( - nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action); - nVersion = this->nVersion; - READWRITE(hashBlock); - READWRITE(vMerkleBranch); - READWRITE(nIndex); - ) - - - int SetMerkleBranch(const CBlock* pblock=NULL); - int GetDepthInMainChain(int& nHeightRet) const; - int GetDepthInMainChain() const { int nHeight; return GetDepthInMainChain(nHeight); } - bool IsInMainChain() const { return GetDepthInMainChain() > 0; } - int GetBlocksToMaturity() const; - bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true); - bool AcceptToMemoryPool() { CTxDB txdb("r"); return AcceptToMemoryPool(txdb); } -}; - - - - -// -// A transaction with a bunch of additional info that only the owner cares -// about. It includes any unrecorded transactions needed to link it back -// to the block chain. -// -class CWalletTx : public CMerkleTx -{ -public: - std::vector vtxPrev; - std::map mapValue; - std::vector > vOrderForm; - unsigned int fTimeReceivedIsTxTime; - unsigned int nTimeReceived; // time received by this node - char fFromMe; - std::string strFromAccount; - std::vector vfSpent; - - // memory only - mutable char fDebitCached; - mutable char fCreditCached; - mutable char fAvailableCreditCached; - mutable char fChangeCached; - mutable int64 nDebitCached; - mutable int64 nCreditCached; - mutable int64 nAvailableCreditCached; - mutable int64 nChangeCached; - - // memory only UI hints - mutable unsigned int nTimeDisplayed; - mutable int nLinesDisplayed; - mutable char fConfirmedDisplayed; - - - CWalletTx() - { - Init(); - } - - CWalletTx(const CMerkleTx& txIn) : CMerkleTx(txIn) - { - Init(); - } - - CWalletTx(const CTransaction& txIn) : CMerkleTx(txIn) - { - Init(); - } - - void Init() - { - vtxPrev.clear(); - mapValue.clear(); - vOrderForm.clear(); - fTimeReceivedIsTxTime = false; - nTimeReceived = 0; - fFromMe = false; - strFromAccount.clear(); - vfSpent.clear(); - fDebitCached = false; - fCreditCached = false; - fAvailableCreditCached = false; - fChangeCached = false; - nDebitCached = 0; - nCreditCached = 0; - nAvailableCreditCached = 0; - nChangeCached = 0; - nTimeDisplayed = 0; - nLinesDisplayed = 0; - fConfirmedDisplayed = false; - } - - IMPLEMENT_SERIALIZE - ( - CWalletTx* pthis = const_cast(this); - if (fRead) - pthis->Init(); - char fSpent = false; - - if (!fRead) - { - pthis->mapValue["fromaccount"] = pthis->strFromAccount; - - std::string str; - BOOST_FOREACH(char f, vfSpent) - { - str += (f ? '1' : '0'); - if (f) - fSpent = true; - } - pthis->mapValue["spent"] = str; - } - - nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action); - READWRITE(vtxPrev); - READWRITE(mapValue); - READWRITE(vOrderForm); - READWRITE(fTimeReceivedIsTxTime); - READWRITE(nTimeReceived); - READWRITE(fFromMe); - READWRITE(fSpent); - - if (fRead) - { - pthis->strFromAccount = pthis->mapValue["fromaccount"]; - - if (mapValue.count("spent")) - BOOST_FOREACH(char c, pthis->mapValue["spent"]) - pthis->vfSpent.push_back(c != '0'); - else - pthis->vfSpent.assign(vout.size(), fSpent); - } - - pthis->mapValue.erase("fromaccount"); - pthis->mapValue.erase("version"); - pthis->mapValue.erase("spent"); - ) - - // marks certain txout's as spent - // returns true if any update took place - bool UpdateSpent(const std::vector& vfNewSpent) - { - bool fReturn = false; - for (int i=0; i < vfNewSpent.size(); i++) - { - if (i == vfSpent.size()) - break; - - if (vfNewSpent[i] && !vfSpent[i]) - { - vfSpent[i] = true; - fReturn = true; - fAvailableCreditCached = false; - } - } - return fReturn; - } - - void MarkDirty() - { - fCreditCached = false; - fAvailableCreditCached = false; - fDebitCached = false; - fChangeCached = false; - } - - void MarkSpent(unsigned int nOut) - { - if (nOut >= vout.size()) - throw std::runtime_error("CWalletTx::MarkSpent() : nOut out of range"); - vfSpent.resize(vout.size()); - if (!vfSpent[nOut]) - { - vfSpent[nOut] = true; - fAvailableCreditCached = false; - } - } - - bool IsSpent(unsigned int nOut) const - { - if (nOut >= vout.size()) - throw std::runtime_error("CWalletTx::IsSpent() : nOut out of range"); - if (nOut >= vfSpent.size()) - return false; - return (!!vfSpent[nOut]); - } - - int64 GetDebit() const - { - if (vin.empty()) - return 0; - if (fDebitCached) - return nDebitCached; - nDebitCached = CTransaction::GetDebit(); - fDebitCached = true; - return nDebitCached; - } - - int64 GetCredit(bool fUseCache=true) const - { - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) - return 0; - - // GetBalance can assume transactions in mapWallet won't change - if (fUseCache && fCreditCached) - return nCreditCached; - nCreditCached = CTransaction::GetCredit(); - fCreditCached = true; - return nCreditCached; - } - - int64 GetAvailableCredit(bool fUseCache=true) const - { - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) - return 0; - - if (fUseCache && fAvailableCreditCached) - return nAvailableCreditCached; - - int64 nCredit = 0; - for (int i = 0; i < vout.size(); i++) - { - if (!IsSpent(i)) - { - const CTxOut &txout = vout[i]; - nCredit += txout.GetCredit(); - if (!MoneyRange(nCredit)) - throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); - } - } - - nAvailableCreditCached = nCredit; - fAvailableCreditCached = true; - return nCredit; - } - - - int64 GetChange() const - { - if (fChangeCached) - return nChangeCached; - nChangeCached = CTransaction::GetChange(); - fChangeCached = true; - return nChangeCached; - } - - void GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, std::list >& listReceived, - std::list >& listSent, int64& nFee, std::string& strSentAccount) const; - - void GetAccountAmounts(const std::string& strAccount, int64& nGenerated, int64& nReceived, - int64& nSent, int64& nFee) const; - - bool IsFromMe() const - { - return (GetDebit() > 0); - } - - bool IsConfirmed() const - { - // Quick answer in most cases - if (!IsFinal()) - return false; - if (GetDepthInMainChain() >= 1) - return true; - if (!IsFromMe()) // using wtx's cached debit - return false; - - // If no confirmations but it's from us, we can still - // consider it confirmed if all dependencies are confirmed - std::map mapPrev; - std::vector vWorkQueue; - vWorkQueue.reserve(vtxPrev.size()+1); - vWorkQueue.push_back(this); - for (int i = 0; i < vWorkQueue.size(); i++) - { - const CMerkleTx* ptx = vWorkQueue[i]; - - if (!ptx->IsFinal()) - return false; - if (ptx->GetDepthInMainChain() >= 1) - continue; - if (!ptx->IsFromMe()) - return false; - - if (mapPrev.empty()) - BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) - mapPrev[tx.GetHash()] = &tx; - - BOOST_FOREACH(const CTxIn& txin, ptx->vin) - { - if (!mapPrev.count(txin.prevout.hash)) - return false; - vWorkQueue.push_back(mapPrev[txin.prevout.hash]); - } - } - return true; - } - - bool WriteToDisk() - { - return CWalletDB().WriteTx(GetHash(), *this); - } - - - int64 GetTxTime() const; - int GetRequestCount() const; - - void AddSupportingTransactions(CTxDB& txdb); - - bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true); - bool AcceptWalletTransaction() { CTxDB txdb("r"); return AcceptWalletTransaction(txdb); } - - void RelayWalletTransaction(CTxDB& txdb); - void RelayWalletTransaction() { CTxDB txdb("r"); RelayWalletTransaction(txdb); } -}; - - - - -// -// A txdb record that contains the disk location of a transaction and the -// locations of transactions that spend its outputs. vSpent is really only -// used as a flag, but having the location is very helpful for debugging. -// -class CTxIndex -{ -public: - CDiskTxPos pos; - std::vector vSpent; - - CTxIndex() - { - SetNull(); - } - - CTxIndex(const CDiskTxPos& posIn, unsigned int nOutputs) - { - pos = posIn; - vSpent.resize(nOutputs); - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(pos); - READWRITE(vSpent); - ) - - void SetNull() - { - pos.SetNull(); - vSpent.clear(); - } - - bool IsNull() - { - return pos.IsNull(); - } - - friend bool operator==(const CTxIndex& a, const CTxIndex& b) - { - return (a.pos == b.pos && - a.vSpent == b.vSpent); - } - - friend bool operator!=(const CTxIndex& a, const CTxIndex& b) - { - return !(a == b); - } - int GetDepthInMainChain() const; -}; - - - - - -// -// Nodes collect new transactions into a block, hash them into a hash tree, -// and scan through nonce values to make the block's hash satisfy proof-of-work -// requirements. When they solve the proof-of-work, they broadcast the block -// to everyone and the block is added to the block chain. The first transaction -// in the block is a special one that creates a new coin owned by the creator -// of the block. -// -// Blocks are appended to blk0001.dat files on disk. Their location on disk -// is indexed by CBlockIndex objects in memory. -// -class CBlock -{ -public: - // header - int nVersion; - uint256 hashPrevBlock; - uint256 hashMerkleRoot; - unsigned int nTime; - unsigned int nBits; - unsigned int nNonce; - - // network and disk - std::vector vtx; - - // memory only - mutable std::vector vMerkleTree; - - - CBlock() - { - SetNull(); - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(hashPrevBlock); - READWRITE(hashMerkleRoot); - READWRITE(nTime); - READWRITE(nBits); - READWRITE(nNonce); - - // ConnectBlock depends on vtx being last so it can calculate offset - if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY))) - READWRITE(vtx); - else if (fRead) - const_cast(this)->vtx.clear(); - ) - - void SetNull() - { - nVersion = 1; - hashPrevBlock = 0; - hashMerkleRoot = 0; - nTime = 0; - nBits = 0; - nNonce = 0; - vtx.clear(); - vMerkleTree.clear(); - } - - bool IsNull() const - { - return (nBits == 0); - } - - uint256 GetHash() const - { - return Hash(BEGIN(nVersion), END(nNonce)); - } - - int64 GetBlockTime() const - { - return (int64)nTime; - } - - int GetSigOpCount() const - { - int n = 0; - BOOST_FOREACH(const CTransaction& tx, vtx) - n += tx.GetSigOpCount(); - return n; - } - - - uint256 BuildMerkleTree() const - { - vMerkleTree.clear(); - BOOST_FOREACH(const CTransaction& tx, vtx) - vMerkleTree.push_back(tx.GetHash()); - int j = 0; - for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) - { - for (int i = 0; i < nSize; i += 2) - { - int i2 = std::min(i+1, nSize-1); - vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), - BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); - } - j += nSize; - } - return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); - } - - std::vector GetMerkleBranch(int nIndex) const - { - if (vMerkleTree.empty()) - BuildMerkleTree(); - std::vector vMerkleBranch; - int j = 0; - for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) - { - int i = std::min(nIndex^1, nSize-1); - vMerkleBranch.push_back(vMerkleTree[j+i]); - nIndex >>= 1; - j += nSize; - } - return vMerkleBranch; - } - - static uint256 CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) - { - if (nIndex == -1) - return 0; - BOOST_FOREACH(const uint256& otherside, vMerkleBranch) - { - if (nIndex & 1) - hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); - else - hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); - nIndex >>= 1; - } - return hash; - } - - - bool WriteToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet) - { - // Open history file to append - CAutoFile fileout = AppendBlockFile(nFileRet); - if (!fileout) - return error("CBlock::WriteToDisk() : AppendBlockFile failed"); - - // Write index header - unsigned int nSize = fileout.GetSerializeSize(*this); - fileout << FLATDATA(pchMessageStart) << nSize; - - // Write block - nBlockPosRet = ftell(fileout); - if (nBlockPosRet == -1) - return error("CBlock::WriteToDisk() : ftell failed"); - fileout << *this; - - // Flush stdio buffers and commit to disk before returning - fflush(fileout); - if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0) - { -#ifdef __WXMSW__ - _commit(_fileno(fileout)); -#else - fsync(fileno(fileout)); -#endif - } - - return true; - } - - bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true) - { - SetNull(); - - // Open history file to read - CAutoFile filein = OpenBlockFile(nFile, nBlockPos, "rb"); - if (!filein) - return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); - if (!fReadTransactions) - filein.nType |= SER_BLOCKHEADERONLY; - - // Read block - filein >> *this; - - // Check the header - if (!CheckProofOfWork(GetHash(), nBits)) - return error("CBlock::ReadFromDisk() : errors in block header"); - - return true; - } - - - - void print() const - { - printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d)\n", - GetHash().ToString().substr(0,20).c_str(), - nVersion, - hashPrevBlock.ToString().substr(0,20).c_str(), - hashMerkleRoot.ToString().substr(0,10).c_str(), - nTime, nBits, nNonce, - vtx.size()); - for (int i = 0; i < vtx.size(); i++) - { - printf(" "); - vtx[i].print(); - } - printf(" vMerkleTree: "); - for (int i = 0; i < vMerkleTree.size(); i++) - printf("%s ", vMerkleTree[i].ToString().substr(0,10).c_str()); - printf("\n"); - } - - - bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex); - bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex); - bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true); - bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew); - bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); - bool CheckBlock() const; - bool AcceptBlock(); -}; - - - - - - -// -// The block chain is a tree shaped structure starting with the -// genesis block at the root, with each block potentially having multiple -// candidates to be the next block. pprev and pnext link a path through the -// main/longest chain. A blockindex may have multiple pprev pointing back -// to it, but pnext will only point forward to the longest branch, or will -// be null if the block is not part of the longest chain. -// -class CBlockIndex -{ -public: - const uint256* phashBlock; - CBlockIndex* pprev; - CBlockIndex* pnext; - unsigned int nFile; - unsigned int nBlockPos; - int nHeight; - CBigNum bnChainWork; - - // block header - int nVersion; - uint256 hashMerkleRoot; - unsigned int nTime; - unsigned int nBits; - unsigned int nNonce; - - - CBlockIndex() - { - phashBlock = NULL; - pprev = NULL; - pnext = NULL; - nFile = 0; - nBlockPos = 0; - nHeight = 0; - bnChainWork = 0; - - nVersion = 0; - hashMerkleRoot = 0; - nTime = 0; - nBits = 0; - nNonce = 0; - } - - CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block) - { - phashBlock = NULL; - pprev = NULL; - pnext = NULL; - nFile = nFileIn; - nBlockPos = nBlockPosIn; - nHeight = 0; - bnChainWork = 0; - - nVersion = block.nVersion; - hashMerkleRoot = block.hashMerkleRoot; - nTime = block.nTime; - nBits = block.nBits; - nNonce = block.nNonce; - } - - CBlock GetBlockHeader() const - { - CBlock block; - block.nVersion = nVersion; - if (pprev) - block.hashPrevBlock = pprev->GetBlockHash(); - block.hashMerkleRoot = hashMerkleRoot; - block.nTime = nTime; - block.nBits = nBits; - block.nNonce = nNonce; - return block; - } - - uint256 GetBlockHash() const - { - return *phashBlock; - } - - int64 GetBlockTime() const - { - return (int64)nTime; - } - - CBigNum GetBlockWork() const - { - CBigNum bnTarget; - bnTarget.SetCompact(nBits); - if (bnTarget <= 0) - return 0; - return (CBigNum(1)<<256) / (bnTarget+1); - } - - bool IsInMainChain() const - { - return (pnext || this == pindexBest); - } - - bool CheckIndex() const - { - return CheckProofOfWork(GetBlockHash(), nBits); - } - - bool EraseBlockFromDisk() - { - // Open history file - CAutoFile fileout = OpenBlockFile(nFile, nBlockPos, "rb+"); - if (!fileout) - return false; - - // Overwrite with empty null block - CBlock block; - block.SetNull(); - fileout << block; - - return true; - } - - enum { nMedianTimeSpan=11 }; - - int64 GetMedianTimePast() const - { - int64 pmedian[nMedianTimeSpan]; - int64* pbegin = &pmedian[nMedianTimeSpan]; - int64* pend = &pmedian[nMedianTimeSpan]; - - const CBlockIndex* pindex = this; - for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev) - *(--pbegin) = pindex->GetBlockTime(); - - std::sort(pbegin, pend); - return pbegin[(pend - pbegin)/2]; - } - - int64 GetMedianTime() const - { - const CBlockIndex* pindex = this; - for (int i = 0; i < nMedianTimeSpan/2; i++) - { - if (!pindex->pnext) - return GetBlockTime(); - pindex = pindex->pnext; - } - return pindex->GetMedianTimePast(); - } - - - - std::string ToString() const - { - return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, merkle=%s, hashBlock=%s)", - pprev, pnext, nFile, nBlockPos, nHeight, - hashMerkleRoot.ToString().substr(0,10).c_str(), - GetBlockHash().ToString().substr(0,20).c_str()); - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - -// -// Used to marshal pointers into hashes for db storage. -// -class CDiskBlockIndex : public CBlockIndex -{ -public: - uint256 hashPrev; - uint256 hashNext; - - CDiskBlockIndex() - { - hashPrev = 0; - hashNext = 0; - } - - explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) - { - hashPrev = (pprev ? pprev->GetBlockHash() : 0); - hashNext = (pnext ? pnext->GetBlockHash() : 0); - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - - READWRITE(hashNext); - READWRITE(nFile); - READWRITE(nBlockPos); - READWRITE(nHeight); - - // block header - READWRITE(this->nVersion); - READWRITE(hashPrev); - READWRITE(hashMerkleRoot); - READWRITE(nTime); - READWRITE(nBits); - READWRITE(nNonce); - ) - - uint256 GetBlockHash() const - { - CBlock block; - block.nVersion = nVersion; - block.hashPrevBlock = hashPrev; - block.hashMerkleRoot = hashMerkleRoot; - block.nTime = nTime; - block.nBits = nBits; - block.nNonce = nNonce; - return block.GetHash(); - } - - - std::string ToString() const - { - std::string str = "CDiskBlockIndex("; - str += CBlockIndex::ToString(); - str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)", - GetBlockHash().ToString().c_str(), - hashPrev.ToString().substr(0,20).c_str(), - hashNext.ToString().substr(0,20).c_str()); - return str; - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - - - - - - -// -// Describes a place in the block chain to another node such that if the -// other node doesn't have the same branch, it can find a recent common trunk. -// The further back it is, the further before the fork it may be. -// -class CBlockLocator -{ -protected: - std::vector vHave; -public: - - CBlockLocator() - { - } - - explicit CBlockLocator(const CBlockIndex* pindex) - { - Set(pindex); - } - - explicit CBlockLocator(uint256 hashBlock) - { - std::map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end()) - Set((*mi).second); - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vHave); - ) - - void SetNull() - { - vHave.clear(); - } - - bool IsNull() - { - return vHave.empty(); - } - - void Set(const CBlockIndex* pindex) - { - vHave.clear(); - int nStep = 1; - while (pindex) - { - vHave.push_back(pindex->GetBlockHash()); - - // Exponentially larger steps back - for (int i = 0; pindex && i < nStep; i++) - pindex = pindex->pprev; - if (vHave.size() > 10) - nStep *= 2; - } - vHave.push_back(hashGenesisBlock); - } - - int GetDistanceBack() - { - // Retrace how far back it was in the sender's branch - int nDistance = 0; - int nStep = 1; - BOOST_FOREACH(const uint256& hash, vHave) - { - std::map::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - return nDistance; - } - nDistance += nStep; - if (nDistance > 10) - nStep *= 2; - } - return nDistance; - } - - CBlockIndex* GetBlockIndex() - { - // Find the first block the caller has in the main chain - BOOST_FOREACH(const uint256& hash, vHave) - { - std::map::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - return pindex; - } - } - return pindexGenesisBlock; - } - - uint256 GetBlockHash() - { - // Find the first block the caller has in the main chain - BOOST_FOREACH(const uint256& hash, vHave) - { - std::map::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - return hash; - } - } - return hashGenesisBlock; - } - - int GetHeight() - { - CBlockIndex* pindex = GetBlockIndex(); - if (!pindex) - return 0; - return pindex->nHeight; - } -}; - - - - - - -// -// Private key that includes an expiration date in case it never gets used. -// -class CWalletKey -{ -public: - CPrivKey vchPrivKey; - int64 nTimeCreated; - int64 nTimeExpires; - std::string strComment; - //// todo: add something to note what created it (user, getnewaddress, change) - //// maybe should have a map property map - - CWalletKey(int64 nExpires=0) - { - nTimeCreated = (nExpires ? GetTime() : 0); - nTimeExpires = nExpires; - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vchPrivKey); - READWRITE(nTimeCreated); - READWRITE(nTimeExpires); - READWRITE(strComment); - ) -}; - - - - - - -// -// Account information. -// Stored in wallet with key "acc"+string account name -// -class CAccount -{ -public: - std::vector vchPubKey; - - CAccount() - { - SetNull(); - } - - void SetNull() - { - vchPubKey.clear(); - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vchPubKey); - ) -}; - - - -// -// Internal transfers. -// Database key is acentry -// -class CAccountingEntry -{ -public: - std::string strAccount; - int64 nCreditDebit; - int64 nTime; - std::string strOtherAccount; - std::string strComment; - - CAccountingEntry() - { - SetNull(); - } - - void SetNull() - { - nCreditDebit = 0; - nTime = 0; - strAccount.clear(); - strOtherAccount.clear(); - strComment.clear(); - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - // Note: strAccount is serialized as part of the key, not here. - READWRITE(nCreditDebit); - READWRITE(nTime); - READWRITE(strOtherAccount); - READWRITE(strComment); - ) -}; - - - - - - - - - -// -// Alerts are for notifying old versions if they become too obsolete and -// need to upgrade. The message is displayed in the status bar. -// Alert messages are broadcast as a vector of signed data. Unserializing may -// not read the entire buffer if the alert is for a newer version, but older -// versions can still relay the original data. -// -class CUnsignedAlert -{ -public: - int nVersion; - int64 nRelayUntil; // when newer nodes stop relaying to newer nodes - int64 nExpiration; - int nID; - int nCancel; - std::set setCancel; - int nMinVer; // lowest version inclusive - int nMaxVer; // highest version inclusive - std::set setSubVer; // empty matches all - int nPriority; - - // Actions - std::string strComment; - std::string strStatusBar; - std::string strReserved; - - IMPLEMENT_SERIALIZE - ( - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(nRelayUntil); - READWRITE(nExpiration); - READWRITE(nID); - READWRITE(nCancel); - READWRITE(setCancel); - READWRITE(nMinVer); - READWRITE(nMaxVer); - READWRITE(setSubVer); - READWRITE(nPriority); - - READWRITE(strComment); - READWRITE(strStatusBar); - READWRITE(strReserved); - ) - - void SetNull() - { - nVersion = 1; - nRelayUntil = 0; - nExpiration = 0; - nID = 0; - nCancel = 0; - setCancel.clear(); - nMinVer = 0; - nMaxVer = 0; - setSubVer.clear(); - nPriority = 0; - - strComment.clear(); - strStatusBar.clear(); - strReserved.clear(); - } - - std::string ToString() const - { - std::string strSetCancel; - BOOST_FOREACH(int n, setCancel) - strSetCancel += strprintf("%d ", n); - std::string strSetSubVer; - BOOST_FOREACH(std::string str, setSubVer) - strSetSubVer += "\"" + str + "\" "; - return strprintf( - "CAlert(\n" - " nVersion = %d\n" - " nRelayUntil = %"PRI64d"\n" - " nExpiration = %"PRI64d"\n" - " nID = %d\n" - " nCancel = %d\n" - " setCancel = %s\n" - " nMinVer = %d\n" - " nMaxVer = %d\n" - " setSubVer = %s\n" - " nPriority = %d\n" - " strComment = \"%s\"\n" - " strStatusBar = \"%s\"\n" - ")\n", - nVersion, - nRelayUntil, - nExpiration, - nID, - nCancel, - strSetCancel.c_str(), - nMinVer, - nMaxVer, - strSetSubVer.c_str(), - nPriority, - strComment.c_str(), - strStatusBar.c_str()); - } - - void print() const - { - printf("%s", ToString().c_str()); - } -}; - -class CAlert : public CUnsignedAlert -{ -public: - std::vector vchMsg; - std::vector vchSig; - - CAlert() - { - SetNull(); - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(vchMsg); - READWRITE(vchSig); - ) - - void SetNull() - { - CUnsignedAlert::SetNull(); - vchMsg.clear(); - vchSig.clear(); - } - - bool IsNull() const - { - return (nExpiration == 0); - } - - uint256 GetHash() const - { - return SerializeHash(*this); - } - - bool IsInEffect() const - { - return (GetAdjustedTime() < nExpiration); - } - - bool Cancels(const CAlert& alert) const - { - if (!IsInEffect()) - return false; // this was a no-op before 31403 - return (alert.nID <= nCancel || setCancel.count(alert.nID)); - } - - bool AppliesTo(int nVersion, std::string strSubVerIn) const - { - return (IsInEffect() && - nMinVer <= nVersion && nVersion <= nMaxVer && - (setSubVer.empty() || setSubVer.count(strSubVerIn))); - } - - bool AppliesToMe() const - { - return AppliesTo(VERSION, ::pszSubVer); - } - - bool RelayTo(CNode* pnode) const - { - if (!IsInEffect()) - return false; - // returns true if wasn't already contained in the set - if (pnode->setKnown.insert(GetHash()).second) - { - if (AppliesTo(pnode->nVersion, pnode->strSubVer) || - AppliesToMe() || - GetAdjustedTime() < nRelayUntil) - { - pnode->PushMessage("alert", *this); - return true; - } - } - return false; - } - - bool CheckSignature() - { - CKey key; - if (!key.SetPubKey(ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284"))) - return error("CAlert::CheckSignature() : SetPubKey failed"); - if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) - return error("CAlert::CheckSignature() : verify signature failed"); - - // Now unserialize the data - CDataStream sMsg(vchMsg); - sMsg >> *(CUnsignedAlert*)this; - return true; - } - - bool ProcessAlert(); -}; - - - - - - - - - - -extern std::map mapTransactions; -extern std::map mapWallet; -extern std::vector vWalletUpdated; -extern CCriticalSection cs_mapWallet; -extern std::map, CPrivKey> mapKeys; -extern std::map > mapPubKeys; -extern CCriticalSection cs_mapKeys; -extern CKey keyUser; - -#endif diff --git a/core/include/net.h b/core/include/net.h deleted file mode 100644 index d1ded87232..0000000000 --- a/core/include/net.h +++ /dev/null @@ -1,1065 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_NET_H -#define BITCOIN_NET_H - -#include -#include -#include - -#ifndef __WXMSW__ -#include -#endif - -class CMessageHeader; -class CAddress; -class CInv; -class CRequestTracker; -class CNode; -class CBlockIndex; -extern int nBestHeight; - - - -inline unsigned short GetDefaultPort() { return fTestNet ? 18333 : 8333; } -static const unsigned int PUBLISH_HOPS = 5; -enum -{ - NODE_NETWORK = (1 << 0), -}; - - - - -bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet); -bool Lookup(const char *pszName, std::vector& vaddr, int nServices, int nMaxSolutions, bool fAllowLookup = false, int portDefault = 0, bool fAllowPort = false); -bool Lookup(const char *pszName, CAddress& addr, int nServices, bool fAllowLookup = false, int portDefault = 0, bool fAllowPort = false); -bool GetMyExternalIP(unsigned int& ipRet); -bool AddAddress(CAddress addr, int64 nTimePenalty=0); -void AddressCurrentlyConnected(const CAddress& addr); -CNode* FindNode(unsigned int ip); -CNode* ConnectNode(CAddress addrConnect, int64 nTimeout=0); -void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); -bool AnySubscribed(unsigned int nChannel); -void MapPort(bool fMapPort); -void DNSAddressSeed(); -bool BindListenPort(std::string& strError=REF(std::string())); -void StartNode(void* parg); -bool StopNode(); - - - - - - - - -// -// Message header -// (4) message start -// (12) command -// (4) size -// (4) checksum - -extern char pchMessageStart[4]; - -class CMessageHeader -{ -public: - enum { COMMAND_SIZE=12 }; - char pchMessageStart[sizeof(::pchMessageStart)]; - char pchCommand[COMMAND_SIZE]; - unsigned int nMessageSize; - unsigned int nChecksum; - - CMessageHeader() - { - memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); - memset(pchCommand, 0, sizeof(pchCommand)); - pchCommand[1] = 1; - nMessageSize = -1; - nChecksum = 0; - } - - CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn) - { - memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); - strncpy(pchCommand, pszCommand, COMMAND_SIZE); - nMessageSize = nMessageSizeIn; - nChecksum = 0; - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(FLATDATA(pchMessageStart)); - READWRITE(FLATDATA(pchCommand)); - READWRITE(nMessageSize); - if (nVersion >= 209) - READWRITE(nChecksum); - ) - - std::string GetCommand() - { - if (pchCommand[COMMAND_SIZE-1] == 0) - return std::string(pchCommand, pchCommand + strlen(pchCommand)); - else - return std::string(pchCommand, pchCommand + COMMAND_SIZE); - } - - bool IsValid() - { - // Check start string - if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0) - return false; - - // Check the command string for errors - for (char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++) - { - if (*p1 == 0) - { - // Must be all zeros after the first zero - for (; p1 < pchCommand + COMMAND_SIZE; p1++) - if (*p1 != 0) - return false; - } - else if (*p1 < ' ' || *p1 > 0x7E) - return false; - } - - // Message size - if (nMessageSize > MAX_SIZE) - { - printf("CMessageHeader::IsValid() : (%s, %u bytes) nMessageSize > MAX_SIZE\n", GetCommand().c_str(), nMessageSize); - return false; - } - - return true; - } -}; - - - - - - -static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; - -class CAddress -{ -public: - uint64 nServices; - unsigned char pchReserved[12]; - unsigned int ip; - unsigned short port; - - // disk and network only - unsigned int nTime; - - // memory only - unsigned int nLastTry; - - CAddress() - { - Init(); - } - - CAddress(unsigned int ipIn, unsigned short portIn=0, uint64 nServicesIn=NODE_NETWORK) - { - Init(); - ip = ipIn; - port = htons(portIn == 0 ? GetDefaultPort() : portIn); - nServices = nServicesIn; - } - - explicit CAddress(const struct sockaddr_in& sockaddr, uint64 nServicesIn=NODE_NETWORK) - { - Init(); - ip = sockaddr.sin_addr.s_addr; - port = sockaddr.sin_port; - nServices = nServicesIn; - } - - explicit CAddress(const char* pszIn, int portIn, bool fNameLookup = false, uint64 nServicesIn=NODE_NETWORK) - { - Init(); - Lookup(pszIn, *this, nServicesIn, fNameLookup, portIn); - } - - explicit CAddress(const char* pszIn, bool fNameLookup = false, uint64 nServicesIn=NODE_NETWORK) - { - Init(); - Lookup(pszIn, *this, nServicesIn, fNameLookup, 0, true); - } - - explicit CAddress(std::string strIn, int portIn, bool fNameLookup = false, uint64 nServicesIn=NODE_NETWORK) - { - Init(); - Lookup(strIn.c_str(), *this, nServicesIn, fNameLookup, portIn); - } - - explicit CAddress(std::string strIn, bool fNameLookup = false, uint64 nServicesIn=NODE_NETWORK) - { - Init(); - Lookup(strIn.c_str(), *this, nServicesIn, fNameLookup, 0, true); - } - - void Init() - { - nServices = NODE_NETWORK; - memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); - ip = INADDR_NONE; - port = htons(GetDefaultPort()); - nTime = 100000000; - nLastTry = 0; - } - - IMPLEMENT_SERIALIZE - ( - if (fRead) - const_cast(this)->Init(); - if (nType & SER_DISK) - READWRITE(nVersion); - if ((nType & SER_DISK) || (nVersion >= 31402 && !(nType & SER_GETHASH))) - READWRITE(nTime); - READWRITE(nServices); - READWRITE(FLATDATA(pchReserved)); // for IPv6 - READWRITE(ip); - READWRITE(port); - ) - - friend inline bool operator==(const CAddress& a, const CAddress& b) - { - return (memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)) == 0 && - a.ip == b.ip && - a.port == b.port); - } - - friend inline bool operator!=(const CAddress& a, const CAddress& b) - { - return (!(a == b)); - } - - friend inline bool operator<(const CAddress& a, const CAddress& b) - { - int ret = memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)); - if (ret < 0) - return true; - else if (ret == 0) - { - if (ntohl(a.ip) < ntohl(b.ip)) - return true; - else if (a.ip == b.ip) - return ntohs(a.port) < ntohs(b.port); - } - return false; - } - - std::vector GetKey() const - { - CDataStream ss; - ss.reserve(18); - ss << FLATDATA(pchReserved) << ip << port; - - #if defined(_MSC_VER) && _MSC_VER < 1300 - return std::vector((unsigned char*)&ss.begin()[0], (unsigned char*)&ss.end()[0]); - #else - return std::vector(ss.begin(), ss.end()); - #endif - } - - struct sockaddr_in GetSockAddr() const - { - struct sockaddr_in sockaddr; - memset(&sockaddr, 0, sizeof(sockaddr)); - sockaddr.sin_family = AF_INET; - sockaddr.sin_addr.s_addr = ip; - sockaddr.sin_port = port; - return sockaddr; - } - - bool IsIPv4() const - { - return (memcmp(pchReserved, pchIPv4, sizeof(pchIPv4)) == 0); - } - - bool IsRFC1918() const - { - return IsIPv4() && (GetByte(3) == 10 || - (GetByte(3) == 192 && GetByte(2) == 168) || - (GetByte(3) == 172 && - (GetByte(2) >= 16 && GetByte(2) <= 31))); - } - - bool IsRFC3927() const - { - return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254); - } - - bool IsLocal() const - { - return IsIPv4() && (GetByte(3) == 127 || - GetByte(3) == 0); - } - - bool IsRoutable() const - { - return IsValid() && - !(IsRFC1918() || IsRFC3927() || IsLocal()); - } - - bool IsValid() const - { - // Clean up 3-byte shifted addresses caused by garbage in size field - // of addr messages from versions before 0.2.9 checksum. - // Two consecutive addr messages look like this: - // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26... - // so if the first length field is garbled, it reads the second batch - // of addr misaligned by 3 bytes. - if (memcmp(pchReserved, pchIPv4+3, sizeof(pchIPv4)-3) == 0) - return false; - - return (ip != 0 && ip != INADDR_NONE && port != htons(USHRT_MAX)); - } - - unsigned char GetByte(int n) const - { - return ((unsigned char*)&ip)[3-n]; - } - - std::string ToStringIPPort() const - { - return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port)); - } - - std::string ToStringIP() const - { - return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0)); - } - - std::string ToStringPort() const - { - return strprintf("%u", ntohs(port)); - } - - std::string ToString() const - { - return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port)); - } - - void print() const - { - printf("CAddress(%s)\n", ToString().c_str()); - } -}; - - - - - - - -enum -{ - MSG_TX = 1, - MSG_BLOCK, -}; - -static const char* ppszTypeName[] = -{ - "ERROR", - "tx", - "block", -}; - -class CInv -{ -public: - int type; - uint256 hash; - - CInv() - { - type = 0; - hash = 0; - } - - CInv(int typeIn, const uint256& hashIn) - { - type = typeIn; - hash = hashIn; - } - - CInv(const std::string& strType, const uint256& hashIn) - { - int i; - for (i = 1; i < ARRAYLEN(ppszTypeName); i++) - { - if (strType == ppszTypeName[i]) - { - type = i; - break; - } - } - if (i == ARRAYLEN(ppszTypeName)) - throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType.c_str())); - hash = hashIn; - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(type); - READWRITE(hash); - ) - - friend inline bool operator<(const CInv& a, const CInv& b) - { - return (a.type < b.type || (a.type == b.type && a.hash < b.hash)); - } - - bool IsKnownType() const - { - return (type >= 1 && type < ARRAYLEN(ppszTypeName)); - } - - const char* GetCommand() const - { - if (!IsKnownType()) - throw std::out_of_range(strprintf("CInv::GetCommand() : type=%d unknown type", type)); - return ppszTypeName[type]; - } - - std::string ToString() const - { - return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,20).c_str()); - } - - void print() const - { - printf("CInv(%s)\n", ToString().c_str()); - } -}; - - - - - -class CRequestTracker -{ -public: - void (*fn)(void*, CDataStream&); - void* param1; - - explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL) - { - fn = fnIn; - param1 = param1In; - } - - bool IsNull() - { - return fn == NULL; - } -}; - - - - - -extern bool fClient; -extern bool fAllowDNS; -extern uint64 nLocalServices; -extern CAddress addrLocalHost; -extern CNode* pnodeLocalHost; -extern uint64 nLocalHostNonce; -extern boost::array vnThreadsRunning; -extern SOCKET hListenSocket; - -extern std::vector vNodes; -extern CCriticalSection cs_vNodes; -extern std::map, CAddress> mapAddresses; -extern CCriticalSection cs_mapAddresses; -extern std::map mapRelay; -extern std::deque > vRelayExpiration; -extern CCriticalSection cs_mapRelay; -extern std::map mapAlreadyAskedFor; - -// Settings -extern int fUseProxy; -extern CAddress addrProxy; - - - - - - -class CNode -{ -public: - // socket - uint64 nServices; - SOCKET hSocket; - CDataStream vSend; - CDataStream vRecv; - CCriticalSection cs_vSend; - CCriticalSection cs_vRecv; - int64 nLastSend; - int64 nLastRecv; - int64 nLastSendEmpty; - int64 nTimeConnected; - unsigned int nHeaderStart; - unsigned int nMessageStart; - CAddress addr; - int nVersion; - std::string strSubVer; - bool fClient; - bool fInbound; - bool fNetworkNode; - bool fSuccessfullyConnected; - bool fDisconnect; -protected: - int nRefCount; -public: - int64 nReleaseTime; - std::map mapRequests; - CCriticalSection cs_mapRequests; - uint256 hashContinue; - CBlockIndex* pindexLastGetBlocksBegin; - uint256 hashLastGetBlocksEnd; - int nStartingHeight; - - // flood relay - std::vector vAddrToSend; - std::set setAddrKnown; - bool fGetAddr; - std::set setKnown; - - // inventory based relay - std::set setInventoryKnown; - std::vector vInventoryToSend; - CCriticalSection cs_inventory; - std::multimap mapAskFor; - - // publish and subscription - std::vector vfSubscribe; - - - CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false) - { - nServices = 0; - hSocket = hSocketIn; - vSend.SetType(SER_NETWORK); - vSend.SetVersion(0); - vRecv.SetType(SER_NETWORK); - vRecv.SetVersion(0); - // Version 0.2 obsoletes 20 Feb 2012 - if (GetTime() > 1329696000) - { - vSend.SetVersion(209); - vRecv.SetVersion(209); - } - nLastSend = 0; - nLastRecv = 0; - nLastSendEmpty = GetTime(); - nTimeConnected = GetTime(); - nHeaderStart = -1; - nMessageStart = -1; - addr = addrIn; - nVersion = 0; - strSubVer = ""; - fClient = false; // set by version message - fInbound = fInboundIn; - fNetworkNode = false; - fSuccessfullyConnected = false; - fDisconnect = false; - nRefCount = 0; - nReleaseTime = 0; - hashContinue = 0; - pindexLastGetBlocksBegin = 0; - hashLastGetBlocksEnd = 0; - nStartingHeight = -1; - fGetAddr = false; - vfSubscribe.assign(256, false); - - // Be shy and don't send version until we hear - if (!fInbound) - PushVersion(); - } - - ~CNode() - { - if (hSocket != INVALID_SOCKET) - { - closesocket(hSocket); - hSocket = INVALID_SOCKET; - } - } - -private: - CNode(const CNode&); - void operator=(const CNode&); -public: - - - int GetRefCount() - { - return std::max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0); - } - - CNode* AddRef(int64 nTimeout=0) - { - if (nTimeout != 0) - nReleaseTime = std::max(nReleaseTime, GetTime() + nTimeout); - else - nRefCount++; - return this; - } - - void Release() - { - nRefCount--; - } - - - - void AddAddressKnown(const CAddress& addr) - { - setAddrKnown.insert(addr); - } - - void PushAddress(const CAddress& addr) - { - // Known checking here is only to save space from duplicates. - // SendMessages will filter it again for knowns that were added - // after addresses were pushed. - if (addr.IsValid() && !setAddrKnown.count(addr)) - vAddrToSend.push_back(addr); - } - - - void AddInventoryKnown(const CInv& inv) - { - CRITICAL_BLOCK(cs_inventory) - setInventoryKnown.insert(inv); - } - - void PushInventory(const CInv& inv) - { - CRITICAL_BLOCK(cs_inventory) - if (!setInventoryKnown.count(inv)) - vInventoryToSend.push_back(inv); - } - - void AskFor(const CInv& inv) - { - // We're using mapAskFor as a priority queue, - // the key is the earliest time the request can be sent - int64& nRequestTime = mapAlreadyAskedFor[inv]; - printf("askfor %s %"PRI64d"\n", inv.ToString().c_str(), nRequestTime); - - // Make sure not to reuse time indexes to keep things in the same order - int64 nNow = (GetTime() - 1) * 1000000; - static int64 nLastTime; - nLastTime = nNow = std::max(nNow, ++nLastTime); - - // Each retry is 2 minutes after the last - nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow); - mapAskFor.insert(std::make_pair(nRequestTime, inv)); - } - - - - void BeginMessage(const char* pszCommand) - { - cs_vSend.Enter(); - if (nHeaderStart != -1) - AbortMessage(); - nHeaderStart = vSend.size(); - vSend << CMessageHeader(pszCommand, 0); - nMessageStart = vSend.size(); - if (fDebug) - printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); - printf("sending: %s ", pszCommand); - } - - void AbortMessage() - { - if (nHeaderStart == -1) - return; - vSend.resize(nHeaderStart); - nHeaderStart = -1; - nMessageStart = -1; - cs_vSend.Leave(); - printf("(aborted)\n"); - } - - void EndMessage() - { - if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) - { - printf("dropmessages DROPPING SEND MESSAGE\n"); - AbortMessage(); - return; - } - - if (nHeaderStart == -1) - return; - - // Set the size - unsigned int nSize = vSend.size() - nMessageStart; - memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize)); - - // Set the checksum - if (vSend.GetVersion() >= 209) - { - uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end()); - unsigned int nChecksum = 0; - memcpy(&nChecksum, &hash, sizeof(nChecksum)); - assert(nMessageStart - nHeaderStart >= offsetof(CMessageHeader, nChecksum) + sizeof(nChecksum)); - memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nChecksum), &nChecksum, sizeof(nChecksum)); - } - - printf("(%d bytes) ", nSize); - printf("\n"); - - nHeaderStart = -1; - nMessageStart = -1; - cs_vSend.Leave(); - } - - void EndMessageAbortIfEmpty() - { - if (nHeaderStart == -1) - return; - int nSize = vSend.size() - nMessageStart; - if (nSize > 0) - EndMessage(); - else - AbortMessage(); - } - - - - void PushVersion() - { - /// when NTP implemented, change to just nTime = GetAdjustedTime() - int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); - CAddress addrYou = (fUseProxy ? CAddress("0.0.0.0") : addr); - CAddress addrMe = (fUseProxy ? CAddress("0.0.0.0") : addrLocalHost); - RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); - PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe, - nLocalHostNonce, std::string(pszSubVer), nBestHeight); - } - - - - - void PushMessage(const char* pszCommand) - { - try - { - BeginMessage(pszCommand); - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1) - { - try - { - BeginMessage(pszCommand); - vSend << a1; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3 << a4; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3 << a4 << a5; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3 << a4 << a5 << a6; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8, const T9& a9) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - - void PushRequest(const char* pszCommand, - void (*fn)(void*, CDataStream&), void* param1) - { - uint256 hashReply; - RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); - - CRITICAL_BLOCK(cs_mapRequests) - mapRequests[hashReply] = CRequestTracker(fn, param1); - - PushMessage(pszCommand, hashReply); - } - - template - void PushRequest(const char* pszCommand, const T1& a1, - void (*fn)(void*, CDataStream&), void* param1) - { - uint256 hashReply; - RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); - - CRITICAL_BLOCK(cs_mapRequests) - mapRequests[hashReply] = CRequestTracker(fn, param1); - - PushMessage(pszCommand, hashReply, a1); - } - - template - void PushRequest(const char* pszCommand, const T1& a1, const T2& a2, - void (*fn)(void*, CDataStream&), void* param1) - { - uint256 hashReply; - RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); - - CRITICAL_BLOCK(cs_mapRequests) - mapRequests[hashReply] = CRequestTracker(fn, param1); - - PushMessage(pszCommand, hashReply, a1, a2); - } - - - - void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd); - bool IsSubscribed(unsigned int nChannel); - void Subscribe(unsigned int nChannel, unsigned int nHops=0); - void CancelSubscribe(unsigned int nChannel); - void CloseSocketDisconnect(); - void Cleanup(); -}; - - - - - - - - - - -inline void RelayInventory(const CInv& inv) -{ - // Put on lists to offer to the other nodes - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - pnode->PushInventory(inv); -} - -template -void RelayMessage(const CInv& inv, const T& a) -{ - CDataStream ss(SER_NETWORK); - ss.reserve(10000); - ss << a; - RelayMessage(inv, ss); -} - -template<> -inline void RelayMessage<>(const CInv& inv, const CDataStream& ss) -{ - CRITICAL_BLOCK(cs_mapRelay) - { - // Expire old relay messages - while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) - { - mapRelay.erase(vRelayExpiration.front().second); - vRelayExpiration.pop_front(); - } - - // Save original serialized message so newer versions are preserved - mapRelay[inv] = ss; - vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv)); - } - - RelayInventory(inv); -} - - - - - - - - -// -// Templates for the publish and subscription system. -// The object being published as T& obj needs to have: -// a set setSources member -// specializations of AdvertInsert and AdvertErase -// Currently implemented for CTable and CProduct. -// - -template -void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) -{ - // Add to sources - obj.setSources.insert(pfrom->addr.ip); - - if (!AdvertInsert(obj)) - return; - - // Relay - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) - pnode->PushMessage("publish", nChannel, nHops, obj); -} - -template -void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) -{ - uint256 hash = obj.GetHash(); - - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) - pnode->PushMessage("pub-cancel", nChannel, nHops, hash); - - AdvertErase(obj); -} - -template -void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) -{ - // Remove a source - obj.setSources.erase(pfrom->addr.ip); - - // If no longer supported by any sources, cancel it - if (obj.setSources.empty()) - AdvertStopPublish(pfrom, nChannel, nHops, obj); -} - -#endif diff --git a/core/include/noui.h b/core/include/noui.h deleted file mode 100644 index afb19526c1..0000000000 --- a/core/include/noui.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_NOUI_H -#define BITCOIN_NOUI_H - -#include - -typedef void wxWindow; -#define wxYES 0x00000002 -#define wxOK 0x00000004 -#define wxNO 0x00000008 -#define wxYES_NO (wxYES|wxNO) -#define wxCANCEL 0x00000010 -#define wxAPPLY 0x00000020 -#define wxCLOSE 0x00000040 -#define wxOK_DEFAULT 0x00000000 -#define wxYES_DEFAULT 0x00000000 -#define wxNO_DEFAULT 0x00000080 -#define wxCANCEL_DEFAULT 0x80000000 -#define wxICON_EXCLAMATION 0x00000100 -#define wxICON_HAND 0x00000200 -#define wxICON_WARNING wxICON_EXCLAMATION -#define wxICON_ERROR wxICON_HAND -#define wxICON_QUESTION 0x00000400 -#define wxICON_INFORMATION 0x00000800 -#define wxICON_STOP wxICON_HAND -#define wxICON_ASTERISK wxICON_INFORMATION -#define wxICON_MASK (0x00000100|0x00000200|0x00000400|0x00000800) -#define wxFORWARD 0x00001000 -#define wxBACKWARD 0x00002000 -#define wxRESET 0x00004000 -#define wxHELP 0x00008000 -#define wxMORE 0x00010000 -#define wxSETUP 0x00020000 - -inline int MyMessageBox(const std::string& message, const std::string& caption="Message", int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1) -{ - printf("%s: %s\n", caption.c_str(), message.c_str()); - fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); - return 4; -} -#define wxMessageBox MyMessageBox - -inline int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1) -{ - return MyMessageBox(message, caption, style, parent, x, y); -} - -inline bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent) -{ - return true; -} - -inline void CalledSetStatusBar(const std::string& strText, int nField) -{ -} - -inline void UIThreadCall(boost::function0 fn) -{ -} - -inline void MainFrameRepaint() -{ -} - -#endif diff --git a/core/include/rpc.h b/core/include/rpc.h deleted file mode 100644 index 48a7b8a8a6..0000000000 --- a/core/include/rpc.h +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. - -void ThreadRPCServer(void* parg); -int CommandLineRPC(int argc, char *argv[]); diff --git a/core/include/script.h b/core/include/script.h deleted file mode 100644 index 22a6020dce..0000000000 --- a/core/include/script.h +++ /dev/null @@ -1,718 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef H_BITCOIN_SCRIPT -#define H_BITCOIN_SCRIPT - -#include "base58.h" - -#include -#include - -class CTransaction; - -enum -{ - SIGHASH_ALL = 1, - SIGHASH_NONE = 2, - SIGHASH_SINGLE = 3, - SIGHASH_ANYONECANPAY = 0x80, -}; - - - -enum opcodetype -{ - // push value - OP_0=0, - OP_FALSE=OP_0, - OP_PUSHDATA1=76, - OP_PUSHDATA2, - OP_PUSHDATA4, - OP_1NEGATE, - OP_RESERVED, - OP_1, - OP_TRUE=OP_1, - OP_2, - OP_3, - OP_4, - OP_5, - OP_6, - OP_7, - OP_8, - OP_9, - OP_10, - OP_11, - OP_12, - OP_13, - OP_14, - OP_15, - OP_16, - - // control - OP_NOP, - OP_VER, - OP_IF, - OP_NOTIF, - OP_VERIF, - OP_VERNOTIF, - OP_ELSE, - OP_ENDIF, - OP_VERIFY, - OP_RETURN, - - // stack ops - OP_TOALTSTACK, - OP_FROMALTSTACK, - OP_2DROP, - OP_2DUP, - OP_3DUP, - OP_2OVER, - OP_2ROT, - OP_2SWAP, - OP_IFDUP, - OP_DEPTH, - OP_DROP, - OP_DUP, - OP_NIP, - OP_OVER, - OP_PICK, - OP_ROLL, - OP_ROT, - OP_SWAP, - OP_TUCK, - - // splice ops - OP_CAT, - OP_SUBSTR, - OP_LEFT, - OP_RIGHT, - OP_SIZE, - - // bit logic - OP_INVERT, - OP_AND, - OP_OR, - OP_XOR, - OP_EQUAL, - OP_EQUALVERIFY, - OP_RESERVED1, - OP_RESERVED2, - - // numeric - OP_1ADD, - OP_1SUB, - OP_2MUL, - OP_2DIV, - OP_NEGATE, - OP_ABS, - OP_NOT, - OP_0NOTEQUAL, - - OP_ADD, - OP_SUB, - OP_MUL, - OP_DIV, - OP_MOD, - OP_LSHIFT, - OP_RSHIFT, - - OP_BOOLAND, - OP_BOOLOR, - OP_NUMEQUAL, - OP_NUMEQUALVERIFY, - OP_NUMNOTEQUAL, - OP_LESSTHAN, - OP_GREATERTHAN, - OP_LESSTHANOREQUAL, - OP_GREATERTHANOREQUAL, - OP_MIN, - OP_MAX, - - OP_WITHIN, - - // crypto - OP_RIPEMD160, - OP_SHA1, - OP_SHA256, - OP_HASH160, - OP_HASH256, - OP_CODESEPARATOR, - OP_CHECKSIG, - OP_CHECKSIGVERIFY, - OP_CHECKMULTISIG, - OP_CHECKMULTISIGVERIFY, - - // expansion - OP_NOP1, - OP_NOP2, - OP_NOP3, - OP_NOP4, - OP_NOP5, - OP_NOP6, - OP_NOP7, - OP_NOP8, - OP_NOP9, - OP_NOP10, - - - - // template matching params - OP_PUBKEYHASH = 0xfd, - OP_PUBKEY = 0xfe, - - OP_INVALIDOPCODE = 0xff, -}; - - - - - - - - -inline const char* GetOpName(opcodetype opcode) -{ - switch (opcode) - { - // push value - case OP_0 : return "0"; - case OP_PUSHDATA1 : return "OP_PUSHDATA1"; - case OP_PUSHDATA2 : return "OP_PUSHDATA2"; - case OP_PUSHDATA4 : return "OP_PUSHDATA4"; - case OP_1NEGATE : return "-1"; - case OP_RESERVED : return "OP_RESERVED"; - case OP_1 : return "1"; - case OP_2 : return "2"; - case OP_3 : return "3"; - case OP_4 : return "4"; - case OP_5 : return "5"; - case OP_6 : return "6"; - case OP_7 : return "7"; - case OP_8 : return "8"; - case OP_9 : return "9"; - case OP_10 : return "10"; - case OP_11 : return "11"; - case OP_12 : return "12"; - case OP_13 : return "13"; - case OP_14 : return "14"; - case OP_15 : return "15"; - case OP_16 : return "16"; - - // control - case OP_NOP : return "OP_NOP"; - case OP_VER : return "OP_VER"; - case OP_IF : return "OP_IF"; - case OP_NOTIF : return "OP_NOTIF"; - case OP_VERIF : return "OP_VERIF"; - case OP_VERNOTIF : return "OP_VERNOTIF"; - case OP_ELSE : return "OP_ELSE"; - case OP_ENDIF : return "OP_ENDIF"; - case OP_VERIFY : return "OP_VERIFY"; - case OP_RETURN : return "OP_RETURN"; - - // stack ops - case OP_TOALTSTACK : return "OP_TOALTSTACK"; - case OP_FROMALTSTACK : return "OP_FROMALTSTACK"; - case OP_2DROP : return "OP_2DROP"; - case OP_2DUP : return "OP_2DUP"; - case OP_3DUP : return "OP_3DUP"; - case OP_2OVER : return "OP_2OVER"; - case OP_2ROT : return "OP_2ROT"; - case OP_2SWAP : return "OP_2SWAP"; - case OP_IFDUP : return "OP_IFDUP"; - case OP_DEPTH : return "OP_DEPTH"; - case OP_DROP : return "OP_DROP"; - case OP_DUP : return "OP_DUP"; - case OP_NIP : return "OP_NIP"; - case OP_OVER : return "OP_OVER"; - case OP_PICK : return "OP_PICK"; - case OP_ROLL : return "OP_ROLL"; - case OP_ROT : return "OP_ROT"; - case OP_SWAP : return "OP_SWAP"; - case OP_TUCK : return "OP_TUCK"; - - // splice ops - case OP_CAT : return "OP_CAT"; - case OP_SUBSTR : return "OP_SUBSTR"; - case OP_LEFT : return "OP_LEFT"; - case OP_RIGHT : return "OP_RIGHT"; - case OP_SIZE : return "OP_SIZE"; - - // bit logic - case OP_INVERT : return "OP_INVERT"; - case OP_AND : return "OP_AND"; - case OP_OR : return "OP_OR"; - case OP_XOR : return "OP_XOR"; - case OP_EQUAL : return "OP_EQUAL"; - case OP_EQUALVERIFY : return "OP_EQUALVERIFY"; - case OP_RESERVED1 : return "OP_RESERVED1"; - case OP_RESERVED2 : return "OP_RESERVED2"; - - // numeric - case OP_1ADD : return "OP_1ADD"; - case OP_1SUB : return "OP_1SUB"; - case OP_2MUL : return "OP_2MUL"; - case OP_2DIV : return "OP_2DIV"; - case OP_NEGATE : return "OP_NEGATE"; - case OP_ABS : return "OP_ABS"; - case OP_NOT : return "OP_NOT"; - case OP_0NOTEQUAL : return "OP_0NOTEQUAL"; - case OP_ADD : return "OP_ADD"; - case OP_SUB : return "OP_SUB"; - case OP_MUL : return "OP_MUL"; - case OP_DIV : return "OP_DIV"; - case OP_MOD : return "OP_MOD"; - case OP_LSHIFT : return "OP_LSHIFT"; - case OP_RSHIFT : return "OP_RSHIFT"; - case OP_BOOLAND : return "OP_BOOLAND"; - case OP_BOOLOR : return "OP_BOOLOR"; - case OP_NUMEQUAL : return "OP_NUMEQUAL"; - case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY"; - case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL"; - case OP_LESSTHAN : return "OP_LESSTHAN"; - case OP_GREATERTHAN : return "OP_GREATERTHAN"; - case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL"; - case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL"; - case OP_MIN : return "OP_MIN"; - case OP_MAX : return "OP_MAX"; - case OP_WITHIN : return "OP_WITHIN"; - - // crypto - case OP_RIPEMD160 : return "OP_RIPEMD160"; - case OP_SHA1 : return "OP_SHA1"; - case OP_SHA256 : return "OP_SHA256"; - case OP_HASH160 : return "OP_HASH160"; - case OP_HASH256 : return "OP_HASH256"; - case OP_CODESEPARATOR : return "OP_CODESEPARATOR"; - case OP_CHECKSIG : return "OP_CHECKSIG"; - case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; - case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; - case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; - - // expanson - case OP_NOP1 : return "OP_NOP1"; - case OP_NOP2 : return "OP_NOP2"; - case OP_NOP3 : return "OP_NOP3"; - case OP_NOP4 : return "OP_NOP4"; - case OP_NOP5 : return "OP_NOP5"; - case OP_NOP6 : return "OP_NOP6"; - case OP_NOP7 : return "OP_NOP7"; - case OP_NOP8 : return "OP_NOP8"; - case OP_NOP9 : return "OP_NOP9"; - case OP_NOP10 : return "OP_NOP10"; - - - - // template matching params - case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; - case OP_PUBKEY : return "OP_PUBKEY"; - - case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; - default: - return "OP_UNKNOWN"; - } -}; - - - - -inline std::string ValueString(const std::vector& vch) -{ - if (vch.size() <= 4) - return strprintf("%d", CBigNum(vch).getint()); - else - return HexStr(vch); -} - -inline std::string StackString(const std::vector >& vStack) -{ - std::string str; - BOOST_FOREACH(const std::vector& vch, vStack) - { - if (!str.empty()) - str += " "; - str += ValueString(vch); - } - return str; -} - - - - - - - - - -class CScript : public std::vector -{ -protected: - CScript& push_int64(int64 n) - { - if (n == -1 || (n >= 1 && n <= 16)) - { - push_back(n + (OP_1 - 1)); - } - else - { - CBigNum bn(n); - *this << bn.getvch(); - } - return *this; - } - - CScript& push_uint64(uint64 n) - { - if (n >= 1 && n <= 16) - { - push_back(n + (OP_1 - 1)); - } - else - { - CBigNum bn(n); - *this << bn.getvch(); - } - return *this; - } - -public: - CScript() { } - CScript(const CScript& b) : std::vector(b.begin(), b.end()) { } - CScript(const_iterator pbegin, const_iterator pend) : std::vector(pbegin, pend) { } -#ifndef _MSC_VER - CScript(const unsigned char* pbegin, const unsigned char* pend) : std::vector(pbegin, pend) { } -#endif - - CScript& operator+=(const CScript& b) - { - insert(end(), b.begin(), b.end()); - return *this; - } - - friend CScript operator+(const CScript& a, const CScript& b) - { - CScript ret = a; - ret += b; - return ret; - } - - - explicit CScript(char b) { operator<<(b); } - explicit CScript(short b) { operator<<(b); } - explicit CScript(int b) { operator<<(b); } - explicit CScript(long b) { operator<<(b); } - explicit CScript(int64 b) { operator<<(b); } - explicit CScript(unsigned char b) { operator<<(b); } - explicit CScript(unsigned int b) { operator<<(b); } - explicit CScript(unsigned short b) { operator<<(b); } - explicit CScript(unsigned long b) { operator<<(b); } - explicit CScript(uint64 b) { operator<<(b); } - - explicit CScript(opcodetype b) { operator<<(b); } - explicit CScript(const uint256& b) { operator<<(b); } - explicit CScript(const CBigNum& b) { operator<<(b); } - explicit CScript(const std::vector& b) { operator<<(b); } - - - CScript& operator<<(char b) { return push_int64(b); } - CScript& operator<<(short b) { return push_int64(b); } - CScript& operator<<(int b) { return push_int64(b); } - CScript& operator<<(long b) { return push_int64(b); } - CScript& operator<<(int64 b) { return push_int64(b); } - CScript& operator<<(unsigned char b) { return push_uint64(b); } - CScript& operator<<(unsigned int b) { return push_uint64(b); } - CScript& operator<<(unsigned short b) { return push_uint64(b); } - CScript& operator<<(unsigned long b) { return push_uint64(b); } - CScript& operator<<(uint64 b) { return push_uint64(b); } - - CScript& operator<<(opcodetype opcode) - { - if (opcode < 0 || opcode > 0xff) - throw std::runtime_error("CScript::operator<<() : invalid opcode"); - insert(end(), (unsigned char)opcode); - return *this; - } - - CScript& operator<<(const uint160& b) - { - insert(end(), sizeof(b)); - insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b)); - return *this; - } - - CScript& operator<<(const uint256& b) - { - insert(end(), sizeof(b)); - insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b)); - return *this; - } - - CScript& operator<<(const CBigNum& b) - { - *this << b.getvch(); - return *this; - } - - CScript& operator<<(const std::vector& b) - { - if (b.size() < OP_PUSHDATA1) - { - insert(end(), (unsigned char)b.size()); - } - else if (b.size() <= 0xff) - { - insert(end(), OP_PUSHDATA1); - insert(end(), (unsigned char)b.size()); - } - else if (b.size() <= 0xffff) - { - insert(end(), OP_PUSHDATA2); - unsigned short nSize = b.size(); - insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize)); - } - else - { - insert(end(), OP_PUSHDATA4); - unsigned int nSize = b.size(); - insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize)); - } - insert(end(), b.begin(), b.end()); - return *this; - } - - CScript& operator<<(const CScript& b) - { - // I'm not sure if this should push the script or concatenate scripts. - // If there's ever a use for pushing a script onto a script, delete this member fn - assert(("warning: pushing a CScript onto a CScript with << is probably not intended, use + to concatenate", false)); - return *this; - } - - - bool GetOp(iterator& pc, opcodetype& opcodeRet, std::vector& vchRet) - { - // Wrapper so it can be called with either iterator or const_iterator - const_iterator pc2 = pc; - bool fRet = GetOp2(pc2, opcodeRet, &vchRet); - pc = begin() + (pc2 - begin()); - return fRet; - } - - bool GetOp(iterator& pc, opcodetype& opcodeRet) - { - const_iterator pc2 = pc; - bool fRet = GetOp2(pc2, opcodeRet, NULL); - pc = begin() + (pc2 - begin()); - return fRet; - } - - bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector& vchRet) const - { - return GetOp2(pc, opcodeRet, &vchRet); - } - - bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const - { - return GetOp2(pc, opcodeRet, NULL); - } - - bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector* pvchRet) const - { - opcodeRet = OP_INVALIDOPCODE; - if (pvchRet) - pvchRet->clear(); - if (pc >= end()) - return false; - - // Read instruction - if (end() - pc < 1) - return false; - unsigned int opcode = *pc++; - - // Immediate operand - if (opcode <= OP_PUSHDATA4) - { - unsigned int nSize; - if (opcode < OP_PUSHDATA1) - { - nSize = opcode; - } - else if (opcode == OP_PUSHDATA1) - { - if (end() - pc < 1) - return false; - nSize = *pc++; - } - else if (opcode == OP_PUSHDATA2) - { - if (end() - pc < 2) - return false; - nSize = 0; - memcpy(&nSize, &pc[0], 2); - pc += 2; - } - else if (opcode == OP_PUSHDATA4) - { - if (end() - pc < 4) - return false; - memcpy(&nSize, &pc[0], 4); - pc += 4; - } - if (end() - pc < nSize) - return false; - if (pvchRet) - pvchRet->assign(pc, pc + nSize); - pc += nSize; - } - - opcodeRet = (opcodetype)opcode; - return true; - } - - - void FindAndDelete(const CScript& b) - { - if (b.empty()) - return; - iterator pc = begin(); - opcodetype opcode; - do - { - while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0) - erase(pc, pc + b.size()); - } - while (GetOp(pc, opcode)); - } - - - int GetSigOpCount() const - { - int n = 0; - const_iterator pc = begin(); - while (pc < end()) - { - opcodetype opcode; - if (!GetOp(pc, opcode)) - break; - if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY) - n++; - else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY) - n += 20; - } - return n; - } - - - bool IsPushOnly() const - { - if (size() > 200) - return false; - const_iterator pc = begin(); - while (pc < end()) - { - opcodetype opcode; - if (!GetOp(pc, opcode)) - return false; - if (opcode > OP_16) - return false; - } - return true; - } - - - uint160 GetBitcoinAddressHash160() const - { - opcodetype opcode; - std::vector vch; - CScript::const_iterator pc = begin(); - if (!GetOp(pc, opcode, vch) || opcode != OP_DUP) return 0; - if (!GetOp(pc, opcode, vch) || opcode != OP_HASH160) return 0; - if (!GetOp(pc, opcode, vch) || vch.size() != sizeof(uint160)) return 0; - uint160 hash160 = uint160(vch); - if (!GetOp(pc, opcode, vch) || opcode != OP_EQUALVERIFY) return 0; - if (!GetOp(pc, opcode, vch) || opcode != OP_CHECKSIG) return 0; - if (pc != end()) return 0; - return hash160; - } - - std::string GetBitcoinAddress() const - { - uint160 hash160 = GetBitcoinAddressHash160(); - if (hash160 == 0) - return ""; - return Hash160ToAddress(hash160); - } - - void SetBitcoinAddress(const uint160& hash160) - { - this->clear(); - *this << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; - } - - void SetBitcoinAddress(const std::vector& vchPubKey) - { - SetBitcoinAddress(Hash160(vchPubKey)); - } - - bool SetBitcoinAddress(const std::string& strAddress) - { - this->clear(); - uint160 hash160; - if (!AddressToHash160(strAddress, hash160)) - return false; - SetBitcoinAddress(hash160); - return true; - } - - - void PrintHex() const - { - printf("CScript(%s)\n", HexStr(begin(), end(), true).c_str()); - } - - std::string ToString() const - { - std::string str; - opcodetype opcode; - std::vector vch; - const_iterator pc = begin(); - while (pc < end()) - { - if (!str.empty()) - str += " "; - if (!GetOp(pc, opcode, vch)) - { - str += "[error]"; - return str; - } - if (0 <= opcode && opcode <= OP_PUSHDATA4) - str += ValueString(vch); - else - str += GetOpName(opcode); - } - return str; - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - - - - - - -uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -bool IsStandard(const CScript& scriptPubKey); -bool IsMine(const CScript& scriptPubKey); -bool ExtractPubKey(const CScript& scriptPubKey, bool fMineOnly, std::vector& vchPubKeyRet); -bool ExtractHash160(const CScript& scriptPubKey, uint160& hash160Ret); -bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript()); -bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType=0); - -#endif diff --git a/core/include/serialize.h b/core/include/serialize.h deleted file mode 100644 index 0d66d6a956..0000000000 --- a/core/include/serialize.h +++ /dev/null @@ -1,1271 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_SERIALIZE_H -#define BITCOIN_SERIALIZE_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if defined(_MSC_VER) || defined(__BORLANDC__) -typedef __int64 int64; -typedef unsigned __int64 uint64; -#else -typedef long long int64; -typedef unsigned long long uint64; -#endif -#if defined(_MSC_VER) && _MSC_VER < 1300 -#define for if (false) ; else for -#endif -class CScript; -class CDataStream; -class CAutoFile; -static const unsigned int MAX_SIZE = 0x02000000; - -static const int VERSION = 32300; -static const char* pszSubVer = ""; -static const bool VERSION_IS_BETA = true; - - - - - - -///////////////////////////////////////////////////////////////// -// -// Templates for serializing to anything that looks like a stream, -// i.e. anything that supports .read(char*, int) and .write(char*, int) -// - -enum -{ - // primary actions - SER_NETWORK = (1 << 0), - SER_DISK = (1 << 1), - SER_GETHASH = (1 << 2), - - // modifiers - SER_SKIPSIG = (1 << 16), - SER_BLOCKHEADERONLY = (1 << 17), -}; - -#define IMPLEMENT_SERIALIZE(statements) \ - unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const \ - { \ - CSerActionGetSerializeSize ser_action; \ - const bool fGetSize = true; \ - const bool fWrite = false; \ - const bool fRead = false; \ - unsigned int nSerSize = 0; \ - ser_streamplaceholder s; \ - s.nType = nType; \ - s.nVersion = nVersion; \ - {statements} \ - return nSerSize; \ - } \ - template \ - void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const \ - { \ - CSerActionSerialize ser_action; \ - const bool fGetSize = false; \ - const bool fWrite = true; \ - const bool fRead = false; \ - unsigned int nSerSize = 0; \ - {statements} \ - } \ - template \ - void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) \ - { \ - CSerActionUnserialize ser_action; \ - const bool fGetSize = false; \ - const bool fWrite = false; \ - const bool fRead = true; \ - unsigned int nSerSize = 0; \ - {statements} \ - } - -#define READWRITE(obj) (nSerSize += ::SerReadWrite(s, (obj), nType, nVersion, ser_action)) - - - - - - -// -// Basic types -// -#define WRITEDATA(s, obj) s.write((char*)&(obj), sizeof(obj)) -#define READDATA(s, obj) s.read((char*)&(obj), sizeof(obj)) - -inline unsigned int GetSerializeSize(char a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed char a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned char a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed short a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned short a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed int a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned int a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed long a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned long a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(int64 a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(uint64 a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(float a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(double a, int, int=0) { return sizeof(a); } - -template inline void Serialize(Stream& s, char a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, signed char a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, unsigned char a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, signed short a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, unsigned short a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, signed int a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, unsigned int a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, signed long a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, unsigned long a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, int64 a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, uint64 a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, float a, int, int=0) { WRITEDATA(s, a); } -template inline void Serialize(Stream& s, double a, int, int=0) { WRITEDATA(s, a); } - -template inline void Unserialize(Stream& s, char& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, signed char& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, unsigned char& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, signed short& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, unsigned short& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, signed int& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, unsigned int& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, signed long& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, unsigned long& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, int64& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, uint64& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, float& a, int, int=0) { READDATA(s, a); } -template inline void Unserialize(Stream& s, double& a, int, int=0) { READDATA(s, a); } - -inline unsigned int GetSerializeSize(bool a, int, int=0) { return sizeof(char); } -template inline void Serialize(Stream& s, bool a, int, int=0) { char f=a; WRITEDATA(s, f); } -template inline void Unserialize(Stream& s, bool& a, int, int=0) { char f; READDATA(s, f); a=f; } - - - - - - -// -// Compact size -// size < 253 -- 1 byte -// size <= USHRT_MAX -- 3 bytes (253 + 2 bytes) -// size <= UINT_MAX -- 5 bytes (254 + 4 bytes) -// size > UINT_MAX -- 9 bytes (255 + 8 bytes) -// -inline unsigned int GetSizeOfCompactSize(uint64 nSize) -{ - if (nSize < 253) return sizeof(unsigned char); - else if (nSize <= USHRT_MAX) return sizeof(unsigned char) + sizeof(unsigned short); - else if (nSize <= UINT_MAX) return sizeof(unsigned char) + sizeof(unsigned int); - else return sizeof(unsigned char) + sizeof(uint64); -} - -template -void WriteCompactSize(Stream& os, uint64 nSize) -{ - if (nSize < 253) - { - unsigned char chSize = nSize; - WRITEDATA(os, chSize); - } - else if (nSize <= USHRT_MAX) - { - unsigned char chSize = 253; - unsigned short xSize = nSize; - WRITEDATA(os, chSize); - WRITEDATA(os, xSize); - } - else if (nSize <= UINT_MAX) - { - unsigned char chSize = 254; - unsigned int xSize = nSize; - WRITEDATA(os, chSize); - WRITEDATA(os, xSize); - } - else - { - unsigned char chSize = 255; - uint64 xSize = nSize; - WRITEDATA(os, chSize); - WRITEDATA(os, xSize); - } - return; -} - -template -uint64 ReadCompactSize(Stream& is) -{ - unsigned char chSize; - READDATA(is, chSize); - uint64 nSizeRet = 0; - if (chSize < 253) - { - nSizeRet = chSize; - } - else if (chSize == 253) - { - unsigned short xSize; - READDATA(is, xSize); - nSizeRet = xSize; - } - else if (chSize == 254) - { - unsigned int xSize; - READDATA(is, xSize); - nSizeRet = xSize; - } - else - { - uint64 xSize; - READDATA(is, xSize); - nSizeRet = xSize; - } - if (nSizeRet > (uint64)MAX_SIZE) - throw std::ios_base::failure("ReadCompactSize() : size too large"); - return nSizeRet; -} - - - -// -// Wrapper for serializing arrays and POD -// There's a clever template way to make arrays serialize normally, but MSVC6 doesn't support it -// -#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) -class CFlatData -{ -protected: - char* pbegin; - char* pend; -public: - CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } - char* begin() { return pbegin; } - const char* begin() const { return pbegin; } - char* end() { return pend; } - const char* end() const { return pend; } - - unsigned int GetSerializeSize(int, int=0) const - { - return pend - pbegin; - } - - template - void Serialize(Stream& s, int, int=0) const - { - s.write(pbegin, pend - pbegin); - } - - template - void Unserialize(Stream& s, int, int=0) - { - s.read(pbegin, pend - pbegin); - } -}; - - - -// -// string stored as a fixed length field -// -template -class CFixedFieldString -{ -protected: - const std::string* pcstr; - std::string* pstr; -public: - explicit CFixedFieldString(const std::string& str) : pcstr(&str), pstr(NULL) { } - explicit CFixedFieldString(std::string& str) : pcstr(&str), pstr(&str) { } - - unsigned int GetSerializeSize(int, int=0) const - { - return LEN; - } - - template - void Serialize(Stream& s, int, int=0) const - { - char pszBuf[LEN]; - strncpy(pszBuf, pcstr->c_str(), LEN); - s.write(pszBuf, LEN); - } - - template - void Unserialize(Stream& s, int, int=0) - { - if (pstr == NULL) - throw std::ios_base::failure("CFixedFieldString::Unserialize : trying to unserialize to const string"); - char pszBuf[LEN+1]; - s.read(pszBuf, LEN); - pszBuf[LEN] = '\0'; - *pstr = pszBuf; - } -}; - - - - - -// -// Forward declarations -// - -// string -template unsigned int GetSerializeSize(const std::basic_string& str, int, int=0); -template void Serialize(Stream& os, const std::basic_string& str, int, int=0); -template void Unserialize(Stream& is, std::basic_string& str, int, int=0); - -// vector -template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&); -template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&); -template inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion=VERSION); -template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&); -template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&); -template inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion=VERSION); -template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&); -template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&); -template inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion=VERSION); - -// others derived from vector -extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion=VERSION); -template void Serialize(Stream& os, const CScript& v, int nType, int nVersion=VERSION); -template void Unserialize(Stream& is, CScript& v, int nType, int nVersion=VERSION); - -// pair -template unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion=VERSION); -template void Serialize(Stream& os, const std::pair& item, int nType, int nVersion=VERSION); -template void Unserialize(Stream& is, std::pair& item, int nType, int nVersion=VERSION); - -// 3 tuple -template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion=VERSION); -template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion=VERSION); -template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion=VERSION); - -// 4 tuple -template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion=VERSION); -template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion=VERSION); -template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion=VERSION); - -// map -template unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion=VERSION); -template void Serialize(Stream& os, const std::map& m, int nType, int nVersion=VERSION); -template void Unserialize(Stream& is, std::map& m, int nType, int nVersion=VERSION); - -// set -template unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion=VERSION); -template void Serialize(Stream& os, const std::set& m, int nType, int nVersion=VERSION); -template void Unserialize(Stream& is, std::set& m, int nType, int nVersion=VERSION); - - - - - -// -// If none of the specialized versions above matched, default to calling member function. -// "int nType" is changed to "long nType" to keep from getting an ambiguous overload error. -// The compiler will only cast int to long if none of the other templates matched. -// Thanks to Boost serialization for this idea. -// -template -inline unsigned int GetSerializeSize(const T& a, long nType, int nVersion=VERSION) -{ - return a.GetSerializeSize((int)nType, nVersion); -} - -template -inline void Serialize(Stream& os, const T& a, long nType, int nVersion=VERSION) -{ - a.Serialize(os, (int)nType, nVersion); -} - -template -inline void Unserialize(Stream& is, T& a, long nType, int nVersion=VERSION) -{ - a.Unserialize(is, (int)nType, nVersion); -} - - - - - -// -// string -// -template -unsigned int GetSerializeSize(const std::basic_string& str, int, int) -{ - return GetSizeOfCompactSize(str.size()) + str.size() * sizeof(str[0]); -} - -template -void Serialize(Stream& os, const std::basic_string& str, int, int) -{ - WriteCompactSize(os, str.size()); - if (!str.empty()) - os.write((char*)&str[0], str.size() * sizeof(str[0])); -} - -template -void Unserialize(Stream& is, std::basic_string& str, int, int) -{ - unsigned int nSize = ReadCompactSize(is); - str.resize(nSize); - if (nSize != 0) - is.read((char*)&str[0], nSize * sizeof(str[0])); -} - - - -// -// vector -// -template -unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&) -{ - return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); -} - -template -unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&) -{ - unsigned int nSize = GetSizeOfCompactSize(v.size()); - for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) - nSize += GetSerializeSize((*vi), nType, nVersion); - return nSize; -} - -template -inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion) -{ - return GetSerializeSize_impl(v, nType, nVersion, boost::is_fundamental()); -} - - -template -void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&) -{ - WriteCompactSize(os, v.size()); - if (!v.empty()) - os.write((char*)&v[0], v.size() * sizeof(T)); -} - -template -void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&) -{ - WriteCompactSize(os, v.size()); - for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) - ::Serialize(os, (*vi), nType, nVersion); -} - -template -inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion) -{ - Serialize_impl(os, v, nType, nVersion, boost::is_fundamental()); -} - - -template -void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&) -{ - //unsigned int nSize = ReadCompactSize(is); - //v.resize(nSize); - //is.read((char*)&v[0], nSize * sizeof(T)); - - // Limit size per read so bogus size value won't cause out of memory - v.clear(); - unsigned int nSize = ReadCompactSize(is); - unsigned int i = 0; - while (i < nSize) - { - unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); - v.resize(i + blk); - is.read((char*)&v[i], blk * sizeof(T)); - i += blk; - } -} - -template -void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&) -{ - //unsigned int nSize = ReadCompactSize(is); - //v.resize(nSize); - //for (std::vector::iterator vi = v.begin(); vi != v.end(); ++vi) - // Unserialize(is, (*vi), nType, nVersion); - - v.clear(); - unsigned int nSize = ReadCompactSize(is); - unsigned int i = 0; - unsigned int nMid = 0; - while (nMid < nSize) - { - nMid += 5000000 / sizeof(T); - if (nMid > nSize) - nMid = nSize; - v.resize(nMid); - for (; i < nMid; i++) - Unserialize(is, v[i], nType, nVersion); - } -} - -template -inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion) -{ - Unserialize_impl(is, v, nType, nVersion, boost::is_fundamental()); -} - - - -// -// others derived from vector -// -inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion) -{ - return GetSerializeSize((const std::vector&)v, nType, nVersion); -} - -template -void Serialize(Stream& os, const CScript& v, int nType, int nVersion) -{ - Serialize(os, (const std::vector&)v, nType, nVersion); -} - -template -void Unserialize(Stream& is, CScript& v, int nType, int nVersion) -{ - Unserialize(is, (std::vector&)v, nType, nVersion); -} - - - -// -// pair -// -template -unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion) -{ - return GetSerializeSize(item.first, nType, nVersion) + GetSerializeSize(item.second, nType, nVersion); -} - -template -void Serialize(Stream& os, const std::pair& item, int nType, int nVersion) -{ - Serialize(os, item.first, nType, nVersion); - Serialize(os, item.second, nType, nVersion); -} - -template -void Unserialize(Stream& is, std::pair& item, int nType, int nVersion) -{ - Unserialize(is, item.first, nType, nVersion); - Unserialize(is, item.second, nType, nVersion); -} - - - -// -// 3 tuple -// -template -unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) -{ - unsigned int nSize = 0; - nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); - return nSize; -} - -template -void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) -{ - Serialize(os, boost::get<0>(item), nType, nVersion); - Serialize(os, boost::get<1>(item), nType, nVersion); - Serialize(os, boost::get<2>(item), nType, nVersion); -} - -template -void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) -{ - Unserialize(is, boost::get<0>(item), nType, nVersion); - Unserialize(is, boost::get<1>(item), nType, nVersion); - Unserialize(is, boost::get<2>(item), nType, nVersion); -} - - - -// -// 4 tuple -// -template -unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) -{ - unsigned int nSize = 0; - nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<3>(item), nType, nVersion); - return nSize; -} - -template -void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) -{ - Serialize(os, boost::get<0>(item), nType, nVersion); - Serialize(os, boost::get<1>(item), nType, nVersion); - Serialize(os, boost::get<2>(item), nType, nVersion); - Serialize(os, boost::get<3>(item), nType, nVersion); -} - -template -void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) -{ - Unserialize(is, boost::get<0>(item), nType, nVersion); - Unserialize(is, boost::get<1>(item), nType, nVersion); - Unserialize(is, boost::get<2>(item), nType, nVersion); - Unserialize(is, boost::get<3>(item), nType, nVersion); -} - - - -// -// map -// -template -unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion) -{ - unsigned int nSize = GetSizeOfCompactSize(m.size()); - for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) - nSize += GetSerializeSize((*mi), nType, nVersion); - return nSize; -} - -template -void Serialize(Stream& os, const std::map& m, int nType, int nVersion) -{ - WriteCompactSize(os, m.size()); - for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) - Serialize(os, (*mi), nType, nVersion); -} - -template -void Unserialize(Stream& is, std::map& m, int nType, int nVersion) -{ - m.clear(); - unsigned int nSize = ReadCompactSize(is); - typename std::map::iterator mi = m.begin(); - for (unsigned int i = 0; i < nSize; i++) - { - std::pair item; - Unserialize(is, item, nType, nVersion); - mi = m.insert(mi, item); - } -} - - - -// -// set -// -template -unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion) -{ - unsigned int nSize = GetSizeOfCompactSize(m.size()); - for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) - nSize += GetSerializeSize((*it), nType, nVersion); - return nSize; -} - -template -void Serialize(Stream& os, const std::set& m, int nType, int nVersion) -{ - WriteCompactSize(os, m.size()); - for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) - Serialize(os, (*it), nType, nVersion); -} - -template -void Unserialize(Stream& is, std::set& m, int nType, int nVersion) -{ - m.clear(); - unsigned int nSize = ReadCompactSize(is); - typename std::set::iterator it = m.begin(); - for (unsigned int i = 0; i < nSize; i++) - { - K key; - Unserialize(is, key, nType, nVersion); - it = m.insert(it, key); - } -} - - - -// -// Support for IMPLEMENT_SERIALIZE and READWRITE macro -// -class CSerActionGetSerializeSize { }; -class CSerActionSerialize { }; -class CSerActionUnserialize { }; - -template -inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionGetSerializeSize ser_action) -{ - return ::GetSerializeSize(obj, nType, nVersion); -} - -template -inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionSerialize ser_action) -{ - ::Serialize(s, obj, nType, nVersion); - return 0; -} - -template -inline unsigned int SerReadWrite(Stream& s, T& obj, int nType, int nVersion, CSerActionUnserialize ser_action) -{ - ::Unserialize(s, obj, nType, nVersion); - return 0; -} - -struct ser_streamplaceholder -{ - int nType; - int nVersion; -}; - - - - - - - - - -// -// Allocator that clears its contents before deletion -// -template -struct secure_allocator : public std::allocator -{ - // MSVC8 default copy constructor is broken - typedef std::allocator base; - typedef typename base::size_type size_type; - typedef typename base::difference_type difference_type; - typedef typename base::pointer pointer; - typedef typename base::const_pointer const_pointer; - typedef typename base::reference reference; - typedef typename base::const_reference const_reference; - typedef typename base::value_type value_type; - secure_allocator() throw() {} - secure_allocator(const secure_allocator& a) throw() : base(a) {} - template - secure_allocator(const secure_allocator& a) throw() : base(a) {} - ~secure_allocator() throw() {} - template struct rebind - { typedef secure_allocator<_Other> other; }; - - void deallocate(T* p, std::size_t n) - { - if (p != NULL) - memset(p, 0, sizeof(T) * n); - std::allocator::deallocate(p, n); - } -}; - - - -// -// Double ended buffer combining vector and stream-like interfaces. -// >> and << read and write unformatted data using the above serialization templates. -// Fills with data in linear time; some stringstream implementations take N^2 time. -// -class CDataStream -{ -protected: - typedef std::vector > vector_type; - vector_type vch; - unsigned int nReadPos; - short state; - short exceptmask; -public: - int nType; - int nVersion; - - typedef vector_type::allocator_type allocator_type; - typedef vector_type::size_type size_type; - typedef vector_type::difference_type difference_type; - typedef vector_type::reference reference; - typedef vector_type::const_reference const_reference; - typedef vector_type::value_type value_type; - typedef vector_type::iterator iterator; - typedef vector_type::const_iterator const_iterator; - typedef vector_type::reverse_iterator reverse_iterator; - - explicit CDataStream(int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) - { - Init(nTypeIn, nVersionIn); - } - - CDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(pbegin, pend) - { - Init(nTypeIn, nVersionIn); - } - -#if !defined(_MSC_VER) || _MSC_VER >= 1300 - CDataStream(const char* pbegin, const char* pend, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(pbegin, pend) - { - Init(nTypeIn, nVersionIn); - } -#endif - - CDataStream(const vector_type& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end()) - { - Init(nTypeIn, nVersionIn); - } - - CDataStream(const std::vector& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end()) - { - Init(nTypeIn, nVersionIn); - } - - CDataStream(const std::vector& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch((char*)&vchIn.begin()[0], (char*)&vchIn.end()[0]) - { - Init(nTypeIn, nVersionIn); - } - - void Init(int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) - { - nReadPos = 0; - nType = nTypeIn; - nVersion = nVersionIn; - state = 0; - exceptmask = std::ios::badbit | std::ios::failbit; - } - - CDataStream& operator+=(const CDataStream& b) - { - vch.insert(vch.end(), b.begin(), b.end()); - return *this; - } - - friend CDataStream operator+(const CDataStream& a, const CDataStream& b) - { - CDataStream ret = a; - ret += b; - return (ret); - } - - std::string str() const - { - return (std::string(begin(), end())); - } - - - // - // Vector subset - // - const_iterator begin() const { return vch.begin() + nReadPos; } - iterator begin() { return vch.begin() + nReadPos; } - const_iterator end() const { return vch.end(); } - iterator end() { return vch.end(); } - size_type size() const { return vch.size() - nReadPos; } - bool empty() const { return vch.size() == nReadPos; } - void resize(size_type n, value_type c=0) { vch.resize(n + nReadPos, c); } - void reserve(size_type n) { vch.reserve(n + nReadPos); } - const_reference operator[](size_type pos) const { return vch[pos + nReadPos]; } - reference operator[](size_type pos) { return vch[pos + nReadPos]; } - void clear() { vch.clear(); nReadPos = 0; } - iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); } - void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); } - - void insert(iterator it, const_iterator first, const_iterator last) - { - if (it == vch.begin() + nReadPos && last - first <= nReadPos) - { - // special case for inserting at the front when there's room - nReadPos -= (last - first); - memcpy(&vch[nReadPos], &first[0], last - first); - } - else - vch.insert(it, first, last); - } - - void insert(iterator it, std::vector::const_iterator first, std::vector::const_iterator last) - { - if (it == vch.begin() + nReadPos && last - first <= nReadPos) - { - // special case for inserting at the front when there's room - nReadPos -= (last - first); - memcpy(&vch[nReadPos], &first[0], last - first); - } - else - vch.insert(it, first, last); - } - -#if !defined(_MSC_VER) || _MSC_VER >= 1300 - void insert(iterator it, const char* first, const char* last) - { - if (it == vch.begin() + nReadPos && last - first <= nReadPos) - { - // special case for inserting at the front when there's room - nReadPos -= (last - first); - memcpy(&vch[nReadPos], &first[0], last - first); - } - else - vch.insert(it, first, last); - } -#endif - - iterator erase(iterator it) - { - if (it == vch.begin() + nReadPos) - { - // special case for erasing from the front - if (++nReadPos >= vch.size()) - { - // whenever we reach the end, we take the opportunity to clear the buffer - nReadPos = 0; - return vch.erase(vch.begin(), vch.end()); - } - return vch.begin() + nReadPos; - } - else - return vch.erase(it); - } - - iterator erase(iterator first, iterator last) - { - if (first == vch.begin() + nReadPos) - { - // special case for erasing from the front - if (last == vch.end()) - { - nReadPos = 0; - return vch.erase(vch.begin(), vch.end()); - } - else - { - nReadPos = (last - vch.begin()); - return last; - } - } - else - return vch.erase(first, last); - } - - inline void Compact() - { - vch.erase(vch.begin(), vch.begin() + nReadPos); - nReadPos = 0; - } - - bool Rewind(size_type n) - { - // Rewind by n characters if the buffer hasn't been compacted yet - if (n > nReadPos) - return false; - nReadPos -= n; - return true; - } - - - // - // Stream subset - // - void setstate(short bits, const char* psz) - { - state |= bits; - if (state & exceptmask) - throw std::ios_base::failure(psz); - } - - bool eof() const { return size() == 0; } - bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } - bool good() const { return !eof() && (state == 0); } - void clear(short n) { state = n; } // name conflict with vector clear() - short exceptions() { return exceptmask; } - short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CDataStream"); return prev; } - CDataStream* rdbuf() { return this; } - int in_avail() { return size(); } - - void SetType(int n) { nType = n; } - int GetType() { return nType; } - void SetVersion(int n) { nVersion = n; } - int GetVersion() { return nVersion; } - void ReadVersion() { *this >> nVersion; } - void WriteVersion() { *this << nVersion; } - - CDataStream& read(char* pch, int nSize) - { - // Read from the beginning of the buffer - assert(nSize >= 0); - unsigned int nReadPosNext = nReadPos + nSize; - if (nReadPosNext >= vch.size()) - { - if (nReadPosNext > vch.size()) - { - setstate(std::ios::failbit, "CDataStream::read() : end of data"); - memset(pch, 0, nSize); - nSize = vch.size() - nReadPos; - } - memcpy(pch, &vch[nReadPos], nSize); - nReadPos = 0; - vch.clear(); - return (*this); - } - memcpy(pch, &vch[nReadPos], nSize); - nReadPos = nReadPosNext; - return (*this); - } - - CDataStream& ignore(int nSize) - { - // Ignore from the beginning of the buffer - assert(nSize >= 0); - unsigned int nReadPosNext = nReadPos + nSize; - if (nReadPosNext >= vch.size()) - { - if (nReadPosNext > vch.size()) - { - setstate(std::ios::failbit, "CDataStream::ignore() : end of data"); - nSize = vch.size() - nReadPos; - } - nReadPos = 0; - vch.clear(); - return (*this); - } - nReadPos = nReadPosNext; - return (*this); - } - - CDataStream& write(const char* pch, int nSize) - { - // Write to the end of the buffer - assert(nSize >= 0); - vch.insert(vch.end(), pch, pch + nSize); - return (*this); - } - - template - void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const - { - // Special case: stream << stream concatenates like stream += stream - if (!vch.empty()) - s.write((char*)&vch[0], vch.size() * sizeof(vch[0])); - } - - template - unsigned int GetSerializeSize(const T& obj) - { - // Tells the size of the object if serialized to this stream - return ::GetSerializeSize(obj, nType, nVersion); - } - - template - CDataStream& operator<<(const T& obj) - { - // Serialize to this stream - ::Serialize(*this, obj, nType, nVersion); - return (*this); - } - - template - CDataStream& operator>>(T& obj) - { - // Unserialize from this stream - ::Unserialize(*this, obj, nType, nVersion); - return (*this); - } -}; - -#ifdef TESTCDATASTREAM -// VC6sp6 -// CDataStream: -// n=1000 0 seconds -// n=2000 0 seconds -// n=4000 0 seconds -// n=8000 0 seconds -// n=16000 0 seconds -// n=32000 0 seconds -// n=64000 1 seconds -// n=128000 1 seconds -// n=256000 2 seconds -// n=512000 4 seconds -// n=1024000 8 seconds -// n=2048000 16 seconds -// n=4096000 32 seconds -// stringstream: -// n=1000 1 seconds -// n=2000 1 seconds -// n=4000 13 seconds -// n=8000 87 seconds -// n=16000 400 seconds -// n=32000 1660 seconds -// n=64000 6749 seconds -// n=128000 27241 seconds -// n=256000 109804 seconds -#include -int main(int argc, char *argv[]) -{ - vector vch(0xcc, 250); - printf("CDataStream:\n"); - for (int n = 1000; n <= 4500000; n *= 2) - { - CDataStream ss; - time_t nStart = time(NULL); - for (int i = 0; i < n; i++) - ss.write((char*)&vch[0], vch.size()); - printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); - } - printf("stringstream:\n"); - for (int n = 1000; n <= 4500000; n *= 2) - { - stringstream ss; - time_t nStart = time(NULL); - for (int i = 0; i < n; i++) - ss.write((char*)&vch[0], vch.size()); - printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); - } -} -#endif - - - - - - - - - - -// -// Automatic closing wrapper for FILE* -// - Will automatically close the file when it goes out of scope if not null. -// - If you're returning the file pointer, return file.release(). -// - If you need to close the file early, use file.fclose() instead of fclose(file). -// -class CAutoFile -{ -protected: - FILE* file; - short state; - short exceptmask; -public: - int nType; - int nVersion; - - typedef FILE element_type; - - CAutoFile(FILE* filenew=NULL, int nTypeIn=SER_DISK, int nVersionIn=VERSION) - { - file = filenew; - nType = nTypeIn; - nVersion = nVersionIn; - state = 0; - exceptmask = std::ios::badbit | std::ios::failbit; - } - - ~CAutoFile() - { - fclose(); - } - - void fclose() - { - if (file != NULL && file != stdin && file != stdout && file != stderr) - ::fclose(file); - file = NULL; - } - - FILE* release() { FILE* ret = file; file = NULL; return ret; } - operator FILE*() { return file; } - FILE* operator->() { return file; } - FILE& operator*() { return *file; } - FILE** operator&() { return &file; } - FILE* operator=(FILE* pnew) { return file = pnew; } - bool operator!() { return (file == NULL); } - - - // - // Stream subset - // - void setstate(short bits, const char* psz) - { - state |= bits; - if (state & exceptmask) - throw std::ios_base::failure(psz); - } - - bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } - bool good() const { return state == 0; } - void clear(short n = 0) { state = n; } - short exceptions() { return exceptmask; } - short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CAutoFile"); return prev; } - - void SetType(int n) { nType = n; } - int GetType() { return nType; } - void SetVersion(int n) { nVersion = n; } - int GetVersion() { return nVersion; } - void ReadVersion() { *this >> nVersion; } - void WriteVersion() { *this << nVersion; } - - CAutoFile& read(char* pch, int nSize) - { - if (!file) - throw std::ios_base::failure("CAutoFile::read : file handle is NULL"); - if (fread(pch, 1, nSize, file) != nSize) - setstate(std::ios::failbit, feof(file) ? "CAutoFile::read : end of file" : "CAutoFile::read : fread failed"); - return (*this); - } - - CAutoFile& write(const char* pch, int nSize) - { - if (!file) - throw std::ios_base::failure("CAutoFile::write : file handle is NULL"); - if (fwrite(pch, 1, nSize, file) != nSize) - setstate(std::ios::failbit, "CAutoFile::write : write failed"); - return (*this); - } - - template - unsigned int GetSerializeSize(const T& obj) - { - // Tells the size of the object if serialized to this stream - return ::GetSerializeSize(obj, nType, nVersion); - } - - template - CAutoFile& operator<<(const T& obj) - { - // Serialize to this stream - if (!file) - throw std::ios_base::failure("CAutoFile::operator<< : file handle is NULL"); - ::Serialize(*this, obj, nType, nVersion); - return (*this); - } - - template - CAutoFile& operator>>(T& obj) - { - // Unserialize from this stream - if (!file) - throw std::ios_base::failure("CAutoFile::operator>> : file handle is NULL"); - ::Unserialize(*this, obj, nType, nVersion); - return (*this); - } -}; - -#endif diff --git a/core/include/strlcpy.h b/core/include/strlcpy.h deleted file mode 100644 index d4d1908e7a..0000000000 --- a/core/include/strlcpy.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 1998 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#ifndef BITCOIN_STRLCPY_H -#define BITCOIN_STRLCPY_H -/* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. - */ -inline size_t strlcpy(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - - /* Copy as many bytes as will fit */ - if (n != 0) - { - while (--n != 0) - { - if ((*d++ = *s++) == '\0') - break; - } - } - - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) - { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } - - return(s - src - 1); /* count does not include NUL */ -} - -/* - * Appends src to string dst of size siz (unlike strncat, siz is the - * full size of dst, not space left). At most siz-1 characters - * will be copied. Always NUL terminates (unless siz <= strlen(dst)). - * Returns strlen(src) + MIN(siz, strlen(initial dst)). - * If retval >= siz, truncation occurred. - */ -inline size_t strlcat(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - size_t dlen; - - /* Find the end of dst and adjust bytes left but don't go past end */ - while (n-- != 0 && *d != '\0') - d++; - dlen = d - dst; - n = siz - dlen; - - if (n == 0) - return(dlen + strlen(s)); - while (*s != '\0') - { - if (n != 1) - { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; - - return(dlen + (s - src)); /* count does not include NUL */ -} -#endif diff --git a/core/include/uint256.h b/core/include/uint256.h deleted file mode 100644 index 14feb1683d..0000000000 --- a/core/include/uint256.h +++ /dev/null @@ -1,765 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_UINT256_H -#define BITCOIN_UINT256_H - -#include "serialize.h" - -#include -#include -#include - -#if defined(_MSC_VER) || defined(__BORLANDC__) -typedef __int64 int64; -typedef unsigned __int64 uint64; -#else -typedef long long int64; -typedef unsigned long long uint64; -#endif -#if defined(_MSC_VER) && _MSC_VER < 1300 -#define for if (false) ; else for -#endif - - -inline int Testuint256AdHoc(std::vector vArg); - - - -// We have to keep a separate base class without constructors -// so the compiler will let us use it in a union -template -class base_uint -{ -protected: - enum { WIDTH=BITS/32 }; - unsigned int pn[WIDTH]; -public: - - bool operator!() const - { - for (int i = 0; i < WIDTH; i++) - if (pn[i] != 0) - return false; - return true; - } - - const base_uint operator~() const - { - base_uint ret; - for (int i = 0; i < WIDTH; i++) - ret.pn[i] = ~pn[i]; - return ret; - } - - const base_uint operator-() const - { - base_uint ret; - for (int i = 0; i < WIDTH; i++) - ret.pn[i] = ~pn[i]; - ret++; - return ret; - } - - - base_uint& operator=(uint64 b) - { - pn[0] = (unsigned int)b; - pn[1] = (unsigned int)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - return *this; - } - - base_uint& operator^=(const base_uint& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] ^= b.pn[i]; - return *this; - } - - base_uint& operator&=(const base_uint& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] &= b.pn[i]; - return *this; - } - - base_uint& operator|=(const base_uint& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] |= b.pn[i]; - return *this; - } - - base_uint& operator^=(uint64 b) - { - pn[0] ^= (unsigned int)b; - pn[1] ^= (unsigned int)(b >> 32); - return *this; - } - - base_uint& operator&=(uint64 b) - { - pn[0] &= (unsigned int)b; - pn[1] &= (unsigned int)(b >> 32); - return *this; - } - - base_uint& operator|=(uint64 b) - { - pn[0] |= (unsigned int)b; - pn[1] |= (unsigned int)(b >> 32); - return *this; - } - - base_uint& operator<<=(unsigned int shift) - { - base_uint a(*this); - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - int k = shift / 32; - shift = shift % 32; - for (int i = 0; i < WIDTH; i++) - { - if (i+k+1 < WIDTH && shift != 0) - pn[i+k+1] |= (a.pn[i] >> (32-shift)); - if (i+k < WIDTH) - pn[i+k] |= (a.pn[i] << shift); - } - return *this; - } - - base_uint& operator>>=(unsigned int shift) - { - base_uint a(*this); - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - int k = shift / 32; - shift = shift % 32; - for (int i = 0; i < WIDTH; i++) - { - if (i-k-1 >= 0 && shift != 0) - pn[i-k-1] |= (a.pn[i] << (32-shift)); - if (i-k >= 0) - pn[i-k] |= (a.pn[i] >> shift); - } - return *this; - } - - base_uint& operator+=(const base_uint& b) - { - uint64 carry = 0; - for (int i = 0; i < WIDTH; i++) - { - uint64 n = carry + pn[i] + b.pn[i]; - pn[i] = n & 0xffffffff; - carry = n >> 32; - } - return *this; - } - - base_uint& operator-=(const base_uint& b) - { - *this += -b; - return *this; - } - - base_uint& operator+=(uint64 b64) - { - base_uint b; - b = b64; - *this += b; - return *this; - } - - base_uint& operator-=(uint64 b64) - { - base_uint b; - b = b64; - *this += -b; - return *this; - } - - - base_uint& operator++() - { - // prefix operator - int i = 0; - while (++pn[i] == 0 && i < WIDTH-1) - i++; - return *this; - } - - const base_uint operator++(int) - { - // postfix operator - const base_uint ret = *this; - ++(*this); - return ret; - } - - base_uint& operator--() - { - // prefix operator - int i = 0; - while (--pn[i] == -1 && i < WIDTH-1) - i++; - return *this; - } - - const base_uint operator--(int) - { - // postfix operator - const base_uint ret = *this; - --(*this); - return ret; - } - - - friend inline bool operator<(const base_uint& a, const base_uint& b) - { - for (int i = base_uint::WIDTH-1; i >= 0; i--) - { - if (a.pn[i] < b.pn[i]) - return true; - else if (a.pn[i] > b.pn[i]) - return false; - } - return false; - } - - friend inline bool operator<=(const base_uint& a, const base_uint& b) - { - for (int i = base_uint::WIDTH-1; i >= 0; i--) - { - if (a.pn[i] < b.pn[i]) - return true; - else if (a.pn[i] > b.pn[i]) - return false; - } - return true; - } - - friend inline bool operator>(const base_uint& a, const base_uint& b) - { - for (int i = base_uint::WIDTH-1; i >= 0; i--) - { - if (a.pn[i] > b.pn[i]) - return true; - else if (a.pn[i] < b.pn[i]) - return false; - } - return false; - } - - friend inline bool operator>=(const base_uint& a, const base_uint& b) - { - for (int i = base_uint::WIDTH-1; i >= 0; i--) - { - if (a.pn[i] > b.pn[i]) - return true; - else if (a.pn[i] < b.pn[i]) - return false; - } - return true; - } - - friend inline bool operator==(const base_uint& a, const base_uint& b) - { - for (int i = 0; i < base_uint::WIDTH; i++) - if (a.pn[i] != b.pn[i]) - return false; - return true; - } - - friend inline bool operator==(const base_uint& a, uint64 b) - { - if (a.pn[0] != (unsigned int)b) - return false; - if (a.pn[1] != (unsigned int)(b >> 32)) - return false; - for (int i = 2; i < base_uint::WIDTH; i++) - if (a.pn[i] != 0) - return false; - return true; - } - - friend inline bool operator!=(const base_uint& a, const base_uint& b) - { - return (!(a == b)); - } - - friend inline bool operator!=(const base_uint& a, uint64 b) - { - return (!(a == b)); - } - - - - std::string GetHex() const - { - char psz[sizeof(pn)*2 + 1]; - for (int i = 0; i < sizeof(pn); i++) - sprintf(psz + i*2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]); - return std::string(psz, psz + sizeof(pn)*2); - } - - void SetHex(const char* psz) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - - // skip leading spaces - while (isspace(*psz)) - psz++; - - // skip 0x - if (psz[0] == '0' && tolower(psz[1]) == 'x') - psz += 2; - - // hex string to uint - static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; - const char* pbegin = psz; - while (phexdigit[*psz] || *psz == '0') - psz++; - psz--; - unsigned char* p1 = (unsigned char*)pn; - unsigned char* pend = p1 + WIDTH * 4; - while (psz >= pbegin && p1 < pend) - { - *p1 = phexdigit[(unsigned char)*psz--]; - if (psz >= pbegin) - { - *p1 |= (phexdigit[(unsigned char)*psz--] << 4); - p1++; - } - } - } - - void SetHex(const std::string& str) - { - SetHex(str.c_str()); - } - - std::string ToString() const - { - return (GetHex()); - } - - unsigned char* begin() - { - return (unsigned char*)&pn[0]; - } - - unsigned char* end() - { - return (unsigned char*)&pn[WIDTH]; - } - - unsigned int size() - { - return sizeof(pn); - } - - - unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const - { - return sizeof(pn); - } - - template - void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const - { - s.write((char*)pn, sizeof(pn)); - } - - template - void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) - { - s.read((char*)pn, sizeof(pn)); - } - - - friend class uint160; - friend class uint256; - friend inline int Testuint256AdHoc(std::vector vArg); -}; - -typedef base_uint<160> base_uint160; -typedef base_uint<256> base_uint256; - - - -// -// uint160 and uint256 could be implemented as templates, but to keep -// compile errors and debugging cleaner, they're copy and pasted. -// - - - -////////////////////////////////////////////////////////////////////////////// -// -// uint160 -// - -class uint160 : public base_uint160 -{ -public: - typedef base_uint160 basetype; - - uint160() - { - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - } - - uint160(const basetype& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = b.pn[i]; - } - - uint160& operator=(const basetype& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = b.pn[i]; - return *this; - } - - uint160(uint64 b) - { - pn[0] = (unsigned int)b; - pn[1] = (unsigned int)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - } - - uint160& operator=(uint64 b) - { - pn[0] = (unsigned int)b; - pn[1] = (unsigned int)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - return *this; - } - - explicit uint160(const std::string& str) - { - SetHex(str); - } - - explicit uint160(const std::vector& vch) - { - if (vch.size() == sizeof(pn)) - memcpy(pn, &vch[0], sizeof(pn)); - else - *this = 0; - } -}; - -inline bool operator==(const uint160& a, uint64 b) { return (base_uint160)a == b; } -inline bool operator!=(const uint160& a, uint64 b) { return (base_uint160)a != b; } -inline const uint160 operator<<(const base_uint160& a, unsigned int shift) { return uint160(a) <<= shift; } -inline const uint160 operator>>(const base_uint160& a, unsigned int shift) { return uint160(a) >>= shift; } -inline const uint160 operator<<(const uint160& a, unsigned int shift) { return uint160(a) <<= shift; } -inline const uint160 operator>>(const uint160& a, unsigned int shift) { return uint160(a) >>= shift; } - -inline const uint160 operator^(const base_uint160& a, const base_uint160& b) { return uint160(a) ^= b; } -inline const uint160 operator&(const base_uint160& a, const base_uint160& b) { return uint160(a) &= b; } -inline const uint160 operator|(const base_uint160& a, const base_uint160& b) { return uint160(a) |= b; } -inline const uint160 operator+(const base_uint160& a, const base_uint160& b) { return uint160(a) += b; } -inline const uint160 operator-(const base_uint160& a, const base_uint160& b) { return uint160(a) -= b; } - -inline bool operator<(const base_uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } -inline bool operator<=(const base_uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } -inline bool operator>(const base_uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } -inline bool operator>=(const base_uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } -inline bool operator==(const base_uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } -inline bool operator!=(const base_uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } -inline const uint160 operator^(const base_uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } -inline const uint160 operator&(const base_uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } -inline const uint160 operator|(const base_uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } -inline const uint160 operator+(const base_uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } -inline const uint160 operator-(const base_uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } - -inline bool operator<(const uint160& a, const base_uint160& b) { return (base_uint160)a < (base_uint160)b; } -inline bool operator<=(const uint160& a, const base_uint160& b) { return (base_uint160)a <= (base_uint160)b; } -inline bool operator>(const uint160& a, const base_uint160& b) { return (base_uint160)a > (base_uint160)b; } -inline bool operator>=(const uint160& a, const base_uint160& b) { return (base_uint160)a >= (base_uint160)b; } -inline bool operator==(const uint160& a, const base_uint160& b) { return (base_uint160)a == (base_uint160)b; } -inline bool operator!=(const uint160& a, const base_uint160& b) { return (base_uint160)a != (base_uint160)b; } -inline const uint160 operator^(const uint160& a, const base_uint160& b) { return (base_uint160)a ^ (base_uint160)b; } -inline const uint160 operator&(const uint160& a, const base_uint160& b) { return (base_uint160)a & (base_uint160)b; } -inline const uint160 operator|(const uint160& a, const base_uint160& b) { return (base_uint160)a | (base_uint160)b; } -inline const uint160 operator+(const uint160& a, const base_uint160& b) { return (base_uint160)a + (base_uint160)b; } -inline const uint160 operator-(const uint160& a, const base_uint160& b) { return (base_uint160)a - (base_uint160)b; } - -inline bool operator<(const uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } -inline bool operator<=(const uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } -inline bool operator>(const uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } -inline bool operator>=(const uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } -inline bool operator==(const uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } -inline bool operator!=(const uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } -inline const uint160 operator^(const uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } -inline const uint160 operator&(const uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } -inline const uint160 operator|(const uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } -inline const uint160 operator+(const uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } -inline const uint160 operator-(const uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// uint256 -// - -class uint256 : public base_uint256 -{ -public: - typedef base_uint256 basetype; - - uint256() - { - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - } - - uint256(const basetype& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = b.pn[i]; - } - - uint256& operator=(const basetype& b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = b.pn[i]; - return *this; - } - - uint256(uint64 b) - { - pn[0] = (unsigned int)b; - pn[1] = (unsigned int)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - } - - uint256& operator=(uint64 b) - { - pn[0] = (unsigned int)b; - pn[1] = (unsigned int)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - return *this; - } - - explicit uint256(const std::string& str) - { - SetHex(str); - } - - explicit uint256(const std::vector& vch) - { - if (vch.size() == sizeof(pn)) - memcpy(pn, &vch[0], sizeof(pn)); - else - *this = 0; - } -}; - -inline bool operator==(const uint256& a, uint64 b) { return (base_uint256)a == b; } -inline bool operator!=(const uint256& a, uint64 b) { return (base_uint256)a != b; } -inline const uint256 operator<<(const base_uint256& a, unsigned int shift) { return uint256(a) <<= shift; } -inline const uint256 operator>>(const base_uint256& a, unsigned int shift) { return uint256(a) >>= shift; } -inline const uint256 operator<<(const uint256& a, unsigned int shift) { return uint256(a) <<= shift; } -inline const uint256 operator>>(const uint256& a, unsigned int shift) { return uint256(a) >>= shift; } - -inline const uint256 operator^(const base_uint256& a, const base_uint256& b) { return uint256(a) ^= b; } -inline const uint256 operator&(const base_uint256& a, const base_uint256& b) { return uint256(a) &= b; } -inline const uint256 operator|(const base_uint256& a, const base_uint256& b) { return uint256(a) |= b; } -inline const uint256 operator+(const base_uint256& a, const base_uint256& b) { return uint256(a) += b; } -inline const uint256 operator-(const base_uint256& a, const base_uint256& b) { return uint256(a) -= b; } - -inline bool operator<(const base_uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } -inline bool operator<=(const base_uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } -inline bool operator>(const base_uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } -inline bool operator>=(const base_uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } -inline bool operator==(const base_uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } -inline bool operator!=(const base_uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } -inline const uint256 operator^(const base_uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } -inline const uint256 operator&(const base_uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } -inline const uint256 operator|(const base_uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } -inline const uint256 operator+(const base_uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } -inline const uint256 operator-(const base_uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } - -inline bool operator<(const uint256& a, const base_uint256& b) { return (base_uint256)a < (base_uint256)b; } -inline bool operator<=(const uint256& a, const base_uint256& b) { return (base_uint256)a <= (base_uint256)b; } -inline bool operator>(const uint256& a, const base_uint256& b) { return (base_uint256)a > (base_uint256)b; } -inline bool operator>=(const uint256& a, const base_uint256& b) { return (base_uint256)a >= (base_uint256)b; } -inline bool operator==(const uint256& a, const base_uint256& b) { return (base_uint256)a == (base_uint256)b; } -inline bool operator!=(const uint256& a, const base_uint256& b) { return (base_uint256)a != (base_uint256)b; } -inline const uint256 operator^(const uint256& a, const base_uint256& b) { return (base_uint256)a ^ (base_uint256)b; } -inline const uint256 operator&(const uint256& a, const base_uint256& b) { return (base_uint256)a & (base_uint256)b; } -inline const uint256 operator|(const uint256& a, const base_uint256& b) { return (base_uint256)a | (base_uint256)b; } -inline const uint256 operator+(const uint256& a, const base_uint256& b) { return (base_uint256)a + (base_uint256)b; } -inline const uint256 operator-(const uint256& a, const base_uint256& b) { return (base_uint256)a - (base_uint256)b; } - -inline bool operator<(const uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } -inline bool operator<=(const uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } -inline bool operator>(const uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } -inline bool operator>=(const uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } -inline bool operator==(const uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } -inline bool operator!=(const uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } -inline const uint256 operator^(const uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } -inline const uint256 operator&(const uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } -inline const uint256 operator|(const uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } -inline const uint256 operator+(const uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } -inline const uint256 operator-(const uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } - - - - - - - - - - - - -inline int Testuint256AdHoc(std::vector vArg) -{ - uint256 g(0); - - - printf("%s\n", g.ToString().c_str()); - g--; printf("g--\n"); - printf("%s\n", g.ToString().c_str()); - g--; printf("g--\n"); - printf("%s\n", g.ToString().c_str()); - g++; printf("g++\n"); - printf("%s\n", g.ToString().c_str()); - g++; printf("g++\n"); - printf("%s\n", g.ToString().c_str()); - g++; printf("g++\n"); - printf("%s\n", g.ToString().c_str()); - g++; printf("g++\n"); - printf("%s\n", g.ToString().c_str()); - - - - uint256 a(7); - printf("a=7\n"); - printf("%s\n", a.ToString().c_str()); - - uint256 b; - printf("b undefined\n"); - printf("%s\n", b.ToString().c_str()); - int c = 3; - - a = c; - a.pn[3] = 15; - printf("%s\n", a.ToString().c_str()); - uint256 k(c); - - a = 5; - a.pn[3] = 15; - printf("%s\n", a.ToString().c_str()); - b = 1; - b <<= 52; - - a |= b; - - a ^= 0x500; - - printf("a %s\n", a.ToString().c_str()); - - a = a | b | (uint256)0x1000; - - - printf("a %s\n", a.ToString().c_str()); - printf("b %s\n", b.ToString().c_str()); - - a = 0xfffffffe; - a.pn[4] = 9; - - printf("%s\n", a.ToString().c_str()); - a++; - printf("%s\n", a.ToString().c_str()); - a++; - printf("%s\n", a.ToString().c_str()); - a++; - printf("%s\n", a.ToString().c_str()); - a++; - printf("%s\n", a.ToString().c_str()); - - a--; - printf("%s\n", a.ToString().c_str()); - a--; - printf("%s\n", a.ToString().c_str()); - a--; - printf("%s\n", a.ToString().c_str()); - uint256 d = a--; - printf("%s\n", d.ToString().c_str()); - printf("%s\n", a.ToString().c_str()); - a--; - printf("%s\n", a.ToString().c_str()); - a--; - printf("%s\n", a.ToString().c_str()); - - d = a; - - printf("%s\n", d.ToString().c_str()); - for (int i = uint256::WIDTH-1; i >= 0; i--) printf("%08x", d.pn[i]); printf("\n"); - - uint256 neg = d; - neg = ~neg; - printf("%s\n", neg.ToString().c_str()); - - - uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111"); - printf("\n"); - printf("%s\n", e.ToString().c_str()); - - - printf("\n"); - uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111"); - uint256 x2; - printf("%s\n", x1.ToString().c_str()); - for (int i = 0; i < 270; i += 4) - { - x2 = x1 << i; - printf("%s\n", x2.ToString().c_str()); - } - - printf("\n"); - printf("%s\n", x1.ToString().c_str()); - for (int i = 0; i < 270; i += 4) - { - x2 = x1; - x2 >>= i; - printf("%s\n", x2.ToString().c_str()); - } - - - for (int i = 0; i < 100; i++) - { - uint256 k = (~uint256(0) >> i); - printf("%s\n", k.ToString().c_str()); - } - - for (int i = 0; i < 100; i++) - { - uint256 k = (~uint256(0) << i); - printf("%s\n", k.ToString().c_str()); - } - - return (0); -} - -#endif diff --git a/core/include/util.h b/core/include/util.h deleted file mode 100644 index 32a98e6264..0000000000 --- a/core/include/util.h +++ /dev/null @@ -1,680 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_UTIL_H -#define BITCOIN_UTIL_H - -#include "uint256.h" - -#ifndef __WXMSW__ -#include -#include -#include -#endif -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - - -#if defined(_MSC_VER) || defined(__BORLANDC__) -typedef __int64 int64; -typedef unsigned __int64 uint64; -#else -typedef long long int64; -typedef unsigned long long uint64; -#endif -#if defined(_MSC_VER) && _MSC_VER < 1300 -#define for if (false) ; else for -#endif -#ifndef _MSC_VER -#define __forceinline inline -#endif - -#define loop for (;;) -#define BEGIN(a) ((char*)&(a)) -#define END(a) ((char*)&((&(a))[1])) -#define UBEGIN(a) ((unsigned char*)&(a)) -#define UEND(a) ((unsigned char*)&((&(a))[1])) -#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) -#define printf OutputDebugStringF - -#ifdef snprintf -#undef snprintf -#endif -#define snprintf my_snprintf - -#ifndef PRI64d -#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MSVCRT__) -#define PRI64d "I64d" -#define PRI64u "I64u" -#define PRI64x "I64x" -#else -#define PRI64d "lld" -#define PRI64u "llu" -#define PRI64x "llx" -#endif -#endif - -// This is needed because the foreach macro can't get over the comma in pair -#define PAIRTYPE(t1, t2) std::pair - -// Used to bypass the rule against non-const reference to temporary -// where it makes sense with wrappers such as CFlatData or CTxDB -template -inline T& REF(const T& val) -{ - return (T&)val; -} - -// Align by increasing pointer, must have extra space at end of buffer -template -T* alignup(T* p) -{ - union - { - T* ptr; - size_t n; - } u; - u.ptr = p; - u.n = (u.n + (nBytes-1)) & ~(nBytes-1); - return u.ptr; -} - -#ifdef __WXMSW__ -#define MSG_NOSIGNAL 0 -#define MSG_DONTWAIT 0 -#ifndef UINT64_MAX -#define UINT64_MAX _UI64_MAX -#define INT64_MAX _I64_MAX -#define INT64_MIN _I64_MIN -#endif -#ifndef S_IRUSR -#define S_IRUSR 0400 -#define S_IWUSR 0200 -#endif -#define unlink _unlink -typedef int socklen_t; -#else -#define WSAGetLastError() errno -#define WSAEWOULDBLOCK EWOULDBLOCK -#define WSAEMSGSIZE EMSGSIZE -#define WSAEINTR EINTR -#define WSAEINPROGRESS EINPROGRESS -#define WSAEADDRINUSE EADDRINUSE -#define WSAENOTSOCK EBADF -#define INVALID_SOCKET (SOCKET)(~0) -#define SOCKET_ERROR -1 -typedef u_int SOCKET; -#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) -#define strlwr(psz) to_lower(psz) -#define _strlwr(psz) to_lower(psz) -#define MAX_PATH 1024 -#define Beep(n1,n2) (0) -inline void Sleep(int64 n) -{ - boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(n)); -} -#endif - -inline int myclosesocket(SOCKET& hSocket) -{ - if (hSocket == INVALID_SOCKET) - return WSAENOTSOCK; -#ifdef __WXMSW__ - int ret = closesocket(hSocket); -#else - int ret = close(hSocket); -#endif - hSocket = INVALID_SOCKET; - return ret; -} -#define closesocket(s) myclosesocket(s) - -#ifndef GUI -inline const char* _(const char* psz) -{ - return psz; -} -#endif - - - - - - - - - - -extern std::map mapArgs; -extern std::map > mapMultiArgs; -extern bool fDebug; -extern bool fPrintToConsole; -extern bool fPrintToDebugger; -extern char pszSetDataDir[MAX_PATH]; -extern bool fRequestShutdown; -extern bool fShutdown; -extern bool fDaemon; -extern bool fServer; -extern bool fCommandLine; -extern std::string strMiscWarning; -extern bool fTestNet; -extern bool fNoListen; -extern bool fLogTimestamps; - -void RandAddSeed(); -void RandAddSeedPerfmon(); -int OutputDebugStringF(const char* pszFormat, ...); -int my_snprintf(char* buffer, size_t limit, const char* format, ...); -std::string strprintf(const char* format, ...); -bool error(const char* format, ...); -void LogException(std::exception* pex, const char* pszThread); -void PrintException(std::exception* pex, const char* pszThread); -void PrintExceptionContinue(std::exception* pex, const char* pszThread); -void ParseString(const std::string& str, char c, std::vector& v); -std::string FormatMoney(int64 n, bool fPlus=false); -bool ParseMoney(const std::string& str, int64& nRet); -bool ParseMoney(const char* pszIn, int64& nRet); -std::vector ParseHex(const char* psz); -std::vector ParseHex(const std::string& str); -void ParseParameters(int argc, char* argv[]); -const char* wxGetTranslation(const char* psz); -bool WildcardMatch(const char* psz, const char* mask); -bool WildcardMatch(const std::string& str, const std::string& mask); -int GetFilesize(FILE* file); -void GetDataDir(char* pszDirRet); -std::string GetConfigFile(); -std::string GetPidFile(); -void CreatePidFile(std::string pidFile, pid_t pid); -void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); -#ifdef __WXMSW__ -std::string MyGetSpecialFolderPath(int nFolder, bool fCreate); -#endif -std::string GetDefaultDataDir(); -std::string GetDataDir(); -void ShrinkDebugFile(); -int GetRandInt(int nMax); -uint64 GetRand(uint64 nMax); -int64 GetTime(); -int64 GetAdjustedTime(); -void AddTimeData(unsigned int ip, int64 nTime); -std::string FormatFullVersion(); - - - - - - - - - - - - - -// Wrapper to automatically initialize critical sections -class CCriticalSection -{ -#ifdef __WXMSW__ -protected: - CRITICAL_SECTION cs; -public: - explicit CCriticalSection() { InitializeCriticalSection(&cs); } - ~CCriticalSection() { DeleteCriticalSection(&cs); } - void Enter() { EnterCriticalSection(&cs); } - void Leave() { LeaveCriticalSection(&cs); } - bool TryEnter() { return TryEnterCriticalSection(&cs); } -#else -protected: - boost::interprocess::interprocess_recursive_mutex mutex; -public: - explicit CCriticalSection() { } - ~CCriticalSection() { } - void Enter() { mutex.lock(); } - void Leave() { mutex.unlock(); } - bool TryEnter() { return mutex.try_lock(); } -#endif -public: - const char* pszFile; - int nLine; -}; - -// Automatically leave critical section when leaving block, needed for exception safety -class CCriticalBlock -{ -protected: - CCriticalSection* pcs; -public: - CCriticalBlock(CCriticalSection& csIn) { pcs = &csIn; pcs->Enter(); } - ~CCriticalBlock() { pcs->Leave(); } -}; - -// WARNING: This will catch continue and break! -// break is caught with an assertion, but there's no way to detect continue. -// I'd rather be careful than suffer the other more error prone syntax. -// The compiler will optimise away all this loop junk. -#define CRITICAL_BLOCK(cs) \ - for (bool fcriticalblockonce=true; fcriticalblockonce; assert(("break caught by CRITICAL_BLOCK!", !fcriticalblockonce)), fcriticalblockonce=false) \ - for (CCriticalBlock criticalblock(cs); fcriticalblockonce && (cs.pszFile=__FILE__, cs.nLine=__LINE__, true); fcriticalblockonce=false, cs.pszFile=NULL, cs.nLine=0) - -class CTryCriticalBlock -{ -protected: - CCriticalSection* pcs; -public: - CTryCriticalBlock(CCriticalSection& csIn) { pcs = (csIn.TryEnter() ? &csIn : NULL); } - ~CTryCriticalBlock() { if (pcs) pcs->Leave(); } - bool Entered() { return pcs != NULL; } -}; - -#define TRY_CRITICAL_BLOCK(cs) \ - for (bool fcriticalblockonce=true; fcriticalblockonce; assert(("break caught by TRY_CRITICAL_BLOCK!", !fcriticalblockonce)), fcriticalblockonce=false) \ - for (CTryCriticalBlock criticalblock(cs); fcriticalblockonce && (fcriticalblockonce = criticalblock.Entered()) && (cs.pszFile=__FILE__, cs.nLine=__LINE__, true); fcriticalblockonce=false, cs.pszFile=NULL, cs.nLine=0) - - - - - - - - - - -inline std::string i64tostr(int64 n) -{ - return strprintf("%"PRI64d, n); -} - -inline std::string itostr(int n) -{ - return strprintf("%d", n); -} - -inline int64 atoi64(const char* psz) -{ -#ifdef _MSC_VER - return _atoi64(psz); -#else - return strtoll(psz, NULL, 10); -#endif -} - -inline int64 atoi64(const std::string& str) -{ -#ifdef _MSC_VER - return _atoi64(str.c_str()); -#else - return strtoll(str.c_str(), NULL, 10); -#endif -} - -inline int atoi(const std::string& str) -{ - return atoi(str.c_str()); -} - -inline int roundint(double d) -{ - return (int)(d > 0 ? d + 0.5 : d - 0.5); -} - -inline int64 roundint64(double d) -{ - return (int64)(d > 0 ? d + 0.5 : d - 0.5); -} - -inline int64 abs64(int64 n) -{ - return (n >= 0 ? n : -n); -} - -template -std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) -{ - if (itbegin == itend) - return ""; - const unsigned char* pbegin = (const unsigned char*)&itbegin[0]; - const unsigned char* pend = pbegin + (itend - itbegin) * sizeof(itbegin[0]); - std::string str; - str.reserve((pend-pbegin) * (fSpaces ? 3 : 2)); - for (const unsigned char* p = pbegin; p != pend; p++) - str += strprintf((fSpaces && p != pend-1 ? "%02x " : "%02x"), *p); - return str; -} - -inline std::string HexStr(const std::vector& vch, bool fSpaces=false) -{ - return HexStr(vch.begin(), vch.end(), fSpaces); -} - -template -std::string HexNumStr(const T itbegin, const T itend, bool f0x=true) -{ - if (itbegin == itend) - return ""; - const unsigned char* pbegin = (const unsigned char*)&itbegin[0]; - const unsigned char* pend = pbegin + (itend - itbegin) * sizeof(itbegin[0]); - std::string str = (f0x ? "0x" : ""); - str.reserve(str.size() + (pend-pbegin) * 2); - for (const unsigned char* p = pend-1; p >= pbegin; p--) - str += strprintf("%02x", *p); - return str; -} - -inline std::string HexNumStr(const std::vector& vch, bool f0x=true) -{ - return HexNumStr(vch.begin(), vch.end(), f0x); -} - -template -void PrintHex(const T pbegin, const T pend, const char* pszFormat="%s", bool fSpaces=true) -{ - printf(pszFormat, HexStr(pbegin, pend, fSpaces).c_str()); -} - -inline void PrintHex(const std::vector& vch, const char* pszFormat="%s", bool fSpaces=true) -{ - printf(pszFormat, HexStr(vch, fSpaces).c_str()); -} - -inline int64 GetPerformanceCounter() -{ - int64 nCounter = 0; -#ifdef __WXMSW__ - QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); -#else - timeval t; - gettimeofday(&t, NULL); - nCounter = t.tv_sec * 1000000 + t.tv_usec; -#endif - return nCounter; -} - -inline int64 GetTimeMillis() -{ - return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - - boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); -} - -inline std::string DateTimeStrFormat(const char* pszFormat, int64 nTime) -{ - time_t n = nTime; - struct tm* ptmTime = gmtime(&n); - char pszTime[200]; - strftime(pszTime, sizeof(pszTime), pszFormat, ptmTime); - return pszTime; -} - -template -void skipspaces(T& it) -{ - while (isspace(*it)) - ++it; -} - -inline bool IsSwitchChar(char c) -{ -#ifdef __WXMSW__ - return c == '-' || c == '/'; -#else - return c == '-'; -#endif -} - -inline std::string GetArg(const std::string& strArg, const std::string& strDefault) -{ - if (mapArgs.count(strArg)) - return mapArgs[strArg]; - return strDefault; -} - -inline int64 GetArg(const std::string& strArg, int64 nDefault) -{ - if (mapArgs.count(strArg)) - return atoi64(mapArgs[strArg]); - return nDefault; -} - -inline bool GetBoolArg(const std::string& strArg) -{ - if (mapArgs.count(strArg)) - { - if (mapArgs[strArg].empty()) - return true; - return (atoi(mapArgs[strArg]) != 0); - } - return false; -} - - - - - - - - - - -inline void heapchk() -{ -#ifdef __WXMSW__ - /// for debugging - //if (_heapchk() != _HEAPOK) - // DebugBreak(); -#endif -} - -// Randomize the stack to help protect against buffer overrun exploits -#define IMPLEMENT_RANDOMIZE_STACK(ThreadFn) \ - { \ - static char nLoops; \ - if (nLoops <= 0) \ - nLoops = GetRand(20) + 1; \ - if (nLoops-- > 1) \ - { \ - ThreadFn; \ - return; \ - } \ - } - -#define CATCH_PRINT_EXCEPTION(pszFn) \ - catch (std::exception& e) { \ - PrintException(&e, (pszFn)); \ - } catch (...) { \ - PrintException(NULL, (pszFn)); \ - } - - - - - - - - - - -template -inline uint256 Hash(const T1 pbegin, const T1 pend) -{ - static unsigned char pblank[1]; - uint256 hash1; - SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1); - uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; -} - -template -inline uint256 Hash(const T1 p1begin, const T1 p1end, - const T2 p2begin, const T2 p2end) -{ - static unsigned char pblank[1]; - uint256 hash1; - SHA256_CTX ctx; - SHA256_Init(&ctx); - SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); - SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); - SHA256_Final((unsigned char*)&hash1, &ctx); - uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; -} - -template -inline uint256 Hash(const T1 p1begin, const T1 p1end, - const T2 p2begin, const T2 p2end, - const T3 p3begin, const T3 p3end) -{ - static unsigned char pblank[1]; - uint256 hash1; - SHA256_CTX ctx; - SHA256_Init(&ctx); - SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); - SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); - SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0])); - SHA256_Final((unsigned char*)&hash1, &ctx); - uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; -} - -template -uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=VERSION) -{ - // Most of the time is spent allocating and deallocating CDataStream's - // buffer. If this ever needs to be optimized further, make a CStaticStream - // class with its buffer on the stack. - CDataStream ss(nType, nVersion); - ss.reserve(10000); - ss << obj; - return Hash(ss.begin(), ss.end()); -} - -inline uint160 Hash160(const std::vector& vch) -{ - uint256 hash1; - SHA256(&vch[0], vch.size(), (unsigned char*)&hash1); - uint160 hash2; - RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; -} - - - - - - - - - - - -// Note: It turns out we might have been able to use boost::thread -// by using TerminateThread(boost::thread.native_handle(), 0); -#ifdef __WXMSW__ -typedef HANDLE pthread_t; - -inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) -{ - DWORD nUnused = 0; - HANDLE hthread = - CreateThread( - NULL, // default security - 0, // inherit stack size from parent - (LPTHREAD_START_ROUTINE)pfn, // function pointer - parg, // argument - 0, // creation option, start immediately - &nUnused); // thread identifier - if (hthread == NULL) - { - printf("Error: CreateThread() returned %d\n", GetLastError()); - return (pthread_t)0; - } - if (!fWantHandle) - { - CloseHandle(hthread); - return (pthread_t)-1; - } - return hthread; -} - -inline void SetThreadPriority(int nPriority) -{ - SetThreadPriority(GetCurrentThread(), nPriority); -} -#else -inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) -{ - pthread_t hthread = 0; - int ret = pthread_create(&hthread, NULL, (void*(*)(void*))pfn, parg); - if (ret != 0) - { - printf("Error: pthread_create() returned %d\n", ret); - return (pthread_t)0; - } - if (!fWantHandle) - return (pthread_t)-1; - return hthread; -} - -#define THREAD_PRIORITY_LOWEST PRIO_MAX -#define THREAD_PRIORITY_BELOW_NORMAL 2 -#define THREAD_PRIORITY_NORMAL 0 -#define THREAD_PRIORITY_ABOVE_NORMAL 0 - -inline void SetThreadPriority(int nPriority) -{ - // It's unclear if it's even possible to change thread priorities on Linux, - // but we really and truly need it for the generation threads. -#ifdef PRIO_THREAD - setpriority(PRIO_THREAD, 0, nPriority); -#else - setpriority(PRIO_PROCESS, 0, nPriority); -#endif -} - -inline bool TerminateThread(pthread_t hthread, unsigned int nExitCode) -{ - return (pthread_cancel(hthread) == 0); -} - -inline void ExitThread(unsigned int nExitCode) -{ - pthread_exit((void*)nExitCode); -} -#endif - - - - - -inline bool AffinityBugWorkaround(void(*pfn)(void*)) -{ -#ifdef __WXMSW__ - // Sometimes after a few hours affinity gets stuck on one processor - DWORD dwProcessAffinityMask = -1; - DWORD dwSystemAffinityMask = -1; - GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask); - DWORD dwPrev1 = SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinityMask); - DWORD dwPrev2 = SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinityMask); - if (dwPrev2 != dwProcessAffinityMask) - { - printf("AffinityBugWorkaround() : SetThreadAffinityMask=%d, ProcessAffinityMask=%d, restarting thread\n", dwPrev2, dwProcessAffinityMask); - if (!CreateThread(pfn, NULL)) - printf("Error: CreateThread() failed\n"); - return true; - } -#endif - return false; -} - -#endif diff --git a/core/src/db.cpp b/core/src/db.cpp deleted file mode 100644 index 52c0f5b4c3..0000000000 --- a/core/src/db.cpp +++ /dev/null @@ -1,1026 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. - -#include "headers.h" - -using namespace std; -using namespace boost; - -void ThreadFlushWalletDB(void* parg); - - -unsigned int nWalletDBUpdated; -uint64 nAccountingEntryNumber = 0; - - - -// -// CDB -// - -static CCriticalSection cs_db; -static bool fDbEnvInit = false; -DbEnv dbenv(0); -static map mapFileUseCount; -static map mapDb; - -class CDBInit -{ -public: - CDBInit() - { - } - ~CDBInit() - { - if (fDbEnvInit) - { - dbenv.close(0); - fDbEnvInit = false; - } - } -} -instance_of_cdbinit; - - -CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) -{ - int ret; - if (pszFile == NULL) - return; - - fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); - bool fCreate = strchr(pszMode, 'c'); - unsigned int nFlags = DB_THREAD; - if (fCreate) - nFlags |= DB_CREATE; - - CRITICAL_BLOCK(cs_db) - { - if (!fDbEnvInit) - { - if (fShutdown) - return; - string strDataDir = GetDataDir(); - string strLogDir = strDataDir + "/database"; - filesystem::create_directory(strLogDir.c_str()); - string strErrorFile = strDataDir + "/db.log"; - printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str()); - - dbenv.set_lg_dir(strLogDir.c_str()); - dbenv.set_lg_max(10000000); - dbenv.set_lk_max_locks(10000); - dbenv.set_lk_max_objects(10000); - dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug - dbenv.set_flags(DB_AUTO_COMMIT, 1); - ret = dbenv.open(strDataDir.c_str(), - DB_CREATE | - DB_INIT_LOCK | - DB_INIT_LOG | - DB_INIT_MPOOL | - DB_INIT_TXN | - DB_THREAD | - DB_RECOVER, - S_IRUSR | S_IWUSR); - if (ret > 0) - throw runtime_error(strprintf("CDB() : error %d opening database environment", ret)); - fDbEnvInit = true; - } - - strFile = pszFile; - ++mapFileUseCount[strFile]; - pdb = mapDb[strFile]; - if (pdb == NULL) - { - pdb = new Db(&dbenv, 0); - - ret = pdb->open(NULL, // Txn pointer - pszFile, // Filename - "main", // Logical db name - DB_BTREE, // Database type - nFlags, // Flags - 0); - - if (ret > 0) - { - delete pdb; - pdb = NULL; - CRITICAL_BLOCK(cs_db) - --mapFileUseCount[strFile]; - strFile = ""; - throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret)); - } - - if (fCreate && !Exists(string("version"))) - { - bool fTmp = fReadOnly; - fReadOnly = false; - WriteVersion(VERSION); - fReadOnly = fTmp; - } - - mapDb[strFile] = pdb; - } - } -} - -void CDB::Close() -{ - if (!pdb) - return; - if (!vTxn.empty()) - vTxn.front()->abort(); - vTxn.clear(); - pdb = NULL; - - // Flush database activity from memory pool to disk log - unsigned int nMinutes = 0; - if (fReadOnly) - nMinutes = 1; - if (strFile == "addr.dat") - nMinutes = 2; - if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 500 != 0) - nMinutes = 1; - dbenv.txn_checkpoint(0, nMinutes, 0); - - CRITICAL_BLOCK(cs_db) - --mapFileUseCount[strFile]; -} - -void CloseDb(const string& strFile) -{ - CRITICAL_BLOCK(cs_db) - { - if (mapDb[strFile] != NULL) - { - // Close the database handle - Db* pdb = mapDb[strFile]; - pdb->close(0); - delete pdb; - mapDb[strFile] = NULL; - } - } -} - -void DBFlush(bool fShutdown) -{ - // Flush log data to the actual data file - // on all files that are not in use - printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started"); - if (!fDbEnvInit) - return; - CRITICAL_BLOCK(cs_db) - { - map::iterator mi = mapFileUseCount.begin(); - while (mi != mapFileUseCount.end()) - { - string strFile = (*mi).first; - int nRefCount = (*mi).second; - printf("%s refcount=%d\n", strFile.c_str(), nRefCount); - if (nRefCount == 0) - { - // Move log data to the dat file - CloseDb(strFile); - dbenv.txn_checkpoint(0, 0, 0); - printf("%s flush\n", strFile.c_str()); - dbenv.lsn_reset(strFile.c_str(), 0); - mapFileUseCount.erase(mi++); - } - else - mi++; - } - if (fShutdown) - { - char** listp; - if (mapFileUseCount.empty()) - dbenv.log_archive(&listp, DB_ARCH_REMOVE); - dbenv.close(0); - fDbEnvInit = false; - } - } -} - - - - - - -// -// CTxDB -// - -bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex) -{ - assert(!fClient); - txindex.SetNull(); - return Read(make_pair(string("tx"), hash), txindex); -} - -bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex) -{ - assert(!fClient); - return Write(make_pair(string("tx"), hash), txindex); -} - -bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight) -{ - assert(!fClient); - - // Add to tx index - uint256 hash = tx.GetHash(); - CTxIndex txindex(pos, tx.vout.size()); - return Write(make_pair(string("tx"), hash), txindex); -} - -bool CTxDB::EraseTxIndex(const CTransaction& tx) -{ - assert(!fClient); - uint256 hash = tx.GetHash(); - - return Erase(make_pair(string("tx"), hash)); -} - -bool CTxDB::ContainsTx(uint256 hash) -{ - assert(!fClient); - return Exists(make_pair(string("tx"), hash)); -} - -bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector& vtx) -{ - assert(!fClient); - vtx.clear(); - - // Get cursor - Dbc* pcursor = GetCursor(); - if (!pcursor) - return false; - - unsigned int fFlags = DB_SET_RANGE; - loop - { - // Read next record - CDataStream ssKey; - if (fFlags == DB_SET_RANGE) - ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0); - CDataStream ssValue; - int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); - fFlags = DB_NEXT; - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - { - pcursor->close(); - return false; - } - - // Unserialize - string strType; - uint160 hashItem; - CDiskTxPos pos; - ssKey >> strType >> hashItem >> pos; - int nItemHeight; - ssValue >> nItemHeight; - - // Read transaction - if (strType != "owner" || hashItem != hash160) - break; - if (nItemHeight >= nMinHeight) - { - vtx.resize(vtx.size()+1); - if (!vtx.back().ReadFromDisk(pos)) - { - pcursor->close(); - return false; - } - } - } - - pcursor->close(); - return true; -} - -bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex) -{ - assert(!fClient); - tx.SetNull(); - if (!ReadTxIndex(hash, txindex)) - return false; - return (tx.ReadFromDisk(txindex.pos)); -} - -bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx) -{ - CTxIndex txindex; - return ReadDiskTx(hash, tx, txindex); -} - -bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex) -{ - return ReadDiskTx(outpoint.hash, tx, txindex); -} - -bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx) -{ - CTxIndex txindex; - return ReadDiskTx(outpoint.hash, tx, txindex); -} - -bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) -{ - return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex); -} - -bool CTxDB::EraseBlockIndex(uint256 hash) -{ - return Erase(make_pair(string("blockindex"), hash)); -} - -bool CTxDB::ReadHashBestChain(uint256& hashBestChain) -{ - return Read(string("hashBestChain"), hashBestChain); -} - -bool CTxDB::WriteHashBestChain(uint256 hashBestChain) -{ - return Write(string("hashBestChain"), hashBestChain); -} - -bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork) -{ - return Read(string("bnBestInvalidWork"), bnBestInvalidWork); -} - -bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork) -{ - return Write(string("bnBestInvalidWork"), bnBestInvalidWork); -} - -CBlockIndex* InsertBlockIndex(uint256 hash) -{ - if (hash == 0) - return NULL; - - // Return existing - map::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - return (*mi).second; - - // Create new - CBlockIndex* pindexNew = new CBlockIndex(); - if (!pindexNew) - throw runtime_error("LoadBlockIndex() : new CBlockIndex failed"); - mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; - pindexNew->phashBlock = &((*mi).first); - - return pindexNew; -} - -bool CTxDB::LoadBlockIndex() -{ - // Get database cursor - Dbc* pcursor = GetCursor(); - if (!pcursor) - return false; - - // Load mapBlockIndex - unsigned int fFlags = DB_SET_RANGE; - loop - { - // Read next record - CDataStream ssKey; - if (fFlags == DB_SET_RANGE) - ssKey << make_pair(string("blockindex"), uint256(0)); - CDataStream ssValue; - int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); - fFlags = DB_NEXT; - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - return false; - - // Unserialize - string strType; - ssKey >> strType; - if (strType == "blockindex") - { - CDiskBlockIndex diskindex; - ssValue >> diskindex; - - // Construct block index object - CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); - pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); - pindexNew->pnext = InsertBlockIndex(diskindex.hashNext); - pindexNew->nFile = diskindex.nFile; - pindexNew->nBlockPos = diskindex.nBlockPos; - pindexNew->nHeight = diskindex.nHeight; - pindexNew->nVersion = diskindex.nVersion; - pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; - pindexNew->nTime = diskindex.nTime; - pindexNew->nBits = diskindex.nBits; - pindexNew->nNonce = diskindex.nNonce; - - // Watch for genesis block - if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock) - pindexGenesisBlock = pindexNew; - - if (!pindexNew->CheckIndex()) - return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight); - } - else - { - break; - } - } - pcursor->close(); - - // Calculate bnChainWork - vector > vSortedByHeight; - vSortedByHeight.reserve(mapBlockIndex.size()); - BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) - { - CBlockIndex* pindex = item.second; - vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); - } - sort(vSortedByHeight.begin(), vSortedByHeight.end()); - BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) - { - CBlockIndex* pindex = item.second; - pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork(); - } - - // Load hashBestChain pointer to end of best chain - if (!ReadHashBestChain(hashBestChain)) - { - if (pindexGenesisBlock == NULL) - return true; - return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded"); - } - if (!mapBlockIndex.count(hashBestChain)) - return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index"); - pindexBest = mapBlockIndex[hashBestChain]; - nBestHeight = pindexBest->nHeight; - bnBestChainWork = pindexBest->bnChainWork; - printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight); - - // Load bnBestInvalidWork, OK if it doesn't exist - ReadBestInvalidWork(bnBestInvalidWork); - - // Verify blocks in the best chain - CBlockIndex* pindexFork = NULL; - for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) - { - if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks")) - break; - CBlock block; - if (!block.ReadFromDisk(pindex)) - return error("LoadBlockIndex() : block.ReadFromDisk failed"); - if (!block.CheckBlock()) - { - printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - pindexFork = pindex->pprev; - } - } - if (pindexFork) - { - // Reorg back to the fork - printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight); - CBlock block; - if (!block.ReadFromDisk(pindexFork)) - return error("LoadBlockIndex() : block.ReadFromDisk failed"); - CTxDB txdb; - block.SetBestChain(txdb, pindexFork); - } - - return true; -} - - - - - -// -// CAddrDB -// - -bool CAddrDB::WriteAddress(const CAddress& addr) -{ - return Write(make_pair(string("addr"), addr.GetKey()), addr); -} - -bool CAddrDB::EraseAddress(const CAddress& addr) -{ - return Erase(make_pair(string("addr"), addr.GetKey())); -} - -bool CAddrDB::LoadAddresses() -{ - CRITICAL_BLOCK(cs_mapAddresses) - { - // Load user provided addresses - CAutoFile filein = fopen((GetDataDir() + "/addr.txt").c_str(), "rt"); - if (filein) - { - try - { - char psz[1000]; - while (fgets(psz, sizeof(psz), filein)) - { - CAddress addr(psz, NODE_NETWORK); - addr.nTime = 0; // so it won't relay unless successfully connected - if (addr.IsValid()) - AddAddress(addr); - } - } - catch (...) { } - } - - // Get cursor - Dbc* pcursor = GetCursor(); - if (!pcursor) - return false; - - loop - { - // Read next record - CDataStream ssKey; - CDataStream ssValue; - int ret = ReadAtCursor(pcursor, ssKey, ssValue); - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - return false; - - // Unserialize - string strType; - ssKey >> strType; - if (strType == "addr") - { - CAddress addr; - ssValue >> addr; - mapAddresses.insert(make_pair(addr.GetKey(), addr)); - } - } - pcursor->close(); - - printf("Loaded %d addresses\n", mapAddresses.size()); - } - - return true; -} - -bool LoadAddresses() -{ - return CAddrDB("cr+").LoadAddresses(); -} - - - - -// -// CWalletDB -// - -static set setKeyPool; -static CCriticalSection cs_setKeyPool; - -bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) -{ - account.SetNull(); - return Read(make_pair(string("acc"), strAccount), account); -} - -bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) -{ - return Write(make_pair(string("acc"), strAccount), account); -} - -bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) -{ - return Write(make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry); -} - -int64 CWalletDB::GetAccountCreditDebit(const string& strAccount) -{ - list entries; - ListAccountCreditDebit(strAccount, entries); - - int64 nCreditDebit = 0; - BOOST_FOREACH (const CAccountingEntry& entry, entries) - nCreditDebit += entry.nCreditDebit; - - return nCreditDebit; -} - -void CWalletDB::ListAccountCreditDebit(const string& strAccount, list& entries) -{ - int64 nCreditDebit = 0; - - bool fAllAccounts = (strAccount == "*"); - - Dbc* pcursor = GetCursor(); - if (!pcursor) - throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor"); - unsigned int fFlags = DB_SET_RANGE; - loop - { - // Read next record - CDataStream ssKey; - if (fFlags == DB_SET_RANGE) - ssKey << make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0)); - CDataStream ssValue; - int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); - fFlags = DB_NEXT; - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - { - pcursor->close(); - throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB"); - } - - // Unserialize - string strType; - ssKey >> strType; - if (strType != "acentry") - break; - CAccountingEntry acentry; - ssKey >> acentry.strAccount; - if (!fAllAccounts && acentry.strAccount != strAccount) - break; - - ssValue >> acentry; - entries.push_back(acentry); - } - - pcursor->close(); -} - - -bool CWalletDB::LoadWallet() -{ - vchDefaultKey.clear(); - int nFileVersion = 0; - vector vWalletUpgrade; - - // Modify defaults -#ifndef __WXMSW__ - // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program - fMinimizeToTray = false; - fMinimizeOnClose = false; -#endif - - //// todo: shouldn't we catch exceptions and try to recover and continue? - CRITICAL_BLOCK(cs_mapWallet) - CRITICAL_BLOCK(cs_mapKeys) - { - // Get cursor - Dbc* pcursor = GetCursor(); - if (!pcursor) - return false; - - loop - { - // Read next record - CDataStream ssKey; - CDataStream ssValue; - int ret = ReadAtCursor(pcursor, ssKey, ssValue); - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - return false; - - // Unserialize - // Taking advantage of the fact that pair serialization - // is just the two items serialized one after the other - string strType; - ssKey >> strType; - if (strType == "name") - { - string strAddress; - ssKey >> strAddress; - ssValue >> mapAddressBook[strAddress]; - } - else if (strType == "tx") - { - uint256 hash; - ssKey >> hash; - CWalletTx& wtx = mapWallet[hash]; - ssValue >> wtx; - - if (wtx.GetHash() != hash) - printf("Error in wallet.dat, hash mismatch\n"); - - // Undo serialize changes in 31600 - if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) - { - if (!ssValue.empty()) - { - char fTmp; - char fUnused; - ssValue >> fTmp >> fUnused >> wtx.strFromAccount; - printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str()); - wtx.fTimeReceivedIsTxTime = fTmp; - } - else - { - printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str()); - wtx.fTimeReceivedIsTxTime = 0; - } - vWalletUpgrade.push_back(hash); - } - - //// debug print - //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); - //printf(" %12I64d %s %s %s\n", - // wtx.vout[0].nValue, - // DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(), - // wtx.hashBlock.ToString().substr(0,20).c_str(), - // wtx.mapValue["message"].c_str()); - } - else if (strType == "acentry") - { - string strAccount; - ssKey >> strAccount; - uint64 nNumber; - ssKey >> nNumber; - if (nNumber > nAccountingEntryNumber) - nAccountingEntryNumber = nNumber; - } - else if (strType == "key" || strType == "wkey") - { - vector vchPubKey; - ssKey >> vchPubKey; - CWalletKey wkey; - if (strType == "key") - ssValue >> wkey.vchPrivKey; - else - ssValue >> wkey; - - mapKeys[vchPubKey] = wkey.vchPrivKey; - mapPubKeys[Hash160(vchPubKey)] = vchPubKey; - } - else if (strType == "defaultkey") - { - ssValue >> vchDefaultKey; - } - else if (strType == "pool") - { - int64 nIndex; - ssKey >> nIndex; - setKeyPool.insert(nIndex); - } - else if (strType == "version") - { - ssValue >> nFileVersion; - if (nFileVersion == 10300) - nFileVersion = 300; - } - else if (strType == "setting") - { - string strKey; - ssKey >> strKey; - - // Options -#ifndef GUI - if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins; -#endif - if (strKey == "nTransactionFee") ssValue >> nTransactionFee; - if (strKey == "addrIncoming") ssValue >> addrIncoming; - if (strKey == "fLimitProcessors") ssValue >> fLimitProcessors; - if (strKey == "nLimitProcessors") ssValue >> nLimitProcessors; - if (strKey == "fMinimizeToTray") ssValue >> fMinimizeToTray; - if (strKey == "fMinimizeOnClose") ssValue >> fMinimizeOnClose; - if (strKey == "fUseProxy") ssValue >> fUseProxy; - if (strKey == "addrProxy") ssValue >> addrProxy; - if (fHaveUPnP && strKey == "fUseUPnP") ssValue >> fUseUPnP; - } - } - pcursor->close(); - } - - BOOST_FOREACH(uint256 hash, vWalletUpgrade) - WriteTx(hash, mapWallet[hash]); - - printf("nFileVersion = %d\n", nFileVersion); - printf("fGenerateBitcoins = %d\n", fGenerateBitcoins); - printf("nTransactionFee = %"PRI64d"\n", nTransactionFee); - printf("addrIncoming = %s\n", addrIncoming.ToString().c_str()); - printf("fMinimizeToTray = %d\n", fMinimizeToTray); - printf("fMinimizeOnClose = %d\n", fMinimizeOnClose); - printf("fUseProxy = %d\n", fUseProxy); - printf("addrProxy = %s\n", addrProxy.ToString().c_str()); - if (fHaveUPnP) - printf("fUseUPnP = %d\n", fUseUPnP); - - - // Upgrade - if (nFileVersion < VERSION) - { - // Get rid of old debug.log file in current directory - if (nFileVersion <= 105 && !pszSetDataDir[0]) - unlink("debug.log"); - - WriteVersion(VERSION); - } - - - return true; -} - -bool LoadWallet(bool& fFirstRunRet) -{ - fFirstRunRet = false; - if (!CWalletDB("cr+").LoadWallet()) - return false; - fFirstRunRet = vchDefaultKey.empty(); - - if (mapKeys.count(vchDefaultKey)) - { - // Set keyUser - keyUser.SetPubKey(vchDefaultKey); - keyUser.SetPrivKey(mapKeys[vchDefaultKey]); - } - else - { - // Create new keyUser and set as default key - RandAddSeedPerfmon(); - keyUser.MakeNewKey(); - if (!AddKey(keyUser)) - return false; - if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "")) - return false; - CWalletDB().WriteDefaultKey(keyUser.GetPubKey()); - } - - CreateThread(ThreadFlushWalletDB, NULL); - return true; -} - -void ThreadFlushWalletDB(void* parg) -{ - static bool fOneThread; - if (fOneThread) - return; - fOneThread = true; - if (mapArgs.count("-noflushwallet")) - return; - - unsigned int nLastSeen = nWalletDBUpdated; - unsigned int nLastFlushed = nWalletDBUpdated; - int64 nLastWalletUpdate = GetTime(); - while (!fShutdown) - { - Sleep(500); - - if (nLastSeen != nWalletDBUpdated) - { - nLastSeen = nWalletDBUpdated; - nLastWalletUpdate = GetTime(); - } - - if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) - { - TRY_CRITICAL_BLOCK(cs_db) - { - // Don't do this if any databases are in use - int nRefCount = 0; - map::iterator mi = mapFileUseCount.begin(); - while (mi != mapFileUseCount.end()) - { - nRefCount += (*mi).second; - mi++; - } - - if (nRefCount == 0 && !fShutdown) - { - string strFile = "wallet.dat"; - map::iterator mi = mapFileUseCount.find(strFile); - if (mi != mapFileUseCount.end()) - { - printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); - printf("Flushing wallet.dat\n"); - nLastFlushed = nWalletDBUpdated; - int64 nStart = GetTimeMillis(); - - // Flush wallet.dat so it's self contained - CloseDb(strFile); - dbenv.txn_checkpoint(0, 0, 0); - dbenv.lsn_reset(strFile.c_str(), 0); - - mapFileUseCount.erase(mi++); - printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart); - } - } - } - } - } -} - -void BackupWallet(const string& strDest) -{ - while (!fShutdown) - { - CRITICAL_BLOCK(cs_db) - { - const string strFile = "wallet.dat"; - if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0) - { - // Flush log data to the dat file - CloseDb(strFile); - dbenv.txn_checkpoint(0, 0, 0); - dbenv.lsn_reset(strFile.c_str(), 0); - mapFileUseCount.erase(strFile); - - // Copy wallet.dat - filesystem::path pathSrc(GetDataDir() + "/" + strFile); - filesystem::path pathDest(strDest); - if (filesystem::is_directory(pathDest)) - pathDest = pathDest / strFile; -#if BOOST_VERSION >= 104000 - filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists); -#else - filesystem::copy_file(pathSrc, pathDest); -#endif - printf("copied wallet.dat to %s\n", pathDest.string().c_str()); - - return; - } - } - Sleep(100); - } -} - - -void CWalletDB::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) -{ - nIndex = -1; - keypool.vchPubKey.clear(); - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - CRITICAL_BLOCK(cs_setKeyPool) - { - // Top up key pool - int64 nTargetSize = max(GetArg("-keypool", 100), (int64)0); - while (setKeyPool.size() < nTargetSize+1) - { - int64 nEnd = 1; - if (!setKeyPool.empty()) - nEnd = *(--setKeyPool.end()) + 1; - if (!Write(make_pair(string("pool"), nEnd), CKeyPool(GenerateNewKey()))) - throw runtime_error("ReserveKeyFromKeyPool() : writing generated key failed"); - setKeyPool.insert(nEnd); - printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size()); - } - - // Get the oldest key - assert(!setKeyPool.empty()); - nIndex = *(setKeyPool.begin()); - setKeyPool.erase(setKeyPool.begin()); - if (!Read(make_pair(string("pool"), nIndex), keypool)) - throw runtime_error("ReserveKeyFromKeyPool() : read failed"); - if (!mapKeys.count(keypool.vchPubKey)) - throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); - assert(!keypool.vchPubKey.empty()); - printf("keypool reserve %"PRI64d"\n", nIndex); - } -} - -void CWalletDB::KeepKey(int64 nIndex) -{ - // Remove from key pool - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - { - Erase(make_pair(string("pool"), nIndex)); - } - printf("keypool keep %"PRI64d"\n", nIndex); -} - -void CWalletDB::ReturnKey(int64 nIndex) -{ - // Return to key pool - CRITICAL_BLOCK(cs_setKeyPool) - setKeyPool.insert(nIndex); - printf("keypool return %"PRI64d"\n", nIndex); -} - -vector GetKeyFromKeyPool() -{ - CWalletDB walletdb; - int64 nIndex = 0; - CKeyPool keypool; - walletdb.ReserveKeyFromKeyPool(nIndex, keypool); - walletdb.KeepKey(nIndex); - return keypool.vchPubKey; -} - -int64 GetOldestKeyPoolTime() -{ - CWalletDB walletdb; - int64 nIndex = 0; - CKeyPool keypool; - walletdb.ReserveKeyFromKeyPool(nIndex, keypool); - walletdb.ReturnKey(nIndex); - return keypool.nTime; -} diff --git a/core/src/init.cpp b/core/src/init.cpp deleted file mode 100644 index f7a1ce9ef1..0000000000 --- a/core/src/init.cpp +++ /dev/null @@ -1,525 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#include "headers.h" - -using namespace std; -using namespace boost; - -////////////////////////////////////////////////////////////////////////////// -// -// Shutdown -// - -void ExitTimeout(void* parg) -{ -#ifdef __WXMSW__ - Sleep(5000); - ExitProcess(0); -#endif -} - -void Shutdown(void* parg) -{ - static CCriticalSection cs_Shutdown; - static bool fTaken; - bool fFirstThread; - CRITICAL_BLOCK(cs_Shutdown) - { - fFirstThread = !fTaken; - fTaken = true; - } - static bool fExit; - if (fFirstThread) - { - fShutdown = true; - nTransactionsUpdated++; - DBFlush(false); - StopNode(); - DBFlush(true); - boost::filesystem::remove(GetPidFile()); - CreateThread(ExitTimeout, NULL); - Sleep(50); - printf("Bitcoin exiting\n\n"); - fExit = true; - exit(0); - } - else - { - while (!fExit) - Sleep(500); - Sleep(100); - ExitThread(0); - } -} - -void HandleSIGTERM(int) -{ - fRequestShutdown = true; -} - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// Start -// -#if 0 -#ifndef GUI -int main(int argc, char* argv[]) -{ - bool fRet = false; - fRet = AppInit(argc, argv); - - if (fRet && fDaemon) - return 0; - - return 1; -} -#endif -#endif - -bool AppInit(int argc, char* argv[]) -{ - bool fRet = false; - try - { - fRet = AppInit2(argc, argv); - } - catch (std::exception& e) { - PrintException(&e, "AppInit()"); - } catch (...) { - PrintException(NULL, "AppInit()"); - } - if (!fRet) - Shutdown(NULL); - return fRet; -} - -bool AppInit2(int argc, char* argv[]) -{ -#ifdef _MSC_VER - // Turn off microsoft heap dump noise - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); -#endif -#if _MSC_VER >= 1400 - // Disable confusing "helpful" text message on abort, ctrl-c - _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); -#endif -#ifndef __WXMSW__ - umask(077); -#endif -#ifndef __WXMSW__ - // Clean shutdown on SIGTERM - struct sigaction sa; - sa.sa_handler = HandleSIGTERM; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGHUP, &sa, NULL); -#endif - - // - // Parameters - // - ParseParameters(argc, argv); - - if (mapArgs.count("-datadir")) - { - filesystem::path pathDataDir = filesystem::system_complete(mapArgs["-datadir"]); - strlcpy(pszSetDataDir, pathDataDir.string().c_str(), sizeof(pszSetDataDir)); - } - - ReadConfigFile(mapArgs, mapMultiArgs); // Must be done after processing datadir - - if (mapArgs.count("-?") || mapArgs.count("--help")) - { - string beta = VERSION_IS_BETA ? _(" beta") : ""; - string strUsage = string() + - _("Bitcoin version") + " " + FormatFullVersion() + "\n\n" + - _("Usage:") + "\t\t\t\t\t\t\t\t\t\t\n" + - " bitcoin [options] \t " + "\n" + - " bitcoin [options] [params]\t " + _("Send command to -server or bitcoind\n") + - " bitcoin [options] help \t\t " + _("List commands\n") + - " bitcoin [options] help \t\t " + _("Get help for a command\n") + - _("Options:\n") + - " -conf= \t\t " + _("Specify configuration file (default: bitcoin.conf)\n") + - " -pid= \t\t " + _("Specify pid file (default: bitcoind.pid)\n") + - " -gen \t\t " + _("Generate coins\n") + - " -gen=0 \t\t " + _("Don't generate coins\n") + - " -min \t\t " + _("Start minimized\n") + - " -datadir= \t\t " + _("Specify data directory\n") + - " -proxy= \t " + _("Connect through socks4 proxy\n") + - " -dns \t " + _("Allow DNS lookups for addnode and connect\n") + - " -addnode= \t " + _("Add a node to connect to\n") + - " -connect= \t\t " + _("Connect only to the specified node\n") + - " -nolisten \t " + _("Don't accept connections from outside\n") + -#ifdef USE_UPNP -#if USE_UPNP - " -noupnp \t " + _("Don't attempt to use UPnP to map the listening port\n") + -#else - " -upnp \t " + _("Attempt to use UPnP to map the listening port\n") + -#endif -#endif - " -paytxfee= \t " + _("Fee per KB to add to transactions you send\n") + -#ifdef GUI - " -server \t\t " + _("Accept command line and JSON-RPC commands\n") + -#endif -#ifndef __WXMSW__ - " -daemon \t\t " + _("Run in the background as a daemon and accept commands\n") + -#endif - " -testnet \t\t " + _("Use the test network\n") + - " -rpcuser= \t " + _("Username for JSON-RPC connections\n") + - " -rpcpassword=\t " + _("Password for JSON-RPC connections\n") + - " -rpcport= \t\t " + _("Listen for JSON-RPC connections on (default: 8332)\n") + - " -rpcallowip= \t\t " + _("Allow JSON-RPC connections from specified IP address\n") + - " -rpcconnect= \t " + _("Send commands to node running on (default: 127.0.0.1)\n") + - " -keypool= \t " + _("Set key pool size to (default: 100)\n") + - " -rescan \t " + _("Rescan the block chain for missing wallet transactions\n"); - -#ifdef USE_SSL - strUsage += string() + - _("\nSSL options: (see the Bitcoin Wiki for SSL setup instructions)\n") + - " -rpcssl \t " + _("Use OpenSSL (https) for JSON-RPC connections\n") + - " -rpcsslcertificatechainfile=\t " + _("Server certificate file (default: server.cert)\n") + - " -rpcsslprivatekeyfile= \t " + _("Server private key (default: server.pem)\n") + - " -rpcsslciphers= \t " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)\n"); -#endif - - strUsage += string() + - " -? \t\t " + _("This help message\n"); - -#if defined(__WXMSW__) && defined(GUI) - // Tabs make the columns line up in the message box - wxMessageBox(strUsage, "Bitcoin", wxOK); -#else - // Remove tabs - strUsage.erase(std::remove(strUsage.begin(), strUsage.end(), '\t'), strUsage.end()); - fprintf(stderr, "%s", strUsage.c_str()); -#endif - return false; - } - - fDebug = GetBoolArg("-debug"); - fAllowDNS = GetBoolArg("-dns"); - -#ifndef __WXMSW__ - fDaemon = GetBoolArg("-daemon"); -#else - fDaemon = false; -#endif - - if (fDaemon) - fServer = true; - else - fServer = GetBoolArg("-server"); - - /* force fServer when running without GUI */ -#if 0 -#ifndef GUI - fServer = true; -#endif -#endif - fPrintToConsole = GetBoolArg("-printtoconsole"); - fPrintToDebugger = GetBoolArg("-printtodebugger"); - - fTestNet = GetBoolArg("-testnet"); - fNoListen = GetBoolArg("-nolisten"); - fLogTimestamps = GetBoolArg("-logtimestamps"); - - for (int i = 1; i < argc; i++) - if (!IsSwitchChar(argv[i][0])) - fCommandLine = true; - - if (fCommandLine) - { - int ret = CommandLineRPC(argc, argv); - exit(ret); - } - -#ifndef __WXMSW__ - if (fDaemon) - { - // Daemonize - pid_t pid = fork(); - if (pid < 0) - { - fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno); - return false; - } - if (pid > 0) - { - CreatePidFile(GetPidFile(), pid); - return true; - } - - pid_t sid = setsid(); - if (sid < 0) - fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno); - } -#endif - - if (!fDebug && !pszSetDataDir[0]) - ShrinkDebugFile(); - printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - printf("Bitcoin version %s\n", FormatFullVersion().c_str()); -#ifdef GUI - printf("OS version %s\n", ((string)wxGetOsDescription()).c_str()); - printf("System default language is %d %s\n", g_locale.GetSystemLanguage(), ((string)g_locale.GetSysName()).c_str()); - printf("Language file %s (%s)\n", (string("locale/") + (string)g_locale.GetCanonicalName() + "/LC_MESSAGES/bitcoin.mo").c_str(), ((string)g_locale.GetLocale()).c_str()); -#endif - printf("Default data directory %s\n", GetDefaultDataDir().c_str()); - - if (GetBoolArg("-loadblockindextest")) - { - CTxDB txdb("r"); - txdb.LoadBlockIndex(); - PrintBlockTree(); - return false; - } - - // - // Limit to single instance per user - // Required to protect the database files if we're going to keep deleting log.* - // -#if defined(__WXMSW__) && defined(GUI) - // wxSingleInstanceChecker doesn't work on Linux - wxString strMutexName = wxString("bitcoin_running.") + getenv("HOMEPATH"); - for (int i = 0; i < strMutexName.size(); i++) - if (!isalnum(strMutexName[i])) - strMutexName[i] = '.'; - wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName); - if (psingleinstancechecker->IsAnotherRunning()) - { - printf("Existing instance found\n"); - unsigned int nStart = GetTime(); - loop - { - // Show the previous instance and exit - HWND hwndPrev = FindWindowA("wxWindowClassNR", "Bitcoin"); - if (hwndPrev) - { - if (IsIconic(hwndPrev)) - ShowWindow(hwndPrev, SW_RESTORE); - SetForegroundWindow(hwndPrev); - return false; - } - - if (GetTime() > nStart + 60) - return false; - - // Resume this instance if the other exits - delete psingleinstancechecker; - Sleep(1000); - psingleinstancechecker = new wxSingleInstanceChecker(strMutexName); - if (!psingleinstancechecker->IsAnotherRunning()) - break; - } - } -#endif - - // Make sure only a single bitcoin process is using the data directory. - string strLockFile = GetDataDir() + "/.lock"; - FILE* file = fopen(strLockFile.c_str(), "a"); // empty lock file; created if it doesn't exist. - if (file) fclose(file); - static boost::interprocess::file_lock lock(strLockFile.c_str()); - if (!lock.try_lock()) - { - wxMessageBox(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), GetDataDir().c_str()), "Bitcoin"); - return false; - } - - // Bind to the port early so we can tell if another instance is already running. - string strErrors; - if (!fNoListen) - { - if (!BindListenPort(strErrors)) - { - wxMessageBox(strErrors, "Bitcoin"); - return false; - } - } - - // - // Load data files - // - if (fDaemon) - fprintf(stdout, "bitcoin server starting\n"); - strErrors = ""; - int64 nStart; - - printf("Loading addresses...\n"); - nStart = GetTimeMillis(); - if (!LoadAddresses()) - strErrors += _("Error loading addr.dat \n"); - printf(" addresses %15"PRI64d"ms\n", GetTimeMillis() - nStart); - - printf("Loading block index...\n"); - nStart = GetTimeMillis(); - if (!LoadBlockIndex()) - strErrors += _("Error loading blkindex.dat \n"); - printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart); - - printf("Loading wallet...\n"); - nStart = GetTimeMillis(); - bool fFirstRun; - if (!LoadWallet(fFirstRun)) - strErrors += _("Error loading wallet.dat \n"); - printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart); - - CBlockIndex *pindexRescan = pindexBest; - if (GetBoolArg("-rescan")) - pindexRescan = pindexGenesisBlock; - else - { - CWalletDB walletdb; - CBlockLocator locator; - if (walletdb.ReadBestBlock(locator)) - pindexRescan = locator.GetBlockIndex(); - } - if (pindexBest != pindexRescan) - { - printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); - nStart = GetTimeMillis(); - ScanForWalletTransactions(pindexRescan, true); - printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); - } - - printf("Done loading\n"); - - //// debug print - printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size()); - printf("nBestHeight = %d\n", nBestHeight); - printf("mapKeys.size() = %d\n", mapKeys.size()); - printf("mapPubKeys.size() = %d\n", mapPubKeys.size()); - printf("mapWallet.size() = %d\n", mapWallet.size()); - printf("mapAddressBook.size() = %d\n", mapAddressBook.size()); - - if (!strErrors.empty()) - { - wxMessageBox(strErrors, "Bitcoin", wxOK | wxICON_ERROR); - return false; - } - - // Add wallet transactions that aren't already in a block to mapTransactions - ReacceptWalletTransactions(); - - // - // Parameters - // - if (GetBoolArg("-printblockindex") || GetBoolArg("-printblocktree")) - { - PrintBlockTree(); - return false; - } - - if (mapArgs.count("-printblock")) - { - string strMatch = mapArgs["-printblock"]; - int nFound = 0; - for (map::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) - { - uint256 hash = (*mi).first; - if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0) - { - CBlockIndex* pindex = (*mi).second; - CBlock block; - block.ReadFromDisk(pindex); - block.BuildMerkleTree(); - block.print(); - printf("\n"); - nFound++; - } - } - if (nFound == 0) - printf("No blocks matching %s were found\n", strMatch.c_str()); - return false; - } - - fGenerateBitcoins = GetBoolArg("-gen"); - - if (mapArgs.count("-proxy")) - { - fUseProxy = true; - addrProxy = CAddress(mapArgs["-proxy"]); - if (!addrProxy.IsValid()) - { - wxMessageBox(_("Invalid -proxy address"), "Bitcoin"); - return false; - } - } - - if (mapArgs.count("-addnode")) - { - BOOST_FOREACH(string strAddr, mapMultiArgs["-addnode"]) - { - CAddress addr(strAddr, fAllowDNS); - addr.nTime = 0; // so it won't relay unless successfully connected - if (addr.IsValid()) - AddAddress(addr); - } - } - - if (mapArgs.count("-dnsseed")) - DNSAddressSeed(); - - if (mapArgs.count("-paytxfee")) - { - if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee)) - { - wxMessageBox(_("Invalid amount for -paytxfee="), "Bitcoin"); - return false; - } - if (nTransactionFee > 0.25 * COIN) - wxMessageBox(_("Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction."), "Bitcoin", wxOK | wxICON_EXCLAMATION); - } - - if (fHaveUPnP) - { -#if USE_UPNP - if (GetBoolArg("-noupnp")) - fUseUPnP = false; -#else - if (GetBoolArg("-upnp")) - fUseUPnP = true; -#endif - } - - // - // Create the main window and start the node - // -#ifdef GUI - if (!fDaemon) - CreateMainWindow(); -#endif - - if (!CheckDiskSpace()) - return false; - - RandAddSeedPerfmon(); - - if (!CreateThread(StartNode, NULL)) - wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin"); - - if (fServer) - CreateThread(ThreadRPCServer, NULL); - -#if defined(__WXMSW__) && defined(GUI) - if (fFirstRun) - SetStartOnSystemStartup(true); -#endif - -#if 0 -#ifndef GUI - while (1) - Sleep(5000); -#endif -#endif - - return true; -} diff --git a/core/src/irc.cpp b/core/src/irc.cpp deleted file mode 100644 index a76374d143..0000000000 --- a/core/src/irc.cpp +++ /dev/null @@ -1,445 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. - -#include "headers.h" - -using namespace std; -using namespace boost; - -int nGotIRCAddresses = 0; -bool fGotExternalIP = false; - -void ThreadIRCSeed2(void* parg); - - - - -#pragma pack(push, 1) -struct ircaddr -{ - int ip; - short port; -}; -#pragma pack(pop) - -string EncodeAddress(const CAddress& addr) -{ - struct ircaddr tmp; - tmp.ip = addr.ip; - tmp.port = addr.port; - - vector vch(UBEGIN(tmp), UEND(tmp)); - return string("u") + EncodeBase58Check(vch); -} - -bool DecodeAddress(string str, CAddress& addr) -{ - vector vch; - if (!DecodeBase58Check(str.substr(1), vch)) - return false; - - struct ircaddr tmp; - if (vch.size() != sizeof(tmp)) - return false; - memcpy(&tmp, &vch[0], sizeof(tmp)); - - addr = CAddress(tmp.ip, ntohs(tmp.port), NODE_NETWORK); - return true; -} - - - - - - -static bool Send(SOCKET hSocket, const char* pszSend) -{ - if (strstr(pszSend, "PONG") != pszSend) - printf("IRC SENDING: %s\n", pszSend); - const char* psz = pszSend; - const char* pszEnd = psz + strlen(psz); - while (psz < pszEnd) - { - int ret = send(hSocket, psz, pszEnd - psz, MSG_NOSIGNAL); - if (ret < 0) - return false; - psz += ret; - } - return true; -} - -bool RecvLine(SOCKET hSocket, string& strLine) -{ - strLine = ""; - loop - { - char c; - int nBytes = recv(hSocket, &c, 1, 0); - if (nBytes > 0) - { - if (c == '\n') - continue; - if (c == '\r') - return true; - strLine += c; - if (strLine.size() >= 9000) - return true; - } - else if (nBytes <= 0) - { - if (fShutdown) - return false; - if (nBytes < 0) - { - int nErr = WSAGetLastError(); - if (nErr == WSAEMSGSIZE) - continue; - if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS) - { - Sleep(10); - continue; - } - } - if (!strLine.empty()) - return true; - if (nBytes == 0) - { - // socket closed - printf("IRC socket closed\n"); - return false; - } - else - { - // socket error - int nErr = WSAGetLastError(); - printf("IRC recv failed: %d\n", nErr); - return false; - } - } - } -} - -bool RecvLineIRC(SOCKET hSocket, string& strLine) -{ - loop - { - bool fRet = RecvLine(hSocket, strLine); - if (fRet) - { - if (fShutdown) - return false; - vector vWords; - ParseString(strLine, ' ', vWords); - if (vWords.size() >= 1 && vWords[0] == "PING") - { - strLine[1] = 'O'; - strLine += '\r'; - Send(hSocket, strLine.c_str()); - continue; - } - } - return fRet; - } -} - -int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL, const char* psz4=NULL) -{ - loop - { - string strLine; - strLine.reserve(10000); - if (!RecvLineIRC(hSocket, strLine)) - return 0; - printf("IRC %s\n", strLine.c_str()); - if (psz1 && strLine.find(psz1) != -1) - return 1; - if (psz2 && strLine.find(psz2) != -1) - return 2; - if (psz3 && strLine.find(psz3) != -1) - return 3; - if (psz4 && strLine.find(psz4) != -1) - return 4; - } -} - -bool Wait(int nSeconds) -{ - if (fShutdown) - return false; - printf("IRC waiting %d seconds to reconnect\n", nSeconds); - for (int i = 0; i < nSeconds; i++) - { - if (fShutdown) - return false; - Sleep(1000); - } - return true; -} - -bool RecvCodeLine(SOCKET hSocket, const char* psz1, string& strRet) -{ - strRet.clear(); - loop - { - string strLine; - if (!RecvLineIRC(hSocket, strLine)) - return false; - - vector vWords; - ParseString(strLine, ' ', vWords); - if (vWords.size() < 2) - continue; - - if (vWords[1] == psz1) - { - printf("IRC %s\n", strLine.c_str()); - strRet = strLine; - return true; - } - } -} - -bool GetIPFromIRC(SOCKET hSocket, string strMyName, unsigned int& ipRet) -{ - Send(hSocket, strprintf("USERHOST %s\r", strMyName.c_str()).c_str()); - - string strLine; - if (!RecvCodeLine(hSocket, "302", strLine)) - return false; - - vector vWords; - ParseString(strLine, ' ', vWords); - if (vWords.size() < 4) - return false; - - string str = vWords[3]; - if (str.rfind("@") == string::npos) - return false; - string strHost = str.substr(str.rfind("@")+1); - - // Hybrid IRC used by lfnet always returns IP when you userhost yourself, - // but in case another IRC is ever used this should work. - printf("GetIPFromIRC() got userhost %s\n", strHost.c_str()); - if (fUseProxy) - return false; - CAddress addr(strHost, 0, true); - if (!addr.IsValid()) - return false; - ipRet = addr.ip; - - return true; -} - - - -void ThreadIRCSeed(void* parg) -{ - IMPLEMENT_RANDOMIZE_STACK(ThreadIRCSeed(parg)); - try - { - ThreadIRCSeed2(parg); - } - catch (std::exception& e) { - PrintExceptionContinue(&e, "ThreadIRCSeed()"); - } catch (...) { - PrintExceptionContinue(NULL, "ThreadIRCSeed()"); - } - printf("ThreadIRCSeed exiting\n"); -} - -void ThreadIRCSeed2(void* parg) -{ - /* Dont advertise on IRC if we don't allow incoming connections */ - if (mapArgs.count("-connect") || fNoListen) - return; - - if (GetBoolArg("-noirc")) - return; - printf("ThreadIRCSeed started\n"); - int nErrorWait = 10; - int nRetryWait = 10; - bool fNameInUse = false; - bool fTOR = (fUseProxy && addrProxy.port == htons(9050)); - - while (!fShutdown) - { - //CAddress addrConnect("216.155.130.130:6667"); // chat.freenode.net - CAddress addrConnect("92.243.23.21", 6667); // irc.lfnet.org - if (!fTOR) - { - //struct hostent* phostent = gethostbyname("chat.freenode.net"); - CAddress addrIRC("irc.lfnet.org", 6667, true); - if (addrIRC.IsValid()) - addrConnect = addrIRC; - } - - SOCKET hSocket; - if (!ConnectSocket(addrConnect, hSocket)) - { - printf("IRC connect failed\n"); - nErrorWait = nErrorWait * 11 / 10; - if (Wait(nErrorWait += 60)) - continue; - else - return; - } - - if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname", "ignoring hostname")) - { - closesocket(hSocket); - hSocket = INVALID_SOCKET; - nErrorWait = nErrorWait * 11 / 10; - if (Wait(nErrorWait += 60)) - continue; - else - return; - } - - string strMyName; - if (addrLocalHost.IsRoutable() && !fUseProxy && !fNameInUse) - strMyName = EncodeAddress(addrLocalHost); - else - strMyName = strprintf("x%u", GetRand(1000000000)); - - Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str()); - Send(hSocket, strprintf("USER %s 8 * : %s\r", strMyName.c_str(), strMyName.c_str()).c_str()); - - int nRet = RecvUntil(hSocket, " 004 ", " 433 "); - if (nRet != 1) - { - closesocket(hSocket); - hSocket = INVALID_SOCKET; - if (nRet == 2) - { - printf("IRC name already in use\n"); - fNameInUse = true; - Wait(10); - continue; - } - nErrorWait = nErrorWait * 11 / 10; - if (Wait(nErrorWait += 60)) - continue; - else - return; - } - Sleep(500); - - // Get our external IP from the IRC server and re-nick before joining the channel - CAddress addrFromIRC; - if (GetIPFromIRC(hSocket, strMyName, addrFromIRC.ip)) - { - printf("GetIPFromIRC() returned %s\n", addrFromIRC.ToStringIP().c_str()); - if (!fUseProxy && addrFromIRC.IsRoutable()) - { - // IRC lets you to re-nick - fGotExternalIP = true; - addrLocalHost.ip = addrFromIRC.ip; - strMyName = EncodeAddress(addrLocalHost); - Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str()); - } - } - - if (fTestNet) { - Send(hSocket, "JOIN #bitcoinTEST\r"); - Send(hSocket, "WHO #bitcoinTEST\r"); - } else { - // randomly join #bitcoin00-#bitcoin99 - int channel_number = GetRandInt(100); - Send(hSocket, strprintf("JOIN #bitcoin%02d\r", channel_number).c_str()); - Send(hSocket, strprintf("WHO #bitcoin%02d\r", channel_number).c_str()); - } - - int64 nStart = GetTime(); - string strLine; - strLine.reserve(10000); - while (!fShutdown && RecvLineIRC(hSocket, strLine)) - { - if (strLine.empty() || strLine.size() > 900 || strLine[0] != ':') - continue; - - vector vWords; - ParseString(strLine, ' ', vWords); - if (vWords.size() < 2) - continue; - - char pszName[10000]; - pszName[0] = '\0'; - - if (vWords[1] == "352" && vWords.size() >= 8) - { - // index 7 is limited to 16 characters - // could get full length name at index 10, but would be different from join messages - strlcpy(pszName, vWords[7].c_str(), sizeof(pszName)); - printf("IRC got who\n"); - } - - if (vWords[1] == "JOIN" && vWords[0].size() > 1) - { - // :username!username@50000007.F000000B.90000002.IP JOIN :#channelname - strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName)); - if (strchr(pszName, '!')) - *strchr(pszName, '!') = '\0'; - printf("IRC got join\n"); - } - - if (pszName[0] == 'u') - { - CAddress addr; - if (DecodeAddress(pszName, addr)) - { - addr.nTime = GetAdjustedTime(); - if (AddAddress(addr, 51 * 60)) - printf("IRC got new address: %s\n", addr.ToString().c_str()); - nGotIRCAddresses++; - } - else - { - printf("IRC decode failed\n"); - } - } - } - closesocket(hSocket); - hSocket = INVALID_SOCKET; - - // IRC usually blocks TOR, so only try once - if (fTOR) - return; - - if (GetTime() - nStart > 20 * 60) - { - nErrorWait /= 3; - nRetryWait /= 3; - } - - nRetryWait = nRetryWait * 11 / 10; - if (!Wait(nRetryWait += 60)) - return; - } -} - - - - - - - - - - -#ifdef TEST -int main(int argc, char *argv[]) -{ - WSADATA wsadata; - if (WSAStartup(MAKEWORD(2,2), &wsadata) != NO_ERROR) - { - printf("Error at WSAStartup()\n"); - return false; - } - - ThreadIRCSeed(NULL); - - WSACleanup(); - return 0; -} -#endif diff --git a/core/src/main.cpp b/core/src/main.cpp deleted file mode 100644 index 0456041ee5..0000000000 --- a/core/src/main.cpp +++ /dev/null @@ -1,4033 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#include "headers.h" -#include "cryptopp/sha.h" - -using namespace std; -using namespace boost; - -// -// Global state -// - -CCriticalSection cs_main; - -map mapTransactions; -CCriticalSection cs_mapTransactions; -unsigned int nTransactionsUpdated = 0; -map mapNextTx; - -map mapBlockIndex; -uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); -CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); -CBlockIndex* pindexGenesisBlock = NULL; -int nBestHeight = -1; -CBigNum bnBestChainWork = 0; -CBigNum bnBestInvalidWork = 0; -uint256 hashBestChain = 0; -CBlockIndex* pindexBest = NULL; -int64 nTimeBestReceived = 0; - -map mapOrphanBlocks; -multimap mapOrphanBlocksByPrev; - -map mapOrphanTransactions; -multimap mapOrphanTransactionsByPrev; - -map mapWallet; -vector vWalletUpdated; -CCriticalSection cs_mapWallet; - -map, CPrivKey> mapKeys; -map > mapPubKeys; -CCriticalSection cs_mapKeys; -CKey keyUser; - -map mapRequestCount; -CCriticalSection cs_mapRequestCount; - -map mapAddressBook; -CCriticalSection cs_mapAddressBook; - -vector vchDefaultKey; - -double dHashesPerSec; -int64 nHPSTimerStart; - -// Settings -int fGenerateBitcoins = false; -int64 nTransactionFee = 0; -CAddress addrIncoming; -int fLimitProcessors = false; -int nLimitProcessors = 1; -int fMinimizeToTray = true; -int fMinimizeOnClose = true; -#if USE_UPNP -int fUseUPnP = true; -#else -int fUseUPnP = false; -#endif - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// mapKeys -// - -bool AddKey(const CKey& key) -{ - CRITICAL_BLOCK(cs_mapKeys) - { - mapKeys[key.GetPubKey()] = key.GetPrivKey(); - mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey(); - } - return CWalletDB().WriteKey(key.GetPubKey(), key.GetPrivKey()); -} - -vector GenerateNewKey() -{ - RandAddSeedPerfmon(); - CKey key; - key.MakeNewKey(); - if (!AddKey(key)) - throw runtime_error("GenerateNewKey() : AddKey failed"); - return key.GetPubKey(); -} - - - - -////////////////////////////////////////////////////////////////////////////// -// -// mapWallet -// - -bool AddToWallet(const CWalletTx& wtxIn) -{ - uint256 hash = wtxIn.GetHash(); - CRITICAL_BLOCK(cs_mapWallet) - { - // Inserts only if not already there, returns tx inserted or tx found - pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); - CWalletTx& wtx = (*ret.first).second; - bool fInsertedNew = ret.second; - if (fInsertedNew) - wtx.nTimeReceived = GetAdjustedTime(); - - bool fUpdated = false; - if (!fInsertedNew) - { - // Merge - if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) - { - wtx.hashBlock = wtxIn.hashBlock; - fUpdated = true; - } - if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) - { - wtx.vMerkleBranch = wtxIn.vMerkleBranch; - wtx.nIndex = wtxIn.nIndex; - fUpdated = true; - } - if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) - { - wtx.fFromMe = wtxIn.fFromMe; - fUpdated = true; - } - fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent); - } - - //// debug print - printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); - - // Write to disk - if (fInsertedNew || fUpdated) - if (!wtx.WriteToDisk()) - return false; - - // If default receiving address gets used, replace it with a new one - CScript scriptDefaultKey; - scriptDefaultKey.SetBitcoinAddress(vchDefaultKey); - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - if (txout.scriptPubKey == scriptDefaultKey) - { - CWalletDB walletdb; - vchDefaultKey = GetKeyFromKeyPool(); - walletdb.WriteDefaultKey(vchDefaultKey); - walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); - } - } - - // Notify UI - vWalletUpdated.push_back(hash); - } - - // Refresh UI - MainFrameRepaint(); - return true; -} - -bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false) -{ - uint256 hash = tx.GetHash(); - bool fExisted = mapWallet.count(hash); - if (fExisted && !fUpdate) return false; - if (fExisted || tx.IsMine() || tx.IsFromMe()) - { - CWalletTx wtx(tx); - // Get merkle branch if transaction was found in a block - if (pblock) - wtx.SetMerkleBranch(pblock); - return AddToWallet(wtx); - } - return false; -} - -bool EraseFromWallet(uint256 hash) -{ - CRITICAL_BLOCK(cs_mapWallet) - { - if (mapWallet.erase(hash)) - CWalletDB().EraseTx(hash); - } - return true; -} - -void WalletUpdateSpent(const COutPoint& prevout) -{ - // Anytime a signature is successfully verified, it's proof the outpoint is spent. - // Update the wallet spent flag if it doesn't know due to wallet.dat being - // restored from backup or the user making copies of wallet.dat. - CRITICAL_BLOCK(cs_mapWallet) - { - map::iterator mi = mapWallet.find(prevout.hash); - if (mi != mapWallet.end()) - { - CWalletTx& wtx = (*mi).second; - if (!wtx.IsSpent(prevout.n) && wtx.vout[prevout.n].IsMine()) - { - printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); - wtx.MarkSpent(prevout.n); - wtx.WriteToDisk(); - vWalletUpdated.push_back(prevout.hash); - } - } - } -} - - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// mapOrphanTransactions -// - -void AddOrphanTx(const CDataStream& vMsg) -{ - CTransaction tx; - CDataStream(vMsg) >> tx; - uint256 hash = tx.GetHash(); - if (mapOrphanTransactions.count(hash)) - return; - CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg); - BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg)); -} - -void EraseOrphanTx(uint256 hash) -{ - if (!mapOrphanTransactions.count(hash)) - return; - const CDataStream* pvMsg = mapOrphanTransactions[hash]; - CTransaction tx; - CDataStream(*pvMsg) >> tx; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - for (multimap::iterator mi = mapOrphanTransactionsByPrev.lower_bound(txin.prevout.hash); - mi != mapOrphanTransactionsByPrev.upper_bound(txin.prevout.hash);) - { - if ((*mi).second == pvMsg) - mapOrphanTransactionsByPrev.erase(mi++); - else - mi++; - } - } - delete pvMsg; - mapOrphanTransactions.erase(hash); -} - - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// CTransaction and CTxIndex -// - -bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet) -{ - SetNull(); - if (!txdb.ReadTxIndex(prevout.hash, txindexRet)) - return false; - if (!ReadFromDisk(txindexRet.pos)) - return false; - if (prevout.n >= vout.size()) - { - SetNull(); - return false; - } - return true; -} - -bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout) -{ - CTxIndex txindex; - return ReadFromDisk(txdb, prevout, txindex); -} - -bool CTransaction::ReadFromDisk(COutPoint prevout) -{ - CTxDB txdb("r"); - CTxIndex txindex; - return ReadFromDisk(txdb, prevout, txindex); -} - -bool CTxIn::IsMine() const -{ - CRITICAL_BLOCK(cs_mapWallet) - { - map::iterator mi = mapWallet.find(prevout.hash); - if (mi != mapWallet.end()) - { - const CWalletTx& prev = (*mi).second; - if (prevout.n < prev.vout.size()) - if (prev.vout[prevout.n].IsMine()) - return true; - } - } - return false; -} - -int64 CTxIn::GetDebit() const -{ - CRITICAL_BLOCK(cs_mapWallet) - { - map::iterator mi = mapWallet.find(prevout.hash); - if (mi != mapWallet.end()) - { - const CWalletTx& prev = (*mi).second; - if (prevout.n < prev.vout.size()) - if (prev.vout[prevout.n].IsMine()) - return prev.vout[prevout.n].nValue; - } - } - return 0; -} - -int64 CWalletTx::GetTxTime() const -{ - if (!fTimeReceivedIsTxTime && hashBlock != 0) - { - // If we did not receive the transaction directly, we rely on the block's - // time to figure out when it happened. We use the median over a range - // of blocks to try to filter out inaccurate block times. - map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex) - return pindex->GetMedianTime(); - } - } - return nTimeReceived; -} - -int CWalletTx::GetRequestCount() const -{ - // Returns -1 if it wasn't being tracked - int nRequests = -1; - CRITICAL_BLOCK(cs_mapRequestCount) - { - if (IsCoinBase()) - { - // Generated block - if (hashBlock != 0) - { - map::iterator mi = mapRequestCount.find(hashBlock); - if (mi != mapRequestCount.end()) - nRequests = (*mi).second; - } - } - else - { - // Did anyone request this transaction? - map::iterator mi = mapRequestCount.find(GetHash()); - if (mi != mapRequestCount.end()) - { - nRequests = (*mi).second; - - // How about the block it's in? - if (nRequests == 0 && hashBlock != 0) - { - map::iterator mi = mapRequestCount.find(hashBlock); - if (mi != mapRequestCount.end()) - nRequests = (*mi).second; - else - nRequests = 1; // If it's in someone else's block it must have got out - } - } - } - } - return nRequests; -} - -void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list >& listReceived, - list >& listSent, int64& nFee, string& strSentAccount) const -{ - nGeneratedImmature = nGeneratedMature = nFee = 0; - listReceived.clear(); - listSent.clear(); - strSentAccount = strFromAccount; - - if (IsCoinBase()) - { - if (GetBlocksToMaturity() > 0) - nGeneratedImmature = CTransaction::GetCredit(); - else - nGeneratedMature = GetCredit(); - return; - } - - // Compute fee: - int64 nDebit = GetDebit(); - if (nDebit > 0) // debit>0 means we signed/sent this transaction - { - int64 nValueOut = GetValueOut(); - nFee = nDebit - nValueOut; - } - - // Sent/received. Standard client will never generate a send-to-multiple-recipients, - // but non-standard clients might (so return a list of address/amount pairs) - BOOST_FOREACH(const CTxOut& txout, vout) - { - string address; - uint160 hash160; - vector vchPubKey; - if (ExtractHash160(txout.scriptPubKey, hash160)) - address = Hash160ToAddress(hash160); - else if (ExtractPubKey(txout.scriptPubKey, false, vchPubKey)) - address = PubKeyToAddress(vchPubKey); - else - { - printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", - this->GetHash().ToString().c_str()); - address = " unknown "; - } - - // Don't report 'change' txouts - if (nDebit > 0 && txout.IsChange()) - continue; - - if (nDebit > 0) - listSent.push_back(make_pair(address, txout.nValue)); - - if (txout.IsMine()) - listReceived.push_back(make_pair(address, txout.nValue)); - } - -} - -void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, - int64& nSent, int64& nFee) const -{ - nGenerated = nReceived = nSent = nFee = 0; - - int64 allGeneratedImmature, allGeneratedMature, allFee; - allGeneratedImmature = allGeneratedMature = allFee = 0; - string strSentAccount; - list > listReceived; - list > listSent; - GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); - - if (strAccount == "") - nGenerated = allGeneratedMature; - if (strAccount == strSentAccount) - { - BOOST_FOREACH(const PAIRTYPE(string,int64)& s, listSent) - nSent += s.second; - nFee = allFee; - } - CRITICAL_BLOCK(cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) - { - if (mapAddressBook.count(r.first)) - { - if (mapAddressBook[r.first] == strAccount) - { - nReceived += r.second; - } - } - else if (strAccount.empty()) - { - nReceived += r.second; - } - } - } -} - - - -int CMerkleTx::SetMerkleBranch(const CBlock* pblock) -{ - if (fClient) - { - if (hashBlock == 0) - return 0; - } - else - { - CBlock blockTmp; - if (pblock == NULL) - { - // Load the block this tx is in - CTxIndex txindex; - if (!CTxDB("r").ReadTxIndex(GetHash(), txindex)) - return 0; - if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos)) - return 0; - pblock = &blockTmp; - } - - // Update the tx's hashBlock - hashBlock = pblock->GetHash(); - - // Locate the transaction - for (nIndex = 0; nIndex < pblock->vtx.size(); nIndex++) - if (pblock->vtx[nIndex] == *(CTransaction*)this) - break; - if (nIndex == pblock->vtx.size()) - { - vMerkleBranch.clear(); - nIndex = -1; - printf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); - return 0; - } - - // Fill in merkle branch - vMerkleBranch = pblock->GetMerkleBranch(nIndex); - } - - // Is the tx in a block that's in the main chain - map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi == mapBlockIndex.end()) - return 0; - CBlockIndex* pindex = (*mi).second; - if (!pindex || !pindex->IsInMainChain()) - return 0; - - return pindexBest->nHeight - pindex->nHeight + 1; -} - - - -void CWalletTx::AddSupportingTransactions(CTxDB& txdb) -{ - vtxPrev.clear(); - - const int COPY_DEPTH = 3; - if (SetMerkleBranch() < COPY_DEPTH) - { - vector vWorkQueue; - BOOST_FOREACH(const CTxIn& txin, vin) - vWorkQueue.push_back(txin.prevout.hash); - - // This critsect is OK because txdb is already open - CRITICAL_BLOCK(cs_mapWallet) - { - map mapWalletPrev; - set setAlreadyDone; - for (int i = 0; i < vWorkQueue.size(); i++) - { - uint256 hash = vWorkQueue[i]; - if (setAlreadyDone.count(hash)) - continue; - setAlreadyDone.insert(hash); - - CMerkleTx tx; - if (mapWallet.count(hash)) - { - tx = mapWallet[hash]; - BOOST_FOREACH(const CMerkleTx& txWalletPrev, mapWallet[hash].vtxPrev) - mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; - } - else if (mapWalletPrev.count(hash)) - { - tx = *mapWalletPrev[hash]; - } - else if (!fClient && txdb.ReadDiskTx(hash, tx)) - { - ; - } - else - { - printf("ERROR: AddSupportingTransactions() : unsupported transaction\n"); - continue; - } - - int nDepth = tx.SetMerkleBranch(); - vtxPrev.push_back(tx); - - if (nDepth < COPY_DEPTH) - BOOST_FOREACH(const CTxIn& txin, tx.vin) - vWorkQueue.push_back(txin.prevout.hash); - } - } - } - - reverse(vtxPrev.begin(), vtxPrev.end()); -} - - - - - - - - - - - -bool CTransaction::CheckTransaction() const -{ - // Basic checks that don't depend on any context - if (vin.empty() || vout.empty()) - return error("CTransaction::CheckTransaction() : vin or vout empty"); - - // Size limits - if (::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE) - return error("CTransaction::CheckTransaction() : size limits failed"); - - // Check for negative or overflow output values - int64 nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, vout) - { - if (txout.nValue < 0) - return error("CTransaction::CheckTransaction() : txout.nValue negative"); - if (txout.nValue > MAX_MONEY) - return error("CTransaction::CheckTransaction() : txout.nValue too high"); - nValueOut += txout.nValue; - if (!MoneyRange(nValueOut)) - return error("CTransaction::CheckTransaction() : txout total out of range"); - } - - if (IsCoinBase()) - { - if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) - return error("CTransaction::CheckTransaction() : coinbase script size"); - } - else - { - BOOST_FOREACH(const CTxIn& txin, vin) - if (txin.prevout.IsNull()) - return error("CTransaction::CheckTransaction() : prevout is null"); - } - - return true; -} - -bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs) -{ - if (pfMissingInputs) - *pfMissingInputs = false; - - if (!CheckTransaction()) - return error("AcceptToMemoryPool() : CheckTransaction failed"); - - // Coinbase is only valid in a block, not as a loose transaction - if (IsCoinBase()) - return error("AcceptToMemoryPool() : coinbase as individual tx"); - - // To help v0.1.5 clients who would see it as a negative number - if ((int64)nLockTime > INT_MAX) - return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet"); - - // Safety limits - unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK); - // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service - // attacks disallow transactions with more than one SigOp per 34 bytes. - // 34 bytes because a TxOut is: - // 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length - if (GetSigOpCount() > nSize / 34 || nSize < 100) - return error("AcceptToMemoryPool() : nonstandard transaction"); - - // Rather not work on nonstandard transactions (unless -testnet) - if (!fTestNet && !IsStandard()) - return error("AcceptToMemoryPool() : nonstandard transaction type"); - - // Do we already have it? - uint256 hash = GetHash(); - CRITICAL_BLOCK(cs_mapTransactions) - if (mapTransactions.count(hash)) - return false; - if (fCheckInputs) - if (txdb.ContainsTx(hash)) - return false; - - // Check for conflicts with in-memory transactions - CTransaction* ptxOld = NULL; - for (int i = 0; i < vin.size(); i++) - { - COutPoint outpoint = vin[i].prevout; - if (mapNextTx.count(outpoint)) - { - // Disable replacement feature for now - return false; - - // Allow replacing with a newer version of the same transaction - if (i != 0) - return false; - ptxOld = mapNextTx[outpoint].ptx; - if (ptxOld->IsFinal()) - return false; - if (!IsNewerThan(*ptxOld)) - return false; - for (int i = 0; i < vin.size(); i++) - { - COutPoint outpoint = vin[i].prevout; - if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld) - return false; - } - break; - } - } - - if (fCheckInputs) - { - // Check against previous transactions - map mapUnused; - int64 nFees = 0; - if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false)) - { - if (pfMissingInputs) - *pfMissingInputs = true; - return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); - } - - // Don't accept it if it can't get into a block - if (nFees < GetMinFee(1000, true, true)) - return error("AcceptToMemoryPool() : not enough fees"); - - // Continuously rate-limit free transactions - // This mitigates 'penny-flooding' -- sending thousands of free transactions just to - // be annoying or make other's transactions take longer to confirm. - if (nFees < MIN_RELAY_TX_FEE) - { - static CCriticalSection cs; - static double dFreeCount; - static int64 nLastTime; - int64 nNow = GetTime(); - - CRITICAL_BLOCK(cs) - { - // Use an exponentially decaying ~10-minute window: - dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); - nLastTime = nNow; - // -limitfreerelay unit is thousand-bytes-per-minute - // At default rate it would take over a month to fill 1GB - if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe()) - return error("AcceptToMemoryPool() : free transaction rejected by rate limiter"); - if (fDebug) - printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); - dFreeCount += nSize; - } - } - } - - // Store transaction in memory - CRITICAL_BLOCK(cs_mapTransactions) - { - if (ptxOld) - { - printf("AcceptToMemoryPool() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); - ptxOld->RemoveFromMemoryPool(); - } - AddToMemoryPoolUnchecked(); - } - - ///// are we sure this is ok when loading transactions or restoring block txes - // If updated, erase old tx from wallet - if (ptxOld) - EraseFromWallet(ptxOld->GetHash()); - - printf("AcceptToMemoryPool(): accepted %s\n", hash.ToString().substr(0,10).c_str()); - return true; -} - - -bool CTransaction::AddToMemoryPoolUnchecked() -{ - // Add to memory pool without checking anything. Don't call this directly, - // call AcceptToMemoryPool to properly check the transaction first. - CRITICAL_BLOCK(cs_mapTransactions) - { - uint256 hash = GetHash(); - mapTransactions[hash] = *this; - for (int i = 0; i < vin.size(); i++) - mapNextTx[vin[i].prevout] = CInPoint(&mapTransactions[hash], i); - nTransactionsUpdated++; - } - return true; -} - - -bool CTransaction::RemoveFromMemoryPool() -{ - // Remove transaction from memory pool - CRITICAL_BLOCK(cs_mapTransactions) - { - BOOST_FOREACH(const CTxIn& txin, vin) - mapNextTx.erase(txin.prevout); - mapTransactions.erase(GetHash()); - nTransactionsUpdated++; - } - return true; -} - - - - - - -int CMerkleTx::GetDepthInMainChain(int& nHeightRet) const -{ - if (hashBlock == 0 || nIndex == -1) - return 0; - - // Find the block it claims to be in - map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi == mapBlockIndex.end()) - return 0; - CBlockIndex* pindex = (*mi).second; - if (!pindex || !pindex->IsInMainChain()) - return 0; - - // Make sure the merkle branch connects to this block - if (!fMerkleVerified) - { - if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) - return 0; - fMerkleVerified = true; - } - - nHeightRet = pindex->nHeight; - return pindexBest->nHeight - pindex->nHeight + 1; -} - - -int CMerkleTx::GetBlocksToMaturity() const -{ - if (!IsCoinBase()) - return 0; - return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain()); -} - - -bool CMerkleTx::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs) -{ - if (fClient) - { - if (!IsInMainChain() && !ClientConnectInputs()) - return false; - return CTransaction::AcceptToMemoryPool(txdb, false); - } - else - { - return CTransaction::AcceptToMemoryPool(txdb, fCheckInputs); - } -} - - - -bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) -{ - CRITICAL_BLOCK(cs_mapTransactions) - { - // Add previous supporting transactions first - BOOST_FOREACH(CMerkleTx& tx, vtxPrev) - { - if (!tx.IsCoinBase()) - { - uint256 hash = tx.GetHash(); - if (!mapTransactions.count(hash) && !txdb.ContainsTx(hash)) - tx.AcceptToMemoryPool(txdb, fCheckInputs); - } - } - return AcceptToMemoryPool(txdb, fCheckInputs); - } - return false; -} - -int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) -{ - int ret = 0; - - CBlockIndex* pindex = pindexStart; - CRITICAL_BLOCK(cs_mapWallet) - { - while (pindex) - { - CBlock block; - block.ReadFromDisk(pindex, true); - BOOST_FOREACH(CTransaction& tx, block.vtx) - { - if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) - ret++; - } - pindex = pindex->pnext; - } - } - return ret; -} - -void ReacceptWalletTransactions() -{ - CTxDB txdb("r"); - bool fRepeat = true; - while (fRepeat) CRITICAL_BLOCK(cs_mapWallet) - { - fRepeat = false; - vector vMissingTx; - BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) - { - CWalletTx& wtx = item.second; - if (wtx.IsCoinBase() && wtx.IsSpent(0)) - continue; - - CTxIndex txindex; - bool fUpdated = false; - if (txdb.ReadTxIndex(wtx.GetHash(), txindex)) - { - // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat - if (txindex.vSpent.size() != wtx.vout.size()) - { - printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size()); - continue; - } - for (int i = 0; i < txindex.vSpent.size(); i++) - { - if (wtx.IsSpent(i)) - continue; - if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine()) - { - wtx.MarkSpent(i); - fUpdated = true; - vMissingTx.push_back(txindex.vSpent[i]); - } - } - if (fUpdated) - { - printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); - wtx.MarkDirty(); - wtx.WriteToDisk(); - } - } - else - { - // Reaccept any txes of ours that aren't already in a block - if (!wtx.IsCoinBase()) - wtx.AcceptWalletTransaction(txdb, false); - } - } - if (!vMissingTx.empty()) - { - // TODO: optimize this to scan just part of the block chain? - if (ScanForWalletTransactions(pindexGenesisBlock)) - fRepeat = true; // Found missing transactions: re-do Reaccept. - } - } -} - - -void CWalletTx::RelayWalletTransaction(CTxDB& txdb) -{ - BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) - { - if (!tx.IsCoinBase()) - { - uint256 hash = tx.GetHash(); - if (!txdb.ContainsTx(hash)) - RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); - } - } - if (!IsCoinBase()) - { - uint256 hash = GetHash(); - if (!txdb.ContainsTx(hash)) - { - printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); - RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); - } - } -} - -void ResendWalletTransactions() -{ - // Do this infrequently and randomly to avoid giving away - // that these are our transactions. - static int64 nNextTime; - if (GetTime() < nNextTime) - return; - bool fFirst = (nNextTime == 0); - nNextTime = GetTime() + GetRand(30 * 60); - if (fFirst) - return; - - // Only do it if there's been a new block since last time - static int64 nLastTime; - if (nTimeBestReceived < nLastTime) - return; - nLastTime = GetTime(); - - // Rebroadcast any of our txes that aren't in a block yet - printf("ResendWalletTransactions()\n"); - CTxDB txdb("r"); - CRITICAL_BLOCK(cs_mapWallet) - { - // Sort them in chronological order - multimap mapSorted; - BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) - { - CWalletTx& wtx = item.second; - // Don't rebroadcast until it's had plenty of time that - // it should have gotten in already by now. - if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) - mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); - } - BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) - { - CWalletTx& wtx = *item.second; - wtx.RelayWalletTransaction(txdb); - } - } -} - -int CTxIndex::GetDepthInMainChain() const -{ - // Read block header - CBlock block; - if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false)) - return 0; - // Find the block in the index - map::iterator mi = mapBlockIndex.find(block.GetHash()); - if (mi == mapBlockIndex.end()) - return 0; - CBlockIndex* pindex = (*mi).second; - if (!pindex || !pindex->IsInMainChain()) - return 0; - return 1 + nBestHeight - pindex->nHeight; -} - - - - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// CBlock and CBlockIndex -// - -bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions) -{ - if (!fReadTransactions) - { - *this = pindex->GetBlockHeader(); - return true; - } - if (!ReadFromDisk(pindex->nFile, pindex->nBlockPos, fReadTransactions)) - return false; - if (GetHash() != pindex->GetBlockHash()) - return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); - return true; -} - -uint256 GetOrphanRoot(const CBlock* pblock) -{ - // Work back to the first block in the orphan chain - while (mapOrphanBlocks.count(pblock->hashPrevBlock)) - pblock = mapOrphanBlocks[pblock->hashPrevBlock]; - return pblock->GetHash(); -} - -int64 GetBlockValue(int nHeight, int64 nFees) -{ - int64 nSubsidy = 50 * COIN; - - // Subsidy is cut in half every 4 years - nSubsidy >>= (nHeight / 210000); - - return nSubsidy + nFees; -} - -unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast) -{ - const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks - const int64 nTargetSpacing = 10 * 60; - const int64 nInterval = nTargetTimespan / nTargetSpacing; - - // Genesis block - if (pindexLast == NULL) - return bnProofOfWorkLimit.GetCompact(); - - // Only change once per interval - if ((pindexLast->nHeight+1) % nInterval != 0) - return pindexLast->nBits; - - // Go back by what we want to be 14 days worth of blocks - const CBlockIndex* pindexFirst = pindexLast; - for (int i = 0; pindexFirst && i < nInterval-1; i++) - pindexFirst = pindexFirst->pprev; - assert(pindexFirst); - - // Limit adjustment step - int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime(); - printf(" nActualTimespan = %"PRI64d" before bounds\n", nActualTimespan); - if (nActualTimespan < nTargetTimespan/4) - nActualTimespan = nTargetTimespan/4; - if (nActualTimespan > nTargetTimespan*4) - nActualTimespan = nTargetTimespan*4; - - // Retarget - CBigNum bnNew; - bnNew.SetCompact(pindexLast->nBits); - bnNew *= nActualTimespan; - bnNew /= nTargetTimespan; - - if (bnNew > bnProofOfWorkLimit) - bnNew = bnProofOfWorkLimit; - - /// debug print - printf("GetNextWorkRequired RETARGET\n"); - printf("nTargetTimespan = %"PRI64d" nActualTimespan = %"PRI64d"\n", nTargetTimespan, nActualTimespan); - printf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str()); - printf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str()); - - return bnNew.GetCompact(); -} - -bool CheckProofOfWork(uint256 hash, unsigned int nBits) -{ - CBigNum bnTarget; - bnTarget.SetCompact(nBits); - - // Check range - if (bnTarget <= 0 || bnTarget > bnProofOfWorkLimit) - return error("CheckProofOfWork() : nBits below minimum work"); - - // Check proof of work matches claimed amount - if (hash > bnTarget.getuint256()) - return error("CheckProofOfWork() : hash doesn't match nBits"); - - return true; -} - -bool IsInitialBlockDownload() -{ - if (pindexBest == NULL || (!fTestNet && nBestHeight < 118000)) - return true; - static int64 nLastUpdate; - static CBlockIndex* pindexLastBest; - if (pindexBest != pindexLastBest) - { - pindexLastBest = pindexBest; - nLastUpdate = GetTime(); - } - return (GetTime() - nLastUpdate < 10 && - pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); -} - -void InvalidChainFound(CBlockIndex* pindexNew) -{ - if (pindexNew->bnChainWork > bnBestInvalidWork) - { - bnBestInvalidWork = pindexNew->bnChainWork; - CTxDB().WriteBestInvalidWork(bnBestInvalidWork); - MainFrameRepaint(); - } - printf("InvalidChainFound: invalid block=%s height=%d work=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, pindexNew->bnChainWork.ToString().c_str()); - printf("InvalidChainFound: current best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str()); - if (pindexBest && bnBestInvalidWork > bnBestChainWork + pindexBest->GetBlockWork() * 6) - printf("InvalidChainFound: WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); -} - - - - - - - - - - - -bool CTransaction::DisconnectInputs(CTxDB& txdb) -{ - // Relinquish previous transactions' spent pointers - if (!IsCoinBase()) - { - BOOST_FOREACH(const CTxIn& txin, vin) - { - COutPoint prevout = txin.prevout; - - // Get prev txindex from disk - CTxIndex txindex; - if (!txdb.ReadTxIndex(prevout.hash, txindex)) - return error("DisconnectInputs() : ReadTxIndex failed"); - - if (prevout.n >= txindex.vSpent.size()) - return error("DisconnectInputs() : prevout.n out of range"); - - // Mark outpoint as not spent - txindex.vSpent[prevout.n].SetNull(); - - // Write back - if (!txdb.UpdateTxIndex(prevout.hash, txindex)) - return error("DisconnectInputs() : UpdateTxIndex failed"); - } - } - - // Remove transaction from index - if (!txdb.EraseTxIndex(*this)) - return error("DisconnectInputs() : EraseTxPos failed"); - - return true; -} - - -bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPool, CDiskTxPos posThisTx, - CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee) -{ - // Take over previous transactions' spent pointers - if (!IsCoinBase()) - { - int64 nValueIn = 0; - for (int i = 0; i < vin.size(); i++) - { - COutPoint prevout = vin[i].prevout; - - // Read txindex - CTxIndex txindex; - bool fFound = true; - if (fMiner && mapTestPool.count(prevout.hash)) - { - // Get txindex from current proposed changes - txindex = mapTestPool[prevout.hash]; - } - else - { - // Read txindex from txdb - fFound = txdb.ReadTxIndex(prevout.hash, txindex); - } - if (!fFound && (fBlock || fMiner)) - return fMiner ? false : error("ConnectInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - - // Read txPrev - CTransaction txPrev; - if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) - { - // Get prev tx from single transactions in memory - CRITICAL_BLOCK(cs_mapTransactions) - { - if (!mapTransactions.count(prevout.hash)) - return error("ConnectInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - txPrev = mapTransactions[prevout.hash]; - } - if (!fFound) - txindex.vSpent.resize(txPrev.vout.size()); - } - else - { - // Get prev tx from disk - if (!txPrev.ReadFromDisk(txindex.pos)) - return error("ConnectInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - } - - if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) - return error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()); - - // If prev is coinbase, check that it's matured - if (txPrev.IsCoinBase()) - for (CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev) - if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) - return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight); - - // Verify signature - if (!VerifySignature(txPrev, *this, i)) - return error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); - - // Check for conflicts - if (!txindex.vSpent[prevout.n].IsNull()) - return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str()); - - // Check for negative or overflow input values - nValueIn += txPrev.vout[prevout.n].nValue; - if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return error("ConnectInputs() : txin values out of range"); - - // Mark outpoints as spent - txindex.vSpent[prevout.n] = posThisTx; - - // Write back - if (fBlock) - { - if (!txdb.UpdateTxIndex(prevout.hash, txindex)) - return error("ConnectInputs() : UpdateTxIndex failed"); - } - else if (fMiner) - { - mapTestPool[prevout.hash] = txindex; - } - } - - if (nValueIn < GetValueOut()) - return error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()); - - // Tally transaction fees - int64 nTxFee = nValueIn - GetValueOut(); - if (nTxFee < 0) - return error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()); - if (nTxFee < nMinFee) - return false; - nFees += nTxFee; - if (!MoneyRange(nFees)) - return error("ConnectInputs() : nFees out of range"); - } - - if (fBlock) - { - // Add transaction to disk index - if (!txdb.AddTxIndex(*this, posThisTx, pindexBlock->nHeight)) - return error("ConnectInputs() : AddTxPos failed"); - } - else if (fMiner) - { - // Add transaction to test pool - mapTestPool[GetHash()] = CTxIndex(CDiskTxPos(1,1,1), vout.size()); - } - - return true; -} - - -bool CTransaction::ClientConnectInputs() -{ - if (IsCoinBase()) - return false; - - // Take over previous transactions' spent pointers - CRITICAL_BLOCK(cs_mapTransactions) - { - int64 nValueIn = 0; - for (int i = 0; i < vin.size(); i++) - { - // Get prev tx from single transactions in memory - COutPoint prevout = vin[i].prevout; - if (!mapTransactions.count(prevout.hash)) - return false; - CTransaction& txPrev = mapTransactions[prevout.hash]; - - if (prevout.n >= txPrev.vout.size()) - return false; - - // Verify signature - if (!VerifySignature(txPrev, *this, i)) - return error("ConnectInputs() : VerifySignature failed"); - - ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of - ///// this has to go away now that posNext is gone - // // Check for conflicts - // if (!txPrev.vout[prevout.n].posNext.IsNull()) - // return error("ConnectInputs() : prev tx already used"); - // - // // Flag outpoints as used - // txPrev.vout[prevout.n].posNext = posThisTx; - - nValueIn += txPrev.vout[prevout.n].nValue; - - if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return error("ClientConnectInputs() : txin values out of range"); - } - if (GetValueOut() > nValueIn) - return false; - } - - return true; -} - - - - -bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) -{ - // Disconnect in reverse order - for (int i = vtx.size()-1; i >= 0; i--) - if (!vtx[i].DisconnectInputs(txdb)) - return false; - - // Update block index on disk without changing it in memory. - // The memory index structure will be changed after the db commits. - if (pindex->pprev) - { - CDiskBlockIndex blockindexPrev(pindex->pprev); - blockindexPrev.hashNext = 0; - if (!txdb.WriteBlockIndex(blockindexPrev)) - return error("DisconnectBlock() : WriteBlockIndex failed"); - } - - return true; -} - -bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) -{ - // Check it again in case a previous version let a bad block in - if (!CheckBlock()) - return false; - - //// issue here: it doesn't know the version - unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size()); - - map mapUnused; - int64 nFees = 0; - BOOST_FOREACH(CTransaction& tx, vtx) - { - CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); - nTxPos += ::GetSerializeSize(tx, SER_DISK); - - if (!tx.ConnectInputs(txdb, mapUnused, posThisTx, pindex, nFees, true, false)) - return false; - } - - if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) - return false; - - // Update block index on disk without changing it in memory. - // The memory index structure will be changed after the db commits. - if (pindex->pprev) - { - CDiskBlockIndex blockindexPrev(pindex->pprev); - blockindexPrev.hashNext = pindex->GetBlockHash(); - if (!txdb.WriteBlockIndex(blockindexPrev)) - return error("ConnectBlock() : WriteBlockIndex failed"); - } - - // Watch for transactions paying to me - BOOST_FOREACH(CTransaction& tx, vtx) - AddToWalletIfInvolvingMe(tx, this, true); - - return true; -} - -bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) -{ - printf("REORGANIZE\n"); - - // Find the fork - CBlockIndex* pfork = pindexBest; - CBlockIndex* plonger = pindexNew; - while (pfork != plonger) - { - while (plonger->nHeight > pfork->nHeight) - if (!(plonger = plonger->pprev)) - return error("Reorganize() : plonger->pprev is null"); - if (pfork == plonger) - break; - if (!(pfork = pfork->pprev)) - return error("Reorganize() : pfork->pprev is null"); - } - - // List of what to disconnect - vector vDisconnect; - for (CBlockIndex* pindex = pindexBest; pindex != pfork; pindex = pindex->pprev) - vDisconnect.push_back(pindex); - - // List of what to connect - vector vConnect; - for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev) - vConnect.push_back(pindex); - reverse(vConnect.begin(), vConnect.end()); - - // Disconnect shorter branch - vector vResurrect; - BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) - { - CBlock block; - if (!block.ReadFromDisk(pindex)) - return error("Reorganize() : ReadFromDisk for disconnect failed"); - if (!block.DisconnectBlock(txdb, pindex)) - return error("Reorganize() : DisconnectBlock failed"); - - // Queue memory transactions to resurrect - BOOST_FOREACH(const CTransaction& tx, block.vtx) - if (!tx.IsCoinBase()) - vResurrect.push_back(tx); - } - - // Connect longer branch - vector vDelete; - for (int i = 0; i < vConnect.size(); i++) - { - CBlockIndex* pindex = vConnect[i]; - CBlock block; - if (!block.ReadFromDisk(pindex)) - return error("Reorganize() : ReadFromDisk for connect failed"); - if (!block.ConnectBlock(txdb, pindex)) - { - // Invalid block - txdb.TxnAbort(); - return error("Reorganize() : ConnectBlock failed"); - } - - // Queue memory transactions to delete - BOOST_FOREACH(const CTransaction& tx, block.vtx) - vDelete.push_back(tx); - } - if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash())) - return error("Reorganize() : WriteHashBestChain failed"); - - // Make sure it's successfully written to disk before changing memory structure - if (!txdb.TxnCommit()) - return error("Reorganize() : TxnCommit failed"); - - // Disconnect shorter branch - BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) - if (pindex->pprev) - pindex->pprev->pnext = NULL; - - // Connect longer branch - BOOST_FOREACH(CBlockIndex* pindex, vConnect) - if (pindex->pprev) - pindex->pprev->pnext = pindex; - - // Resurrect memory transactions that were in the disconnected branch - BOOST_FOREACH(CTransaction& tx, vResurrect) - tx.AcceptToMemoryPool(txdb, false); - - // Delete redundant memory transactions that are in the connected branch - BOOST_FOREACH(CTransaction& tx, vDelete) - tx.RemoveFromMemoryPool(); - - return true; -} - - -bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) -{ - uint256 hash = GetHash(); - - txdb.TxnBegin(); - if (pindexGenesisBlock == NULL && hash == hashGenesisBlock) - { - txdb.WriteHashBestChain(hash); - if (!txdb.TxnCommit()) - return error("SetBestChain() : TxnCommit failed"); - pindexGenesisBlock = pindexNew; - } - else if (hashPrevBlock == hashBestChain) - { - // Adding to current best branch - if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash)) - { - txdb.TxnAbort(); - InvalidChainFound(pindexNew); - return error("SetBestChain() : ConnectBlock failed"); - } - if (!txdb.TxnCommit()) - return error("SetBestChain() : TxnCommit failed"); - - // Add to current best branch - pindexNew->pprev->pnext = pindexNew; - - // Delete redundant memory transactions - BOOST_FOREACH(CTransaction& tx, vtx) - tx.RemoveFromMemoryPool(); - } - else - { - // New best branch - if (!Reorganize(txdb, pindexNew)) - { - txdb.TxnAbort(); - InvalidChainFound(pindexNew); - return error("SetBestChain() : Reorganize failed"); - } - } - - // Update best block in wallet (so we can detect restored wallets) - if (!IsInitialBlockDownload()) - { - CWalletDB walletdb; - const CBlockLocator locator(pindexNew); - if (!walletdb.WriteBestBlock(locator)) - return error("SetBestChain() : WriteWalletBest failed"); - } - - // New best block - hashBestChain = hash; - pindexBest = pindexNew; - nBestHeight = pindexBest->nHeight; - bnBestChainWork = pindexNew->bnChainWork; - nTimeBestReceived = GetTime(); - nTransactionsUpdated++; - printf("SetBestChain: new best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str()); - - return true; -} - - -bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) -{ - // Check for duplicate - uint256 hash = GetHash(); - if (mapBlockIndex.count(hash)) - return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,20).c_str()); - - // Construct new block index object - CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this); - if (!pindexNew) - return error("AddToBlockIndex() : new CBlockIndex failed"); - map::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; - pindexNew->phashBlock = &((*mi).first); - map::iterator miPrev = mapBlockIndex.find(hashPrevBlock); - if (miPrev != mapBlockIndex.end()) - { - pindexNew->pprev = (*miPrev).second; - pindexNew->nHeight = pindexNew->pprev->nHeight + 1; - } - pindexNew->bnChainWork = (pindexNew->pprev ? pindexNew->pprev->bnChainWork : 0) + pindexNew->GetBlockWork(); - - CTxDB txdb; - txdb.TxnBegin(); - txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew)); - if (!txdb.TxnCommit()) - return false; - - // New best - if (pindexNew->bnChainWork > bnBestChainWork) - if (!SetBestChain(txdb, pindexNew)) - return false; - - txdb.Close(); - - if (pindexNew == pindexBest) - { - // Notify UI to display prev block's coinbase if it was ours - static uint256 hashPrevBestCoinBase; - CRITICAL_BLOCK(cs_mapWallet) - vWalletUpdated.push_back(hashPrevBestCoinBase); - hashPrevBestCoinBase = vtx[0].GetHash(); - } - - MainFrameRepaint(); - return true; -} - - - - -bool CBlock::CheckBlock() const -{ - // These are checks that are independent of context - // that can be verified before saving an orphan block. - - // Size limits - if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE) - return error("CheckBlock() : size limits failed"); - - // Check proof of work matches claimed amount - if (!CheckProofOfWork(GetHash(), nBits)) - return error("CheckBlock() : proof of work failed"); - - // Check timestamp - if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return error("CheckBlock() : block timestamp too far in the future"); - - // First transaction must be coinbase, the rest must not be - if (vtx.empty() || !vtx[0].IsCoinBase()) - return error("CheckBlock() : first tx is not coinbase"); - for (int i = 1; i < vtx.size(); i++) - if (vtx[i].IsCoinBase()) - return error("CheckBlock() : more than one coinbase"); - - // Check transactions - BOOST_FOREACH(const CTransaction& tx, vtx) - if (!tx.CheckTransaction()) - return error("CheckBlock() : CheckTransaction failed"); - - // Check that it's not full of nonstandard transactions - if (GetSigOpCount() > MAX_BLOCK_SIGOPS) - return error("CheckBlock() : too many nonstandard transactions"); - - // Check merkleroot - if (hashMerkleRoot != BuildMerkleTree()) - return error("CheckBlock() : hashMerkleRoot mismatch"); - - return true; -} - -bool CBlock::AcceptBlock() -{ - // Check for duplicate - uint256 hash = GetHash(); - if (mapBlockIndex.count(hash)) - return error("AcceptBlock() : block already in mapBlockIndex"); - - // Get prev block index - map::iterator mi = mapBlockIndex.find(hashPrevBlock); - if (mi == mapBlockIndex.end()) - return error("AcceptBlock() : prev block not found"); - CBlockIndex* pindexPrev = (*mi).second; - int nHeight = pindexPrev->nHeight+1; - - // Check proof of work - if (nBits != GetNextWorkRequired(pindexPrev)) - return error("AcceptBlock() : incorrect proof of work"); - - // Check timestamp against prev - if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return error("AcceptBlock() : block's timestamp is too early"); - - // Check that all transactions are finalized - BOOST_FOREACH(const CTransaction& tx, vtx) - if (!tx.IsFinal(nHeight, GetBlockTime())) - return error("AcceptBlock() : contains a non-final transaction"); - - // Check that the block chain matches the known block chain up to a checkpoint - if (!fTestNet) - if ((nHeight == 11111 && hash != uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")) || - (nHeight == 33333 && hash != uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")) || - (nHeight == 68555 && hash != uint256("0x00000000001e1b4903550a0b96e9a9405c8a95f387162e4944e8d9fbe501cd6a")) || - (nHeight == 70567 && hash != uint256("0x00000000006a49b14bcf27462068f1264c961f11fa2e0eddd2be0791e1d4124a")) || - (nHeight == 74000 && hash != uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")) || - (nHeight == 105000 && hash != uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")) || - (nHeight == 118000 && hash != uint256("0x000000000000774a7f8a7a12dc906ddb9e17e75d684f15e00f8767f9e8f36553"))) - return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight); - - // Write block to history file - if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK))) - return error("AcceptBlock() : out of disk space"); - unsigned int nFile = -1; - unsigned int nBlockPos = 0; - if (!WriteToDisk(nFile, nBlockPos)) - return error("AcceptBlock() : WriteToDisk failed"); - if (!AddToBlockIndex(nFile, nBlockPos)) - return error("AcceptBlock() : AddToBlockIndex failed"); - - // Relay inventory, but don't relay old inventory during initial block download - if (hashBestChain == hash) - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 118000)) - pnode->PushInventory(CInv(MSG_BLOCK, hash)); - - return true; -} - -bool ProcessBlock(CNode* pfrom, CBlock* pblock) -{ - // Check for duplicate - uint256 hash = pblock->GetHash(); - if (mapBlockIndex.count(hash)) - return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().substr(0,20).c_str()); - if (mapOrphanBlocks.count(hash)) - return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str()); - - // Preliminary checks - if (!pblock->CheckBlock()) - return error("ProcessBlock() : CheckBlock FAILED"); - - // If don't already have its previous block, shunt it off to holding area until we get it - if (!mapBlockIndex.count(pblock->hashPrevBlock)) - { - printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str()); - CBlock* pblock2 = new CBlock(*pblock); - mapOrphanBlocks.insert(make_pair(hash, pblock2)); - mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); - - // Ask this guy to fill in what we're missing - if (pfrom) - pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2)); - return true; - } - - // Store to disk - if (!pblock->AcceptBlock()) - return error("ProcessBlock() : AcceptBlock FAILED"); - - // Recursively process any orphan blocks that depended on this one - vector vWorkQueue; - vWorkQueue.push_back(hash); - for (int i = 0; i < vWorkQueue.size(); i++) - { - uint256 hashPrev = vWorkQueue[i]; - for (multimap::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev); - mi != mapOrphanBlocksByPrev.upper_bound(hashPrev); - ++mi) - { - CBlock* pblockOrphan = (*mi).second; - if (pblockOrphan->AcceptBlock()) - vWorkQueue.push_back(pblockOrphan->GetHash()); - mapOrphanBlocks.erase(pblockOrphan->GetHash()); - delete pblockOrphan; - } - mapOrphanBlocksByPrev.erase(hashPrev); - } - - printf("ProcessBlock: ACCEPTED\n"); - return true; -} - - - - - - - - -template -bool ScanMessageStart(Stream& s) -{ - // Scan ahead to the next pchMessageStart, which should normally be immediately - // at the file pointer. Leaves file pointer at end of pchMessageStart. - s.clear(0); - short prevmask = s.exceptions(0); - const char* p = BEGIN(pchMessageStart); - try - { - loop - { - char c; - s.read(&c, 1); - if (s.fail()) - { - s.clear(0); - s.exceptions(prevmask); - return false; - } - if (*p != c) - p = BEGIN(pchMessageStart); - if (*p == c) - { - if (++p == END(pchMessageStart)) - { - s.clear(0); - s.exceptions(prevmask); - return true; - } - } - } - } - catch (...) - { - s.clear(0); - s.exceptions(prevmask); - return false; - } -} - -bool CheckDiskSpace(uint64 nAdditionalBytes) -{ - uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available; - - // Check for 15MB because database could create another 10MB log file at any time - if (nFreeBytesAvailable < (uint64)15000000 + nAdditionalBytes) - { - fShutdown = true; - string strMessage = _("Warning: Disk space is low "); - strMiscWarning = strMessage; - printf("*** %s\n", strMessage.c_str()); - ThreadSafeMessageBox(strMessage, "Bitcoin", wxOK | wxICON_EXCLAMATION); - CreateThread(Shutdown, NULL); - return false; - } - return true; -} - -FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode) -{ - if (nFile == -1) - return NULL; - FILE* file = fopen(strprintf("%s/blk%04d.dat", GetDataDir().c_str(), nFile).c_str(), pszMode); - if (!file) - return NULL; - if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w')) - { - if (fseek(file, nBlockPos, SEEK_SET) != 0) - { - fclose(file); - return NULL; - } - } - return file; -} - -static unsigned int nCurrentBlockFile = 1; - -FILE* AppendBlockFile(unsigned int& nFileRet) -{ - nFileRet = 0; - loop - { - FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab"); - if (!file) - return NULL; - if (fseek(file, 0, SEEK_END) != 0) - return NULL; - // FAT32 filesize max 4GB, fseek and ftell max 2GB, so we must stay under 2GB - if (ftell(file) < 0x7F000000 - MAX_SIZE) - { - nFileRet = nCurrentBlockFile; - return file; - } - fclose(file); - nCurrentBlockFile++; - } -} - -bool LoadBlockIndex(bool fAllowNew) -{ - if (fTestNet) - { - hashGenesisBlock = uint256("0x00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"); - bnProofOfWorkLimit = CBigNum(~uint256(0) >> 28); - pchMessageStart[0] = 0xfa; - pchMessageStart[1] = 0xbf; - pchMessageStart[2] = 0xb5; - pchMessageStart[3] = 0xda; - } - - // - // Load block index - // - CTxDB txdb("cr"); - if (!txdb.LoadBlockIndex()) - return false; - txdb.Close(); - - // - // Init with genesis block - // - if (mapBlockIndex.empty()) - { - if (!fAllowNew) - return false; - - // Genesis Block: - // CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1) - // CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) - // CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73) - // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) - // vMerkleTree: 4a5e1e - - // Genesis block - const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"; - CTransaction txNew; - txNew.vin.resize(1); - txNew.vout.resize(1); - txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); - txNew.vout[0].nValue = 50 * COIN; - txNew.vout[0].scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; - CBlock block; - block.vtx.push_back(txNew); - block.hashPrevBlock = 0; - block.hashMerkleRoot = block.BuildMerkleTree(); - block.nVersion = 1; - block.nTime = 1231006505; - block.nBits = 0x1d00ffff; - block.nNonce = 2083236893; - - if (fTestNet) - { - block.nTime = 1296688602; - block.nBits = 0x1d07fff8; - block.nNonce = 384568319; - } - - //// debug print - printf("%s\n", block.GetHash().ToString().c_str()); - printf("%s\n", hashGenesisBlock.ToString().c_str()); - printf("%s\n", block.hashMerkleRoot.ToString().c_str()); - assert(block.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); - block.print(); - assert(block.GetHash() == hashGenesisBlock); - - // Start new block file - unsigned int nFile; - unsigned int nBlockPos; - if (!block.WriteToDisk(nFile, nBlockPos)) - return error("LoadBlockIndex() : writing genesis block to disk failed"); - if (!block.AddToBlockIndex(nFile, nBlockPos)) - return error("LoadBlockIndex() : genesis block not accepted"); - } - - return true; -} - - - -void PrintBlockTree() -{ - // precompute tree structure - map > mapNext; - for (map::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) - { - CBlockIndex* pindex = (*mi).second; - mapNext[pindex->pprev].push_back(pindex); - // test - //while (rand() % 3 == 0) - // mapNext[pindex->pprev].push_back(pindex); - } - - vector > vStack; - vStack.push_back(make_pair(0, pindexGenesisBlock)); - - int nPrevCol = 0; - while (!vStack.empty()) - { - int nCol = vStack.back().first; - CBlockIndex* pindex = vStack.back().second; - vStack.pop_back(); - - // print split or gap - if (nCol > nPrevCol) - { - for (int i = 0; i < nCol-1; i++) - printf("| "); - printf("|\\\n"); - } - else if (nCol < nPrevCol) - { - for (int i = 0; i < nCol; i++) - printf("| "); - printf("|\n"); - } - nPrevCol = nCol; - - // print columns - for (int i = 0; i < nCol; i++) - printf("| "); - - // print item - CBlock block; - block.ReadFromDisk(pindex); - printf("%d (%u,%u) %s %s tx %d", - pindex->nHeight, - pindex->nFile, - pindex->nBlockPos, - block.GetHash().ToString().substr(0,20).c_str(), - DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(), - block.vtx.size()); - - CRITICAL_BLOCK(cs_mapWallet) - { - if (mapWallet.count(block.vtx[0].GetHash())) - { - CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; - printf(" mine: %d %d %d", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); - } - } - printf("\n"); - - - // put the main timechain first - vector& vNext = mapNext[pindex]; - for (int i = 0; i < vNext.size(); i++) - { - if (vNext[i]->pnext) - { - swap(vNext[0], vNext[i]); - break; - } - } - - // iterate children - for (int i = 0; i < vNext.size(); i++) - vStack.push_back(make_pair(nCol+i, vNext[i])); - } -} - - - - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// CAlert -// - -map mapAlerts; -CCriticalSection cs_mapAlerts; - -string GetWarnings(string strFor) -{ - int nPriority = 0; - string strStatusBar; - string strRPC; - if (GetBoolArg("-testsafemode")) - strRPC = "test"; - - // Misc warnings like out of disk space and clock is wrong - if (strMiscWarning != "") - { - nPriority = 1000; - strStatusBar = strMiscWarning; - } - - // Longer invalid proof-of-work chain - if (pindexBest && bnBestInvalidWork > bnBestChainWork + pindexBest->GetBlockWork() * 6) - { - nPriority = 2000; - strStatusBar = strRPC = "WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade."; - } - - // Alerts - CRITICAL_BLOCK(cs_mapAlerts) - { - BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) - { - const CAlert& alert = item.second; - if (alert.AppliesToMe() && alert.nPriority > nPriority) - { - nPriority = alert.nPriority; - strStatusBar = alert.strStatusBar; - } - } - } - - if (strFor == "statusbar") - return strStatusBar; - else if (strFor == "rpc") - return strRPC; - assert(("GetWarnings() : invalid parameter", false)); - return "error"; -} - -bool CAlert::ProcessAlert() -{ - if (!CheckSignature()) - return false; - if (!IsInEffect()) - return false; - - CRITICAL_BLOCK(cs_mapAlerts) - { - // Cancel previous alerts - for (map::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();) - { - const CAlert& alert = (*mi).second; - if (Cancels(alert)) - { - printf("cancelling alert %d\n", alert.nID); - mapAlerts.erase(mi++); - } - else if (!alert.IsInEffect()) - { - printf("expiring alert %d\n", alert.nID); - mapAlerts.erase(mi++); - } - else - mi++; - } - - // Check if this alert has been cancelled - BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) - { - const CAlert& alert = item.second; - if (alert.Cancels(*this)) - { - printf("alert already cancelled by %d\n", alert.nID); - return false; - } - } - - // Add to mapAlerts - mapAlerts.insert(make_pair(GetHash(), *this)); - } - - printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe()); - MainFrameRepaint(); - return true; -} - - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// Messages -// - - -bool AlreadyHave(CTxDB& txdb, const CInv& inv) -{ - switch (inv.type) - { - case MSG_TX: return mapTransactions.count(inv.hash) || mapOrphanTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash); - case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash); - } - // Don't know what it is, just say we already got one - return true; -} - - - - -// The message start string is designed to be unlikely to occur in normal data. -// The characters are rarely used upper ascii, not valid as UTF-8, and produce -// a large 4-byte int at any alignment. -char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; - - -bool ProcessMessages(CNode* pfrom) -{ - CDataStream& vRecv = pfrom->vRecv; - if (vRecv.empty()) - return true; - //if (fDebug) - // printf("ProcessMessages(%u bytes)\n", vRecv.size()); - - // - // Message format - // (4) message start - // (12) command - // (4) size - // (4) checksum - // (x) data - // - - loop - { - // Scan for message start - CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart)); - int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader()); - if (vRecv.end() - pstart < nHeaderSize) - { - if (vRecv.size() > nHeaderSize) - { - printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n"); - vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); - } - break; - } - if (pstart - vRecv.begin() > 0) - printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", pstart - vRecv.begin()); - vRecv.erase(vRecv.begin(), pstart); - - // Read header - vector vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize); - CMessageHeader hdr; - vRecv >> hdr; - if (!hdr.IsValid()) - { - printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); - continue; - } - string strCommand = hdr.GetCommand(); - - // Message size - unsigned int nMessageSize = hdr.nMessageSize; - if (nMessageSize > MAX_SIZE) - { - printf("ProcessMessage(%s, %u bytes) : nMessageSize > MAX_SIZE\n", strCommand.c_str(), nMessageSize); - continue; - } - if (nMessageSize > vRecv.size()) - { - // Rewind and wait for rest of message - vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end()); - break; - } - - // Checksum - if (vRecv.GetVersion() >= 209) - { - uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); - unsigned int nChecksum = 0; - memcpy(&nChecksum, &hash, sizeof(nChecksum)); - if (nChecksum != hdr.nChecksum) - { - printf("ProcessMessage(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", - strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); - continue; - } - } - - // Copy message to its own buffer - CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion); - vRecv.ignore(nMessageSize); - - // Process message - bool fRet = false; - try - { - CRITICAL_BLOCK(cs_main) - fRet = ProcessMessage(pfrom, strCommand, vMsg); - if (fShutdown) - return true; - } - catch (std::ios_base::failure& e) - { - if (strstr(e.what(), "end of data")) - { - // Allow exceptions from underlength message on vRecv - printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what()); - } - else if (strstr(e.what(), "size too large")) - { - // Allow exceptions from overlong size - printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); - } - else - { - PrintExceptionContinue(&e, "ProcessMessage()"); - } - } - catch (std::exception& e) { - PrintExceptionContinue(&e, "ProcessMessage()"); - } catch (...) { - PrintExceptionContinue(NULL, "ProcessMessage()"); - } - - if (!fRet) - printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); - } - - vRecv.Compact(); - return true; -} - - - - -bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) -{ - static map > mapReuseKey; - RandAddSeedPerfmon(); - if (fDebug) - printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); - printf("received: %s (%d bytes)\n", strCommand.c_str(), vRecv.size()); - if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) - { - printf("dropmessagestest DROPPING RECV MESSAGE\n"); - return true; - } - - - - - - if (strCommand == "version") - { - // Each connection can only send one version message - if (pfrom->nVersion != 0) - return false; - - int64 nTime; - CAddress addrMe; - CAddress addrFrom; - uint64 nNonce = 1; - vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; - if (pfrom->nVersion == 10300) - pfrom->nVersion = 300; - if (pfrom->nVersion >= 106 && !vRecv.empty()) - vRecv >> addrFrom >> nNonce; - if (pfrom->nVersion >= 106 && !vRecv.empty()) - vRecv >> pfrom->strSubVer; - if (pfrom->nVersion >= 209 && !vRecv.empty()) - vRecv >> pfrom->nStartingHeight; - - if (pfrom->nVersion == 0) - return false; - - // Disconnect if we connected to ourself - if (nNonce == nLocalHostNonce && nNonce > 1) - { - printf("connected to self at %s, disconnecting\n", pfrom->addr.ToString().c_str()); - pfrom->fDisconnect = true; - return true; - } - - // Be shy and don't send version until we hear - if (pfrom->fInbound) - pfrom->PushVersion(); - - pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); - - AddTimeData(pfrom->addr.ip, nTime); - - // Change version - if (pfrom->nVersion >= 209) - pfrom->PushMessage("verack"); - pfrom->vSend.SetVersion(min(pfrom->nVersion, VERSION)); - if (pfrom->nVersion < 209) - pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION)); - - if (!pfrom->fInbound) - { - // Advertise our address - if (addrLocalHost.IsRoutable() && !fUseProxy) - { - CAddress addr(addrLocalHost); - addr.nTime = GetAdjustedTime(); - pfrom->PushAddress(addr); - } - - // Get recent addresses - if (pfrom->nVersion >= 31402 || mapAddresses.size() < 1000) - { - pfrom->PushMessage("getaddr"); - pfrom->fGetAddr = true; - } - } - - // Ask the first connected node for block updates - static int nAskedForBlocks; - if (!pfrom->fClient && (nAskedForBlocks < 1 || vNodes.size() <= 1)) - { - nAskedForBlocks++; - pfrom->PushGetBlocks(pindexBest, uint256(0)); - } - - // Relay alerts - CRITICAL_BLOCK(cs_mapAlerts) - BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) - item.second.RelayTo(pfrom); - - pfrom->fSuccessfullyConnected = true; - - printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight); - } - - - else if (pfrom->nVersion == 0) - { - // Must have a version message before anything else - return false; - } - - - else if (strCommand == "verack") - { - pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION)); - } - - - else if (strCommand == "addr") - { - vector vAddr; - vRecv >> vAddr; - - // Don't want addr from older versions unless seeding - if (pfrom->nVersion < 209) - return true; - if (pfrom->nVersion < 31402 && mapAddresses.size() > 1000) - return true; - if (vAddr.size() > 1000) - return error("message addr size() = %d", vAddr.size()); - - // Store the new addresses - int64 nNow = GetAdjustedTime(); - int64 nSince = nNow - 10 * 60; - BOOST_FOREACH(CAddress& addr, vAddr) - { - if (fShutdown) - return true; - // ignore IPv6 for now, since it isn't implemented anyway - if (!addr.IsIPv4()) - continue; - if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) - addr.nTime = nNow - 5 * 24 * 60 * 60; - AddAddress(addr, 2 * 60 * 60); - pfrom->AddAddressKnown(addr); - if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) - { - // Relay to a limited number of other nodes - CRITICAL_BLOCK(cs_vNodes) - { - // Use deterministic randomness to send to the same nodes for 24 hours - // at a time so the setAddrKnowns of the chosen nodes prevent repeats - static uint256 hashSalt; - if (hashSalt == 0) - RAND_bytes((unsigned char*)&hashSalt, sizeof(hashSalt)); - uint256 hashRand = hashSalt ^ (((int64)addr.ip)<<32) ^ ((GetTime()+addr.ip)/(24*60*60)); - hashRand = Hash(BEGIN(hashRand), END(hashRand)); - multimap mapMix; - BOOST_FOREACH(CNode* pnode, vNodes) - { - if (pnode->nVersion < 31402) - continue; - unsigned int nPointer; - memcpy(&nPointer, &pnode, sizeof(nPointer)); - uint256 hashKey = hashRand ^ nPointer; - hashKey = Hash(BEGIN(hashKey), END(hashKey)); - mapMix.insert(make_pair(hashKey, pnode)); - } - int nRelayNodes = 2; - for (multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) - ((*mi).second)->PushAddress(addr); - } - } - } - if (vAddr.size() < 1000) - pfrom->fGetAddr = false; - } - - - else if (strCommand == "inv") - { - vector vInv; - vRecv >> vInv; - if (vInv.size() > 50000) - return error("message inv size() = %d", vInv.size()); - - CTxDB txdb("r"); - BOOST_FOREACH(const CInv& inv, vInv) - { - if (fShutdown) - return true; - pfrom->AddInventoryKnown(inv); - - bool fAlreadyHave = AlreadyHave(txdb, inv); - printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); - - if (!fAlreadyHave) - pfrom->AskFor(inv); - else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) - pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); - - // Track requests for our stuff - CRITICAL_BLOCK(cs_mapRequestCount) - { - map::iterator mi = mapRequestCount.find(inv.hash); - if (mi != mapRequestCount.end()) - (*mi).second++; - } - } - } - - - else if (strCommand == "getdata") - { - vector vInv; - vRecv >> vInv; - if (vInv.size() > 50000) - return error("message getdata size() = %d", vInv.size()); - - BOOST_FOREACH(const CInv& inv, vInv) - { - if (fShutdown) - return true; - printf("received getdata for: %s\n", inv.ToString().c_str()); - - if (inv.type == MSG_BLOCK) - { - // Send block from disk - map::iterator mi = mapBlockIndex.find(inv.hash); - if (mi != mapBlockIndex.end()) - { - CBlock block; - block.ReadFromDisk((*mi).second); - pfrom->PushMessage("block", block); - - // Trigger them to send a getblocks request for the next batch of inventory - if (inv.hash == pfrom->hashContinue) - { - // Bypass PushInventory, this must send even if redundant, - // and we want it right after the last block so they don't - // wait for other stuff first. - vector vInv; - vInv.push_back(CInv(MSG_BLOCK, hashBestChain)); - pfrom->PushMessage("inv", vInv); - pfrom->hashContinue = 0; - } - } - } - else if (inv.IsKnownType()) - { - // Send stream from relay memory - CRITICAL_BLOCK(cs_mapRelay) - { - map::iterator mi = mapRelay.find(inv); - if (mi != mapRelay.end()) - pfrom->PushMessage(inv.GetCommand(), (*mi).second); - } - } - - // Track requests for our stuff - CRITICAL_BLOCK(cs_mapRequestCount) - { - map::iterator mi = mapRequestCount.find(inv.hash); - if (mi != mapRequestCount.end()) - (*mi).second++; - } - } - } - - - else if (strCommand == "getblocks") - { - CBlockLocator locator; - uint256 hashStop; - vRecv >> locator >> hashStop; - - // Find the last block the caller has in the main chain - CBlockIndex* pindex = locator.GetBlockIndex(); - - // Send the rest of the chain - if (pindex) - pindex = pindex->pnext; - int nLimit = 500 + locator.GetDistanceBack(); - printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit); - for (; pindex; pindex = pindex->pnext) - { - if (pindex->GetBlockHash() == hashStop) - { - printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); - break; - } - pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); - if (--nLimit <= 0) - { - // When this block is requested, we'll send an inv that'll make them - // getblocks the next batch of inventory. - printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); - pfrom->hashContinue = pindex->GetBlockHash(); - break; - } - } - } - - - else if (strCommand == "getheaders") - { - CBlockLocator locator; - uint256 hashStop; - vRecv >> locator >> hashStop; - - CBlockIndex* pindex = NULL; - if (locator.IsNull()) - { - // If locator is null, return the hashStop block - map::iterator mi = mapBlockIndex.find(hashStop); - if (mi == mapBlockIndex.end()) - return true; - pindex = (*mi).second; - } - else - { - // Find the last block the caller has in the main chain - pindex = locator.GetBlockIndex(); - if (pindex) - pindex = pindex->pnext; - } - - vector vHeaders; - int nLimit = 2000 + locator.GetDistanceBack(); - printf("getheaders %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit); - for (; pindex; pindex = pindex->pnext) - { - vHeaders.push_back(pindex->GetBlockHeader()); - if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) - break; - } - pfrom->PushMessage("headers", vHeaders); - } - - - else if (strCommand == "tx") - { - vector vWorkQueue; - CDataStream vMsg(vRecv); - CTransaction tx; - vRecv >> tx; - - CInv inv(MSG_TX, tx.GetHash()); - pfrom->AddInventoryKnown(inv); - - bool fMissingInputs = false; - if (tx.AcceptToMemoryPool(true, &fMissingInputs)) - { - AddToWalletIfInvolvingMe(tx, NULL, true); - RelayMessage(inv, vMsg); - mapAlreadyAskedFor.erase(inv); - vWorkQueue.push_back(inv.hash); - - // Recursively process any orphan transactions that depended on this one - for (int i = 0; i < vWorkQueue.size(); i++) - { - uint256 hashPrev = vWorkQueue[i]; - for (multimap::iterator mi = mapOrphanTransactionsByPrev.lower_bound(hashPrev); - mi != mapOrphanTransactionsByPrev.upper_bound(hashPrev); - ++mi) - { - const CDataStream& vMsg = *((*mi).second); - CTransaction tx; - CDataStream(vMsg) >> tx; - CInv inv(MSG_TX, tx.GetHash()); - - if (tx.AcceptToMemoryPool(true)) - { - printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); - AddToWalletIfInvolvingMe(tx, NULL, true); - RelayMessage(inv, vMsg); - mapAlreadyAskedFor.erase(inv); - vWorkQueue.push_back(inv.hash); - } - } - } - - BOOST_FOREACH(uint256 hash, vWorkQueue) - EraseOrphanTx(hash); - } - else if (fMissingInputs) - { - printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); - AddOrphanTx(vMsg); - } - } - - - else if (strCommand == "block") - { - CBlock block; - vRecv >> block; - - printf("received block %s\n", block.GetHash().ToString().substr(0,20).c_str()); - // block.print(); - - CInv inv(MSG_BLOCK, block.GetHash()); - pfrom->AddInventoryKnown(inv); - - if (ProcessBlock(pfrom, &block)) - mapAlreadyAskedFor.erase(inv); - } - - - else if (strCommand == "getaddr") - { - // Nodes rebroadcast an addr every 24 hours - pfrom->vAddrToSend.clear(); - int64 nSince = GetAdjustedTime() - 3 * 60 * 60; // in the last 3 hours - CRITICAL_BLOCK(cs_mapAddresses) - { - unsigned int nCount = 0; - BOOST_FOREACH(const PAIRTYPE(vector, CAddress)& item, mapAddresses) - { - const CAddress& addr = item.second; - if (addr.nTime > nSince) - nCount++; - } - BOOST_FOREACH(const PAIRTYPE(vector, CAddress)& item, mapAddresses) - { - const CAddress& addr = item.second; - if (addr.nTime > nSince && GetRand(nCount) < 2500) - pfrom->PushAddress(addr); - } - } - } - - - else if (strCommand == "checkorder") - { - uint256 hashReply; - vRecv >> hashReply; - - if (!GetBoolArg("-allowreceivebyip")) - { - pfrom->PushMessage("reply", hashReply, (int)2, string("")); - return true; - } - - CWalletTx order; - vRecv >> order; - - /// we have a chance to check the order here - - // Keep giving the same key to the same ip until they use it - if (!mapReuseKey.count(pfrom->addr.ip)) - mapReuseKey[pfrom->addr.ip] = GetKeyFromKeyPool(); - - // Send back approval of order and pubkey to use - CScript scriptPubKey; - scriptPubKey << mapReuseKey[pfrom->addr.ip] << OP_CHECKSIG; - pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey); - } - - - else if (strCommand == "submitorder") - { - uint256 hashReply; - vRecv >> hashReply; - - if (!GetBoolArg("-allowreceivebyip")) - { - pfrom->PushMessage("reply", hashReply, (int)2); - return true; - } - - CWalletTx wtxNew; - vRecv >> wtxNew; - wtxNew.fFromMe = false; - - // Broadcast - if (!wtxNew.AcceptWalletTransaction()) - { - pfrom->PushMessage("reply", hashReply, (int)1); - return error("submitorder AcceptWalletTransaction() failed, returning error 1"); - } - wtxNew.fTimeReceivedIsTxTime = true; - AddToWallet(wtxNew); - wtxNew.RelayWalletTransaction(); - mapReuseKey.erase(pfrom->addr.ip); - - // Send back confirmation - pfrom->PushMessage("reply", hashReply, (int)0); - } - - - else if (strCommand == "reply") - { - uint256 hashReply; - vRecv >> hashReply; - - CRequestTracker tracker; - CRITICAL_BLOCK(pfrom->cs_mapRequests) - { - map::iterator mi = pfrom->mapRequests.find(hashReply); - if (mi != pfrom->mapRequests.end()) - { - tracker = (*mi).second; - pfrom->mapRequests.erase(mi); - } - } - if (!tracker.IsNull()) - tracker.fn(tracker.param1, vRecv); - } - - - else if (strCommand == "ping") - { - } - - - else if (strCommand == "alert") - { - CAlert alert; - vRecv >> alert; - - if (alert.ProcessAlert()) - { - // Relay - pfrom->setKnown.insert(alert.GetHash()); - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - alert.RelayTo(pnode); - } - } - - - else - { - // Ignore unknown commands for extensibility - } - - - // Update the last seen time for this node's address - if (pfrom->fNetworkNode) - if (strCommand == "version" || strCommand == "addr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping") - AddressCurrentlyConnected(pfrom->addr); - - - return true; -} - - - - - - - - - -bool SendMessages(CNode* pto, bool fSendTrickle) -{ - CRITICAL_BLOCK(cs_main) - { - // Don't send anything until we get their version message - if (pto->nVersion == 0) - return true; - - // Keep-alive ping - if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSend.empty()) - pto->PushMessage("ping"); - - // Resend wallet transactions that haven't gotten in a block yet - ResendWalletTransactions(); - - // Address refresh broadcast - static int64 nLastRebroadcast; - if (GetTime() - nLastRebroadcast > 24 * 60 * 60) - { - nLastRebroadcast = GetTime(); - CRITICAL_BLOCK(cs_vNodes) - { - BOOST_FOREACH(CNode* pnode, vNodes) - { - // Periodically clear setAddrKnown to allow refresh broadcasts - pnode->setAddrKnown.clear(); - - // Rebroadcast our address - if (addrLocalHost.IsRoutable() && !fUseProxy) - { - CAddress addr(addrLocalHost); - addr.nTime = GetAdjustedTime(); - pnode->PushAddress(addr); - } - } - } - } - - // Clear out old addresses periodically so it's not too much work at once - static int64 nLastClear; - if (nLastClear == 0) - nLastClear = GetTime(); - if (GetTime() - nLastClear > 10 * 60 && vNodes.size() >= 3) - { - nLastClear = GetTime(); - CRITICAL_BLOCK(cs_mapAddresses) - { - CAddrDB addrdb; - int64 nSince = GetAdjustedTime() - 14 * 24 * 60 * 60; - for (map, CAddress>::iterator mi = mapAddresses.begin(); - mi != mapAddresses.end();) - { - const CAddress& addr = (*mi).second; - if (addr.nTime < nSince) - { - if (mapAddresses.size() < 1000 || GetTime() > nLastClear + 20) - break; - addrdb.EraseAddress(addr); - mapAddresses.erase(mi++); - } - else - mi++; - } - } - } - - - // - // Message: addr - // - if (fSendTrickle) - { - vector vAddr; - vAddr.reserve(pto->vAddrToSend.size()); - BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend) - { - // returns true if wasn't already contained in the set - if (pto->setAddrKnown.insert(addr).second) - { - vAddr.push_back(addr); - // receiver rejects addr messages larger than 1000 - if (vAddr.size() >= 1000) - { - pto->PushMessage("addr", vAddr); - vAddr.clear(); - } - } - } - pto->vAddrToSend.clear(); - if (!vAddr.empty()) - pto->PushMessage("addr", vAddr); - } - - - // - // Message: inventory - // - vector vInv; - vector vInvWait; - CRITICAL_BLOCK(pto->cs_inventory) - { - vInv.reserve(pto->vInventoryToSend.size()); - vInvWait.reserve(pto->vInventoryToSend.size()); - BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) - { - if (pto->setInventoryKnown.count(inv)) - continue; - - // trickle out tx inv to protect privacy - if (inv.type == MSG_TX && !fSendTrickle) - { - // 1/4 of tx invs blast to all immediately - static uint256 hashSalt; - if (hashSalt == 0) - RAND_bytes((unsigned char*)&hashSalt, sizeof(hashSalt)); - uint256 hashRand = inv.hash ^ hashSalt; - hashRand = Hash(BEGIN(hashRand), END(hashRand)); - bool fTrickleWait = ((hashRand & 3) != 0); - - // always trickle our own transactions - if (!fTrickleWait) - { - TRY_CRITICAL_BLOCK(cs_mapWallet) - { - map::iterator mi = mapWallet.find(inv.hash); - if (mi != mapWallet.end()) - { - CWalletTx& wtx = (*mi).second; - if (wtx.fFromMe) - fTrickleWait = true; - } - } - } - - if (fTrickleWait) - { - vInvWait.push_back(inv); - continue; - } - } - - // returns true if wasn't already contained in the set - if (pto->setInventoryKnown.insert(inv).second) - { - vInv.push_back(inv); - if (vInv.size() >= 1000) - { - pto->PushMessage("inv", vInv); - vInv.clear(); - } - } - } - pto->vInventoryToSend = vInvWait; - } - if (!vInv.empty()) - pto->PushMessage("inv", vInv); - - - // - // Message: getdata - // - vector vGetData; - int64 nNow = GetTime() * 1000000; - CTxDB txdb("r"); - while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) - { - const CInv& inv = (*pto->mapAskFor.begin()).second; - if (!AlreadyHave(txdb, inv)) - { - printf("sending getdata: %s\n", inv.ToString().c_str()); - vGetData.push_back(inv); - if (vGetData.size() >= 1000) - { - pto->PushMessage("getdata", vGetData); - vGetData.clear(); - } - } - pto->mapAskFor.erase(pto->mapAskFor.begin()); - } - if (!vGetData.empty()) - pto->PushMessage("getdata", vGetData); - - } - return true; -} - - - - - - - - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// BitcoinMiner -// - -void GenerateBitcoins(bool fGenerate) -{ - if (fGenerateBitcoins != fGenerate) - { - fGenerateBitcoins = fGenerate; - CWalletDB().WriteSetting("fGenerateBitcoins", fGenerateBitcoins); - MainFrameRepaint(); - } - if (fGenerateBitcoins) - { - int nProcessors = boost::thread::hardware_concurrency(); - printf("%d processors\n", nProcessors); - if (nProcessors < 1) - nProcessors = 1; - if (fLimitProcessors && nProcessors > nLimitProcessors) - nProcessors = nLimitProcessors; - int nAddThreads = nProcessors - vnThreadsRunning[3]; - printf("Starting %d BitcoinMiner threads\n", nAddThreads); - for (int i = 0; i < nAddThreads; i++) - { - if (!CreateThread(ThreadBitcoinMiner, NULL)) - printf("Error: CreateThread(ThreadBitcoinMiner) failed\n"); - Sleep(10); - } - } -} - -void ThreadBitcoinMiner(void* parg) -{ - try - { - vnThreadsRunning[3]++; - BitcoinMiner(); - vnThreadsRunning[3]--; - } - catch (std::exception& e) { - vnThreadsRunning[3]--; - PrintException(&e, "ThreadBitcoinMiner()"); - } catch (...) { - vnThreadsRunning[3]--; - PrintException(NULL, "ThreadBitcoinMiner()"); - } - UIThreadCall(boost::bind(CalledSetStatusBar, "", 0)); - nHPSTimerStart = 0; - if (vnThreadsRunning[3] == 0) - dHashesPerSec = 0; - printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[3]); -} - - -int FormatHashBlocks(void* pbuffer, unsigned int len) -{ - unsigned char* pdata = (unsigned char*)pbuffer; - unsigned int blocks = 1 + ((len + 8) / 64); - unsigned char* pend = pdata + 64 * blocks; - memset(pdata + len, 0, 64 * blocks - len); - pdata[len] = 0x80; - unsigned int bits = len * 8; - pend[-1] = (bits >> 0) & 0xff; - pend[-2] = (bits >> 8) & 0xff; - pend[-3] = (bits >> 16) & 0xff; - pend[-4] = (bits >> 24) & 0xff; - return blocks; -} - -using CryptoPP::ByteReverse; - -static const unsigned int pSHA256InitState[8] = -{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; - -inline void SHA256Transform(void* pstate, void* pinput, const void* pinit) -{ - memcpy(pstate, pinit, 32); - CryptoPP::SHA256::Transform((CryptoPP::word32*)pstate, (CryptoPP::word32*)pinput); -} - -// -// ScanHash scans nonces looking for a hash with at least some zero bits. -// It operates on big endian data. Caller does the byte reversing. -// All input buffers are 16-byte aligned. nNonce is usually preserved -// between calls, but periodically or if nNonce is 0xffff0000 or above, -// the block is rebuilt and nNonce starts over at zero. -// -unsigned int ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone) -{ - unsigned int& nNonce = *(unsigned int*)(pdata + 12); - for (;;) - { - // Crypto++ SHA-256 - // Hash pdata using pmidstate as the starting state into - // preformatted buffer phash1, then hash phash1 into phash - nNonce++; - SHA256Transform(phash1, pdata, pmidstate); - SHA256Transform(phash, phash1, pSHA256InitState); - - // Return the nonce if the hash has at least some zero bits, - // caller will check if it has enough to reach the target - if (((unsigned short*)phash)[14] == 0) - return nNonce; - - // If nothing found after trying for a while, return -1 - if ((nNonce & 0xffff) == 0) - { - nHashesDone = 0xffff+1; - return -1; - } - } -} - - -class COrphan -{ -public: - CTransaction* ptx; - set setDependsOn; - double dPriority; - - COrphan(CTransaction* ptxIn) - { - ptx = ptxIn; - dPriority = 0; - } - - void print() const - { - printf("COrphan(hash=%s, dPriority=%.1f)\n", ptx->GetHash().ToString().substr(0,10).c_str(), dPriority); - BOOST_FOREACH(uint256 hash, setDependsOn) - printf(" setDependsOn %s\n", hash.ToString().substr(0,10).c_str()); - } -}; - - -CBlock* CreateNewBlock(CReserveKey& reservekey) -{ - CBlockIndex* pindexPrev = pindexBest; - - // Create new block - auto_ptr pblock(new CBlock()); - if (!pblock.get()) - return NULL; - - // Create coinbase tx - CTransaction txNew; - txNew.vin.resize(1); - txNew.vin[0].prevout.SetNull(); - txNew.vout.resize(1); - txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG; - - // Add our coinbase tx as first transaction - pblock->vtx.push_back(txNew); - - // Collect memory pool transactions into the block - int64 nFees = 0; - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapTransactions) - { - CTxDB txdb("r"); - - // Priority order to process transactions - list vOrphan; // list memory doesn't move - map > mapDependers; - multimap mapPriority; - for (map::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi) - { - CTransaction& tx = (*mi).second; - if (tx.IsCoinBase() || !tx.IsFinal()) - continue; - - COrphan* porphan = NULL; - double dPriority = 0; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - // Read prev transaction - CTransaction txPrev; - CTxIndex txindex; - if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) - { - // Has to wait for dependencies - if (!porphan) - { - // Use list for automatic deletion - vOrphan.push_back(COrphan(&tx)); - porphan = &vOrphan.back(); - } - mapDependers[txin.prevout.hash].push_back(porphan); - porphan->setDependsOn.insert(txin.prevout.hash); - continue; - } - int64 nValueIn = txPrev.vout[txin.prevout.n].nValue; - - // Read block header - int nConf = txindex.GetDepthInMainChain(); - - dPriority += (double)nValueIn * nConf; - - if (fDebug && GetBoolArg("-printpriority")) - printf("priority nValueIn=%-12I64d nConf=%-5d dPriority=%-20.1f\n", nValueIn, nConf, dPriority); - } - - // Priority is sum(valuein * age) / txsize - dPriority /= ::GetSerializeSize(tx, SER_NETWORK); - - if (porphan) - porphan->dPriority = dPriority; - else - mapPriority.insert(make_pair(-dPriority, &(*mi).second)); - - if (fDebug && GetBoolArg("-printpriority")) - { - printf("priority %-20.1f %s\n%s", dPriority, tx.GetHash().ToString().substr(0,10).c_str(), tx.ToString().c_str()); - if (porphan) - porphan->print(); - printf("\n"); - } - } - - // Collect transactions into block - map mapTestPool; - uint64 nBlockSize = 1000; - int nBlockSigOps = 100; - while (!mapPriority.empty()) - { - // Take highest priority transaction off priority queue - double dPriority = -(*mapPriority.begin()).first; - CTransaction& tx = *(*mapPriority.begin()).second; - mapPriority.erase(mapPriority.begin()); - - // Size limits - unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK); - if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN) - continue; - int nTxSigOps = tx.GetSigOpCount(); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) - continue; - - // Transaction fee required depends on block size - bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority)); - int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, true); - - // Connecting shouldn't fail due to dependency on other memory pool transactions - // because we're already processing them in order of dependency - map mapTestPoolTmp(mapTestPool); - if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee)) - continue; - swap(mapTestPool, mapTestPoolTmp); - - // Added - pblock->vtx.push_back(tx); - nBlockSize += nTxSize; - nBlockSigOps += nTxSigOps; - - // Add transactions that depend on this one to the priority queue - uint256 hash = tx.GetHash(); - if (mapDependers.count(hash)) - { - BOOST_FOREACH(COrphan* porphan, mapDependers[hash]) - { - if (!porphan->setDependsOn.empty()) - { - porphan->setDependsOn.erase(hash); - if (porphan->setDependsOn.empty()) - mapPriority.insert(make_pair(-porphan->dPriority, porphan->ptx)); - } - } - } - } - } - pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); - - // Fill in header - pblock->hashPrevBlock = pindexPrev->GetBlockHash(); - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - pblock->nBits = GetNextWorkRequired(pindexPrev); - pblock->nNonce = 0; - - return pblock.release(); -} - - -void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, int64& nPrevTime) -{ - // Update nExtraNonce - int64 nNow = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - if (++nExtraNonce >= 0x7f && nNow > nPrevTime+1) - { - nExtraNonce = 1; - nPrevTime = nNow; - } - pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce); - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); -} - - -void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1) -{ - // - // Prebuild hash buffers - // - struct - { - struct unnamed2 - { - int nVersion; - uint256 hashPrevBlock; - uint256 hashMerkleRoot; - unsigned int nTime; - unsigned int nBits; - unsigned int nNonce; - } - block; - unsigned char pchPadding0[64]; - uint256 hash1; - unsigned char pchPadding1[64]; - } - tmp; - memset(&tmp, 0, sizeof(tmp)); - - tmp.block.nVersion = pblock->nVersion; - tmp.block.hashPrevBlock = pblock->hashPrevBlock; - tmp.block.hashMerkleRoot = pblock->hashMerkleRoot; - tmp.block.nTime = pblock->nTime; - tmp.block.nBits = pblock->nBits; - tmp.block.nNonce = pblock->nNonce; - - FormatHashBlocks(&tmp.block, sizeof(tmp.block)); - FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1)); - - // Byte swap all the input buffer - for (int i = 0; i < sizeof(tmp)/4; i++) - ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]); - - // Precalc the first half of the first hash, which stays constant - SHA256Transform(pmidstate, &tmp.block, pSHA256InitState); - - memcpy(pdata, &tmp.block, 128); - memcpy(phash1, &tmp.hash1, 64); -} - - -bool CheckWork(CBlock* pblock, CReserveKey& reservekey) -{ - uint256 hash = pblock->GetHash(); - uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - - if (hash > hashTarget) - return false; - - //// debug print - printf("BitcoinMiner:\n"); - printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); - pblock->print(); - printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str()); - printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); - - // Found a solution - CRITICAL_BLOCK(cs_main) - { - if (pblock->hashPrevBlock != hashBestChain) - return error("BitcoinMiner : generated block is stale"); - - // Remove key from key pool - reservekey.KeepKey(); - - // Track how many getdata requests this block gets - CRITICAL_BLOCK(cs_mapRequestCount) - mapRequestCount[pblock->GetHash()] = 0; - - // Process this block the same as if we had received it from another node - if (!ProcessBlock(NULL, pblock)) - return error("BitcoinMiner : ProcessBlock, block not accepted"); - } - - Sleep(2000); - return true; -} - - -void BitcoinMiner() -{ - printf("BitcoinMiner started\n"); - SetThreadPriority(THREAD_PRIORITY_LOWEST); - - // Each thread has its own key and counter - CReserveKey reservekey; - unsigned int nExtraNonce = 0; - int64 nPrevTime = 0; - - while (fGenerateBitcoins) - { - if (AffinityBugWorkaround(ThreadBitcoinMiner)) - return; - if (fShutdown) - return; - while (vNodes.empty() || IsInitialBlockDownload()) - { - Sleep(1000); - if (fShutdown) - return; - if (!fGenerateBitcoins) - return; - } - - - // - // Create new block - // - unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrev = pindexBest; - - auto_ptr pblock(CreateNewBlock(reservekey)); - if (!pblock.get()) - return; - IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce, nPrevTime); - - printf("Running BitcoinMiner with %d transactions in block\n", pblock->vtx.size()); - - - // - // Prebuild hash buffers - // - char pmidstatebuf[32+16]; char* pmidstate = alignup<16>(pmidstatebuf); - char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf); - char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf); - - FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1); - - unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4); - unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12); - - - // - // Search - // - int64 nStart = GetTime(); - uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - uint256 hashbuf[2]; - uint256& hash = *alignup<16>(hashbuf); - loop - { - unsigned int nHashesDone = 0; - unsigned int nNonceFound; - - // Crypto++ SHA-256 - nNonceFound = ScanHash_CryptoPP(pmidstate, pdata + 64, phash1, - (char*)&hash, nHashesDone); - - // Check if something found - if (nNonceFound != -1) - { - for (int i = 0; i < sizeof(hash)/4; i++) - ((unsigned int*)&hash)[i] = ByteReverse(((unsigned int*)&hash)[i]); - - if (hash <= hashTarget) - { - // Found a solution - pblock->nNonce = ByteReverse(nNonceFound); - assert(hash == pblock->GetHash()); - - SetThreadPriority(THREAD_PRIORITY_NORMAL); - CheckWork(pblock.get(), reservekey); - SetThreadPriority(THREAD_PRIORITY_LOWEST); - break; - } - } - - // Meter hashes/sec - static int64 nHashCounter; - if (nHPSTimerStart == 0) - { - nHPSTimerStart = GetTimeMillis(); - nHashCounter = 0; - } - else - nHashCounter += nHashesDone; - if (GetTimeMillis() - nHPSTimerStart > 4000) - { - static CCriticalSection cs; - CRITICAL_BLOCK(cs) - { - if (GetTimeMillis() - nHPSTimerStart > 4000) - { - dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart); - nHPSTimerStart = GetTimeMillis(); - nHashCounter = 0; - string strStatus = strprintf(" %.0f khash/s", dHashesPerSec/1000.0); - UIThreadCall(boost::bind(CalledSetStatusBar, strStatus, 0)); - static int64 nLogTime; - if (GetTime() - nLogTime > 30 * 60) - { - nLogTime = GetTime(); - printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str()); - printf("hashmeter %3d CPUs %6.0f khash/s\n", vnThreadsRunning[3], dHashesPerSec/1000.0); - } - } - } - } - - // Check for stop or if block needs to be rebuilt - if (fShutdown) - return; - if (!fGenerateBitcoins) - return; - if (fLimitProcessors && vnThreadsRunning[3] > nLimitProcessors) - return; - if (vNodes.empty()) - break; - if (nBlockNonce >= 0xffff0000) - break; - if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60) - break; - if (pindexPrev != pindexBest) - break; - - // Update nTime every few seconds - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - nBlockTime = ByteReverse(pblock->nTime); - } - } -} - - - - - - - - - - - - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// Actions -// - - -int64 GetBalance() -{ - int64 nStart = GetTimeMillis(); - - int64 nTotal = 0; - CRITICAL_BLOCK(cs_mapWallet) - { - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - CWalletTx* pcoin = &(*it).second; - if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) - continue; - nTotal += pcoin->GetAvailableCredit(); - } - } - - //printf("GetBalance() %"PRI64d"ms\n", GetTimeMillis() - nStart); - return nTotal; -} - - -bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) -{ - setCoinsRet.clear(); - nValueRet = 0; - - // List of values less than target - pair > coinLowestLarger; - coinLowestLarger.first = INT64_MAX; - coinLowestLarger.second.first = NULL; - vector > > vValue; - int64 nTotalLower = 0; - - CRITICAL_BLOCK(cs_mapWallet) - { - vector vCoins; - vCoins.reserve(mapWallet.size()); - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - vCoins.push_back(&(*it).second); - random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); - - BOOST_FOREACH(CWalletTx* pcoin, vCoins) - { - if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) - continue; - - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) - continue; - - int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) - continue; - - for (int i = 0; i < pcoin->vout.size(); i++) - { - if (pcoin->IsSpent(i) || !pcoin->vout[i].IsMine()) - continue; - - int64 n = pcoin->vout[i].nValue; - - if (n <= 0) - continue; - - pair > coin = make_pair(n,make_pair(pcoin,i)); - - if (n == nTargetValue) - { - setCoinsRet.insert(coin.second); - nValueRet += coin.first; - return true; - } - else if (n < nTargetValue + CENT) - { - vValue.push_back(coin); - nTotalLower += n; - } - else if (n < coinLowestLarger.first) - { - coinLowestLarger = coin; - } - } - } - } - - if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT) - { - for (int i = 0; i < vValue.size(); ++i) - { - setCoinsRet.insert(vValue[i].second); - nValueRet += vValue[i].first; - } - return true; - } - - if (nTotalLower < nTargetValue + (coinLowestLarger.second.first ? CENT : 0)) - { - if (coinLowestLarger.second.first == NULL) - return false; - setCoinsRet.insert(coinLowestLarger.second); - nValueRet += coinLowestLarger.first; - return true; - } - - if (nTotalLower >= nTargetValue + CENT) - nTargetValue += CENT; - - // Solve subset sum by stochastic approximation - sort(vValue.rbegin(), vValue.rend()); - vector vfIncluded; - vector vfBest(vValue.size(), true); - int64 nBest = nTotalLower; - - for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++) - { - vfIncluded.assign(vValue.size(), false); - int64 nTotal = 0; - bool fReachedTarget = false; - for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) - { - for (int i = 0; i < vValue.size(); i++) - { - if (nPass == 0 ? rand() % 2 : !vfIncluded[i]) - { - nTotal += vValue[i].first; - vfIncluded[i] = true; - if (nTotal >= nTargetValue) - { - fReachedTarget = true; - if (nTotal < nBest) - { - nBest = nTotal; - vfBest = vfIncluded; - } - nTotal -= vValue[i].first; - vfIncluded[i] = false; - } - } - } - } - } - - // If the next larger is still closer, return it - if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue) - { - setCoinsRet.insert(coinLowestLarger.second); - nValueRet += coinLowestLarger.first; - } - else { - for (int i = 0; i < vValue.size(); i++) - if (vfBest[i]) - { - setCoinsRet.insert(vValue[i].second); - nValueRet += vValue[i].first; - } - - //// debug print - printf("SelectCoins() best subset: "); - for (int i = 0; i < vValue.size(); i++) - if (vfBest[i]) - printf("%s ", FormatMoney(vValue[i].first).c_str()); - printf("total %s\n", FormatMoney(nBest).c_str()); - } - - return true; -} - -bool SelectCoins(int64 nTargetValue, set >& setCoinsRet, int64& nValueRet) -{ - return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet)); -} - - - - -bool CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) -{ - int64 nValue = 0; - BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) - { - if (nValue < 0) - return false; - nValue += s.second; - } - if (vecSend.empty() || nValue < 0) - return false; - - CRITICAL_BLOCK(cs_main) - { - // txdb must be opened before the mapWallet lock - CTxDB txdb("r"); - CRITICAL_BLOCK(cs_mapWallet) - { - nFeeRet = nTransactionFee; - loop - { - wtxNew.vin.clear(); - wtxNew.vout.clear(); - wtxNew.fFromMe = true; - - int64 nTotalValue = nValue + nFeeRet; - double dPriority = 0; - // vouts to the payees - BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) - wtxNew.vout.push_back(CTxOut(s.second, s.first)); - - // Choose coins to use - set > setCoins; - int64 nValueIn = 0; - if (!SelectCoins(nTotalValue, setCoins, nValueIn)) - return false; - BOOST_FOREACH(PAIRTYPE(CWalletTx*, unsigned int) pcoin, setCoins) - { - int64 nCredit = pcoin.first->vout[pcoin.second].nValue; - dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(); - } - - int64 nChange = nValueIn - nValue - nFeeRet; - - // if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE - // or until nChange becomes zero - if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT) - { - int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet); - nChange -= nMoveToFee; - nFeeRet += nMoveToFee; - } - - if (nChange > 0) - { - // Note: We use a new key here to keep it from being obvious which side is the change. - // The drawback is that by not reusing a previous key, the change may be lost if a - // backup is restored, if the backup doesn't have the new private key for the change. - // If we reused the old key, it would be possible to add code to look for and - // rediscover unknown transactions that were written with keys of ours to recover - // post-backup change. - - // Reserve a new key pair from key pool - vector vchPubKey = reservekey.GetReservedKey(); - assert(mapKeys.count(vchPubKey)); - - // Fill a vout to ourself, using same address type as the payment - CScript scriptChange; - if (vecSend[0].first.GetBitcoinAddressHash160() != 0) - scriptChange.SetBitcoinAddress(vchPubKey); - else - scriptChange << vchPubKey << OP_CHECKSIG; - - // Insert change txn at random position: - vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); - wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); - } - else - reservekey.ReturnKey(); - - // Fill vin - BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) - wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); - - // Sign - int nIn = 0; - BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) - if (!SignSignature(*coin.first, wtxNew, nIn++)) - return false; - - // Limit size - unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK); - if (nBytes >= MAX_BLOCK_SIZE_GEN/5) - return false; - dPriority /= nBytes; - - // Check that enough fee is included - int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); - bool fAllowFree = CTransaction::AllowFree(dPriority); - int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree); - if (nFeeRet < max(nPayFee, nMinFee)) - { - nFeeRet = max(nPayFee, nMinFee); - continue; - } - - // Fill vtxPrev by copying from previous transactions vtxPrev - wtxNew.AddSupportingTransactions(txdb); - wtxNew.fTimeReceivedIsTxTime = true; - - break; - } - } - } - return true; -} - -bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) -{ - vector< pair > vecSend; - vecSend.push_back(make_pair(scriptPubKey, nValue)); - return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); -} - -// Call after CreateTransaction unless you want to abort -bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) -{ - CRITICAL_BLOCK(cs_main) - { - printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); - CRITICAL_BLOCK(cs_mapWallet) - { - // This is only to keep the database open to defeat the auto-flush for the - // duration of this scope. This is the only place where this optimization - // maybe makes sense; please don't do it anywhere else. - CWalletDB walletdb("r"); - - // Take key pair from key pool so it won't be used again - reservekey.KeepKey(); - - // Add tx to wallet, because if it has change it's also ours, - // otherwise just for transaction history. - AddToWallet(wtxNew); - - // Mark old coins as spent - set setCoins; - BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) - { - CWalletTx &pcoin = mapWallet[txin.prevout.hash]; - pcoin.MarkSpent(txin.prevout.n); - pcoin.WriteToDisk(); - vWalletUpdated.push_back(pcoin.GetHash()); - } - } - - // Track how many getdata requests our transaction gets - CRITICAL_BLOCK(cs_mapRequestCount) - mapRequestCount[wtxNew.GetHash()] = 0; - - // Broadcast - if (!wtxNew.AcceptToMemoryPool()) - { - // This must not fail. The transaction has already been signed and recorded. - printf("CommitTransaction() : Error: Transaction not valid"); - return false; - } - wtxNew.RelayWalletTransaction(); - } - MainFrameRepaint(); - return true; -} - - - - -// requires cs_main lock -string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) -{ - CReserveKey reservekey; - int64 nFeeRequired; - if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired)) - { - string strError; - if (nValue + nFeeRequired > 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("SendMoney() : %s", strError.c_str()); - return strError; - } - - if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL)) - return "ABORTED"; - - if (!CommitTransaction(wtxNew, reservekey)) - return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); - - MainFrameRepaint(); - return ""; -} - - - -// requires cs_main lock -string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee) -{ - // Check amount - if (nValue <= 0) - return _("Invalid amount"); - if (nValue + nTransactionFee > GetBalance()) - return _("Insufficient funds"); - - // Parse bitcoin address - CScript scriptPubKey; - if (!scriptPubKey.SetBitcoinAddress(strAddress)) - return _("Invalid bitcoin address"); - - return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); -} diff --git a/core/src/net.cpp b/core/src/net.cpp deleted file mode 100644 index 39360a334c..0000000000 --- a/core/src/net.cpp +++ /dev/null @@ -1,1667 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. - -#include "headers.h" - -#ifdef USE_UPNP -#include -#include -#include -#include -#endif - -using namespace std; -using namespace boost; - -static const int MAX_OUTBOUND_CONNECTIONS = 8; - -void ThreadMessageHandler2(void* parg); -void ThreadSocketHandler2(void* parg); -void ThreadOpenConnections2(void* parg); -#ifdef USE_UPNP -void ThreadMapPort2(void* parg); -#endif -bool OpenNetworkConnection(const CAddress& addrConnect); - - - - - -// -// Global state variables -// -bool fClient = false; -bool fAllowDNS = false; -uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); -CAddress addrLocalHost("0.0.0.0", 0, false, nLocalServices); -CNode* pnodeLocalHost = NULL; -uint64 nLocalHostNonce = 0; -array vnThreadsRunning; -SOCKET hListenSocket = INVALID_SOCKET; - -vector vNodes; -CCriticalSection cs_vNodes; -map, CAddress> mapAddresses; -CCriticalSection cs_mapAddresses; -map mapRelay; -deque > vRelayExpiration; -CCriticalSection cs_mapRelay; -map mapAlreadyAskedFor; - -// Settings -int fUseProxy = false; -CAddress addrProxy("127.0.0.1",9050); - - - - -unsigned short GetListenPort() -{ - return (unsigned short)(GetArg("-port", GetDefaultPort())); -} - -void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) -{ - // Filter out duplicate requests - if (pindexBegin == pindexLastGetBlocksBegin && hashEnd == hashLastGetBlocksEnd) - return; - pindexLastGetBlocksBegin = pindexBegin; - hashLastGetBlocksEnd = hashEnd; - - PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); -} - - - - - -bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) -{ - hSocketRet = INVALID_SOCKET; - - SOCKET hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (hSocket == INVALID_SOCKET) - return false; -#ifdef BSD - int set = 1; - setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)); -#endif - - bool fProxy = (fUseProxy && addrConnect.IsRoutable()); - struct sockaddr_in sockaddr = (fProxy ? addrProxy.GetSockAddr() : addrConnect.GetSockAddr()); - - if (connect(hSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) - { - closesocket(hSocket); - return false; - } - - if (fProxy) - { - printf("proxy connecting %s\n", addrConnect.ToString().c_str()); - char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; - memcpy(pszSocks4IP + 2, &addrConnect.port, 2); - memcpy(pszSocks4IP + 4, &addrConnect.ip, 4); - char* pszSocks4 = pszSocks4IP; - int nSize = sizeof(pszSocks4IP); - - int ret = send(hSocket, pszSocks4, nSize, MSG_NOSIGNAL); - if (ret != nSize) - { - closesocket(hSocket); - return error("Error sending to proxy"); - } - char pchRet[8]; - if (recv(hSocket, pchRet, 8, 0) != 8) - { - closesocket(hSocket); - return error("Error reading proxy response"); - } - if (pchRet[1] != 0x5a) - { - closesocket(hSocket); - if (pchRet[1] != 0x5b) - printf("ERROR: Proxy returned error %d\n", pchRet[1]); - return false; - } - printf("proxy connected %s\n", addrConnect.ToString().c_str()); - } - - hSocketRet = hSocket; - return true; -} - -// portDefault is in host order -bool Lookup(const char *pszName, vector& vaddr, int nServices, int nMaxSolutions, bool fAllowLookup, int portDefault, bool fAllowPort) -{ - vaddr.clear(); - if (pszName[0] == 0) - return false; - int port = portDefault; - char psz[256]; - char *pszHost = psz; - strlcpy(psz, pszName, sizeof(psz)); - if (fAllowPort) - { - char* pszColon = strrchr(psz+1,':'); - char *pszPortEnd = NULL; - int portParsed = pszColon ? strtoul(pszColon+1, &pszPortEnd, 10) : 0; - if (pszColon && pszPortEnd && pszPortEnd[0] == 0) - { - if (psz[0] == '[' && pszColon[-1] == ']') - { - // Future: enable IPv6 colon-notation inside [] - pszHost = psz+1; - pszColon[-1] = 0; - } - else - pszColon[0] = 0; - port = portParsed; - if (port < 0 || port > USHRT_MAX) - port = USHRT_MAX; - } - } - - unsigned int addrIP = inet_addr(pszHost); - if (addrIP != INADDR_NONE) - { - // valid IP address passed - vaddr.push_back(CAddress(addrIP, port, nServices)); - return true; - } - - if (!fAllowLookup) - return false; - - struct hostent* phostent = gethostbyname(pszHost); - if (!phostent) - return false; - - if (phostent->h_addrtype != AF_INET) - return false; - - char** ppAddr = phostent->h_addr_list; - while (*ppAddr != NULL && vaddr.size() != nMaxSolutions) - { - CAddress addr(((struct in_addr*)ppAddr[0])->s_addr, port, nServices); - if (addr.IsValid()) - vaddr.push_back(addr); - ppAddr++; - } - - return (vaddr.size() > 0); -} - -// portDefault is in host order -bool Lookup(const char *pszName, CAddress& addr, int nServices, bool fAllowLookup, int portDefault, bool fAllowPort) -{ - vector vaddr; - bool fRet = Lookup(pszName, vaddr, nServices, 1, fAllowLookup, portDefault, fAllowPort); - if (fRet) - addr = vaddr[0]; - return fRet; -} - -bool GetMyExternalIP2(const CAddress& addrConnect, const char* pszGet, const char* pszKeyword, unsigned int& ipRet) -{ - SOCKET hSocket; - if (!ConnectSocket(addrConnect, hSocket)) - return error("GetMyExternalIP() : connection to %s failed", addrConnect.ToString().c_str()); - - send(hSocket, pszGet, strlen(pszGet), MSG_NOSIGNAL); - - string strLine; - while (RecvLine(hSocket, strLine)) - { - if (strLine.empty()) // HTTP response is separated from headers by blank line - { - loop - { - if (!RecvLine(hSocket, strLine)) - { - closesocket(hSocket); - return false; - } - if (pszKeyword == NULL) - break; - if (strLine.find(pszKeyword) != -1) - { - strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword)); - break; - } - } - closesocket(hSocket); - if (strLine.find("<") != -1) - strLine = strLine.substr(0, strLine.find("<")); - strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r")); - while (strLine.size() > 0 && isspace(strLine[strLine.size()-1])) - strLine.resize(strLine.size()-1); - CAddress addr(strLine,0,true); - printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); - if (addr.ip == 0 || addr.ip == INADDR_NONE || !addr.IsRoutable()) - return false; - ipRet = addr.ip; - return true; - } - } - closesocket(hSocket); - return error("GetMyExternalIP() : connection closed"); -} - -// We now get our external IP from the IRC server first and only use this as a backup -bool GetMyExternalIP(unsigned int& ipRet) -{ - CAddress addrConnect; - const char* pszGet; - const char* pszKeyword; - - if (fUseProxy) - return false; - - for (int nLookup = 0; nLookup <= 1; nLookup++) - for (int nHost = 1; nHost <= 2; nHost++) - { - // We should be phasing out our use of sites like these. If we need - // replacements, we should ask for volunteers to put this simple - // php file on their webserver that prints the client IP: - // - if (nHost == 1) - { - addrConnect = CAddress("91.198.22.70",80); // checkip.dyndns.org - - if (nLookup == 1) - { - CAddress addrIP("checkip.dyndns.org", 80, true); - if (addrIP.IsValid()) - addrConnect = addrIP; - } - - pszGet = "GET / HTTP/1.1\r\n" - "Host: checkip.dyndns.org\r\n" - "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" - "Connection: close\r\n" - "\r\n"; - - pszKeyword = "Address:"; - } - else if (nHost == 2) - { - addrConnect = CAddress("74.208.43.192", 80); // www.showmyip.com - - if (nLookup == 1) - { - CAddress addrIP("www.showmyip.com", 80, true); - if (addrIP.IsValid()) - addrConnect = addrIP; - } - - pszGet = "GET /simple/ HTTP/1.1\r\n" - "Host: www.showmyip.com\r\n" - "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" - "Connection: close\r\n" - "\r\n"; - - pszKeyword = NULL; // Returns just IP address - } - - if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet)) - return true; - } - - return false; -} - -void ThreadGetMyExternalIP(void* parg) -{ - // Wait for IRC to get it first - if (!GetBoolArg("-noirc")) - { - for (int i = 0; i < 2 * 60; i++) - { - Sleep(1000); - if (fGotExternalIP || fShutdown) - return; - } - } - - // Fallback in case IRC fails to get it - if (GetMyExternalIP(addrLocalHost.ip)) - { - printf("GetMyExternalIP() returned %s\n", addrLocalHost.ToStringIP().c_str()); - if (addrLocalHost.IsRoutable()) - { - // If we already connected to a few before we had our IP, go back and addr them. - // setAddrKnown automatically filters any duplicate sends. - CAddress addr(addrLocalHost); - addr.nTime = GetAdjustedTime(); - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - pnode->PushAddress(addr); - } - } -} - - - - - -bool AddAddress(CAddress addr, int64 nTimePenalty) -{ - if (!addr.IsRoutable()) - return false; - if (addr.ip == addrLocalHost.ip) - return false; - addr.nTime = max((int64)0, (int64)addr.nTime - nTimePenalty); - CRITICAL_BLOCK(cs_mapAddresses) - { - map, CAddress>::iterator it = mapAddresses.find(addr.GetKey()); - if (it == mapAddresses.end()) - { - // New address - printf("AddAddress(%s)\n", addr.ToString().c_str()); - mapAddresses.insert(make_pair(addr.GetKey(), addr)); - CAddrDB().WriteAddress(addr); - return true; - } - else - { - bool fUpdated = false; - CAddress& addrFound = (*it).second; - if ((addrFound.nServices | addr.nServices) != addrFound.nServices) - { - // Services have been added - addrFound.nServices |= addr.nServices; - fUpdated = true; - } - bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60); - int64 nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); - if (addrFound.nTime < addr.nTime - nUpdateInterval) - { - // Periodically update most recently seen time - addrFound.nTime = addr.nTime; - fUpdated = true; - } - if (fUpdated) - CAddrDB().WriteAddress(addrFound); - } - } - return false; -} - -void AddressCurrentlyConnected(const CAddress& addr) -{ - CRITICAL_BLOCK(cs_mapAddresses) - { - // Only if it's been published already - map, CAddress>::iterator it = mapAddresses.find(addr.GetKey()); - if (it != mapAddresses.end()) - { - CAddress& addrFound = (*it).second; - int64 nUpdateInterval = 20 * 60; - if (addrFound.nTime < GetAdjustedTime() - nUpdateInterval) - { - // Periodically update most recently seen time - addrFound.nTime = GetAdjustedTime(); - CAddrDB addrdb; - addrdb.WriteAddress(addrFound); - } - } - } -} - - - - - -void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) -{ - // If the dialog might get closed before the reply comes back, - // call this in the destructor so it doesn't get called after it's deleted. - CRITICAL_BLOCK(cs_vNodes) - { - BOOST_FOREACH(CNode* pnode, vNodes) - { - CRITICAL_BLOCK(pnode->cs_mapRequests) - { - for (map::iterator mi = pnode->mapRequests.begin(); mi != pnode->mapRequests.end();) - { - CRequestTracker& tracker = (*mi).second; - if (tracker.fn == fn && tracker.param1 == param1) - pnode->mapRequests.erase(mi++); - else - mi++; - } - } - } - } -} - - - - - - - -// -// Subscription methods for the broadcast and subscription system. -// Channel numbers are message numbers, i.e. MSG_TABLE and MSG_PRODUCT. -// -// The subscription system uses a meet-in-the-middle strategy. -// With 100,000 nodes, if senders broadcast to 1000 random nodes and receivers -// subscribe to 1000 random nodes, 99.995% (1 - 0.99^1000) of messages will get through. -// - -bool AnySubscribed(unsigned int nChannel) -{ - if (pnodeLocalHost->IsSubscribed(nChannel)) - return true; - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->IsSubscribed(nChannel)) - return true; - return false; -} - -bool CNode::IsSubscribed(unsigned int nChannel) -{ - if (nChannel >= vfSubscribe.size()) - return false; - return vfSubscribe[nChannel]; -} - -void CNode::Subscribe(unsigned int nChannel, unsigned int nHops) -{ - if (nChannel >= vfSubscribe.size()) - return; - - if (!AnySubscribed(nChannel)) - { - // Relay subscribe - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode != this) - pnode->PushMessage("subscribe", nChannel, nHops); - } - - vfSubscribe[nChannel] = true; -} - -void CNode::CancelSubscribe(unsigned int nChannel) -{ - if (nChannel >= vfSubscribe.size()) - return; - - // Prevent from relaying cancel if wasn't subscribed - if (!vfSubscribe[nChannel]) - return; - vfSubscribe[nChannel] = false; - - if (!AnySubscribed(nChannel)) - { - // Relay subscription cancel - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode != this) - pnode->PushMessage("sub-cancel", nChannel); - } -} - - - - - - - - - -CNode* FindNode(unsigned int ip) -{ - CRITICAL_BLOCK(cs_vNodes) - { - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->addr.ip == ip) - return (pnode); - } - return NULL; -} - -CNode* FindNode(CAddress addr) -{ - CRITICAL_BLOCK(cs_vNodes) - { - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->addr == addr) - return (pnode); - } - return NULL; -} - -CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) -{ - if (addrConnect.ip == addrLocalHost.ip) - return NULL; - - // Look for an existing connection - CNode* pnode = FindNode(addrConnect.ip); - if (pnode) - { - if (nTimeout != 0) - pnode->AddRef(nTimeout); - else - pnode->AddRef(); - return pnode; - } - - /// debug print - printf("trying connection %s lastseen=%.1fhrs lasttry=%.1fhrs\n", - addrConnect.ToString().c_str(), - (double)(addrConnect.nTime - GetAdjustedTime())/3600.0, - (double)(addrConnect.nLastTry - GetAdjustedTime())/3600.0); - - CRITICAL_BLOCK(cs_mapAddresses) - mapAddresses[addrConnect.GetKey()].nLastTry = GetAdjustedTime(); - - // Connect - SOCKET hSocket; - if (ConnectSocket(addrConnect, hSocket)) - { - /// debug print - printf("connected %s\n", addrConnect.ToString().c_str()); - - // Set to nonblocking -#ifdef __WXMSW__ - u_long nOne = 1; - if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) - printf("ConnectSocket() : ioctlsocket nonblocking setting failed, error %d\n", WSAGetLastError()); -#else - if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) - printf("ConnectSocket() : fcntl nonblocking setting failed, error %d\n", errno); -#endif - - // Add node - CNode* pnode = new CNode(hSocket, addrConnect, false); - if (nTimeout != 0) - pnode->AddRef(nTimeout); - else - pnode->AddRef(); - CRITICAL_BLOCK(cs_vNodes) - vNodes.push_back(pnode); - - pnode->nTimeConnected = GetTime(); - return pnode; - } - else - { - return NULL; - } -} - -void CNode::CloseSocketDisconnect() -{ - fDisconnect = true; - if (hSocket != INVALID_SOCKET) - { - if (fDebug) - printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); - printf("disconnecting node %s\n", addr.ToString().c_str()); - closesocket(hSocket); - hSocket = INVALID_SOCKET; - } -} - -void CNode::Cleanup() -{ - // All of a nodes broadcasts and subscriptions are automatically torn down - // when it goes down, so a node has to stay up to keep its broadcast going. - - // Cancel subscriptions - for (unsigned int nChannel = 0; nChannel < vfSubscribe.size(); nChannel++) - if (vfSubscribe[nChannel]) - CancelSubscribe(nChannel); -} - - - - - - - - - - - - - -void ThreadSocketHandler(void* parg) -{ - IMPLEMENT_RANDOMIZE_STACK(ThreadSocketHandler(parg)); - try - { - vnThreadsRunning[0]++; - ThreadSocketHandler2(parg); - vnThreadsRunning[0]--; - } - catch (std::exception& e) { - vnThreadsRunning[0]--; - PrintException(&e, "ThreadSocketHandler()"); - } catch (...) { - vnThreadsRunning[0]--; - throw; // support pthread_cancel() - } - printf("ThreadSocketHandler exiting\n"); -} - -void ThreadSocketHandler2(void* parg) -{ - printf("ThreadSocketHandler started\n"); - list vNodesDisconnected; - int nPrevNodeCount = 0; - - loop - { - // - // Disconnect nodes - // - CRITICAL_BLOCK(cs_vNodes) - { - // Disconnect unused nodes - vector vNodesCopy = vNodes; - BOOST_FOREACH(CNode* pnode, vNodesCopy) - { - if (pnode->fDisconnect || - (pnode->GetRefCount() <= 0 && pnode->vRecv.empty() && pnode->vSend.empty())) - { - // remove from vNodes - vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); - - // close socket and cleanup - pnode->CloseSocketDisconnect(); - pnode->Cleanup(); - - // hold in disconnected pool until all refs are released - pnode->nReleaseTime = max(pnode->nReleaseTime, GetTime() + 15 * 60); - if (pnode->fNetworkNode || pnode->fInbound) - pnode->Release(); - vNodesDisconnected.push_back(pnode); - } - } - - // Delete disconnected nodes - list vNodesDisconnectedCopy = vNodesDisconnected; - BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy) - { - // wait until threads are done using it - if (pnode->GetRefCount() <= 0) - { - bool fDelete = false; - TRY_CRITICAL_BLOCK(pnode->cs_vSend) - TRY_CRITICAL_BLOCK(pnode->cs_vRecv) - TRY_CRITICAL_BLOCK(pnode->cs_mapRequests) - TRY_CRITICAL_BLOCK(pnode->cs_inventory) - fDelete = true; - if (fDelete) - { - vNodesDisconnected.remove(pnode); - delete pnode; - } - } - } - } - if (vNodes.size() != nPrevNodeCount) - { - nPrevNodeCount = vNodes.size(); - MainFrameRepaint(); - } - - - // - // Find which sockets have data to receive - // - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 50000; // frequency to poll pnode->vSend - - fd_set fdsetRecv; - fd_set fdsetSend; - fd_set fdsetError; - FD_ZERO(&fdsetRecv); - FD_ZERO(&fdsetSend); - FD_ZERO(&fdsetError); - SOCKET hSocketMax = 0; - - if(hListenSocket != INVALID_SOCKET) - FD_SET(hListenSocket, &fdsetRecv); - hSocketMax = max(hSocketMax, hListenSocket); - CRITICAL_BLOCK(cs_vNodes) - { - BOOST_FOREACH(CNode* pnode, vNodes) - { - if (pnode->hSocket == INVALID_SOCKET || pnode->hSocket < 0) - continue; - FD_SET(pnode->hSocket, &fdsetRecv); - FD_SET(pnode->hSocket, &fdsetError); - hSocketMax = max(hSocketMax, pnode->hSocket); - TRY_CRITICAL_BLOCK(pnode->cs_vSend) - if (!pnode->vSend.empty()) - FD_SET(pnode->hSocket, &fdsetSend); - } - } - - vnThreadsRunning[0]--; - int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); - vnThreadsRunning[0]++; - if (fShutdown) - return; - if (nSelect == SOCKET_ERROR) - { - int nErr = WSAGetLastError(); - printf("socket select error %d\n", nErr); - for (int i = 0; i <= hSocketMax; i++) - FD_SET(i, &fdsetRecv); - FD_ZERO(&fdsetSend); - FD_ZERO(&fdsetError); - Sleep(timeout.tv_usec/1000); - } - - - // - // Accept new connections - // - if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) - { - struct sockaddr_in sockaddr; - socklen_t len = sizeof(sockaddr); - SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); - CAddress addr(sockaddr); - int nInbound = 0; - - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->fInbound) - nInbound++; - if (hSocket == INVALID_SOCKET) - { - if (WSAGetLastError() != WSAEWOULDBLOCK) - printf("socket error accept failed: %d\n", WSAGetLastError()); - } - else if (nInbound >= GetArg("-maxconnections", 125) - MAX_OUTBOUND_CONNECTIONS) - { - closesocket(hSocket); - } - else - { - printf("accepted connection %s\n", addr.ToString().c_str()); - CNode* pnode = new CNode(hSocket, addr, true); - pnode->AddRef(); - CRITICAL_BLOCK(cs_vNodes) - vNodes.push_back(pnode); - } - } - - - // - // Service each socket - // - vector vNodesCopy; - CRITICAL_BLOCK(cs_vNodes) - { - vNodesCopy = vNodes; - BOOST_FOREACH(CNode* pnode, vNodesCopy) - pnode->AddRef(); - } - BOOST_FOREACH(CNode* pnode, vNodesCopy) - { - if (fShutdown) - return; - - // - // Receive - // - if (pnode->hSocket == INVALID_SOCKET) - continue; - if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) - { - TRY_CRITICAL_BLOCK(pnode->cs_vRecv) - { - CDataStream& vRecv = pnode->vRecv; - unsigned int nPos = vRecv.size(); - - if (nPos > 1000*GetArg("-maxreceivebuffer", 10*1000)) { - if (!pnode->fDisconnect) - printf("socket recv flood control disconnect (%d bytes)\n", vRecv.size()); - pnode->CloseSocketDisconnect(); - } - else { - // typical socket buffer is 8K-64K - char pchBuf[0x10000]; - int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); - if (nBytes > 0) - { - vRecv.resize(nPos + nBytes); - memcpy(&vRecv[nPos], pchBuf, nBytes); - pnode->nLastRecv = GetTime(); - } - else if (nBytes == 0) - { - // socket closed gracefully - if (!pnode->fDisconnect) - printf("socket closed\n"); - pnode->CloseSocketDisconnect(); - } - else if (nBytes < 0) - { - // error - int nErr = WSAGetLastError(); - if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) - { - if (!pnode->fDisconnect) - printf("socket recv error %d\n", nErr); - pnode->CloseSocketDisconnect(); - } - } - } - } - } - - // - // Send - // - if (pnode->hSocket == INVALID_SOCKET) - continue; - if (FD_ISSET(pnode->hSocket, &fdsetSend)) - { - TRY_CRITICAL_BLOCK(pnode->cs_vSend) - { - CDataStream& vSend = pnode->vSend; - if (!vSend.empty()) - { - int nBytes = send(pnode->hSocket, &vSend[0], vSend.size(), MSG_NOSIGNAL | MSG_DONTWAIT); - if (nBytes > 0) - { - vSend.erase(vSend.begin(), vSend.begin() + nBytes); - pnode->nLastSend = GetTime(); - } - else if (nBytes < 0) - { - // error - int nErr = WSAGetLastError(); - if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) - { - printf("socket send error %d\n", nErr); - pnode->CloseSocketDisconnect(); - } - } - if (vSend.size() > 1000*GetArg("-maxsendbuffer", 10*1000)) { - if (!pnode->fDisconnect) - printf("socket send flood control disconnect (%d bytes)\n", vSend.size()); - pnode->CloseSocketDisconnect(); - } - } - } - } - - // - // Inactivity checking - // - if (pnode->vSend.empty()) - pnode->nLastSendEmpty = GetTime(); - if (GetTime() - pnode->nTimeConnected > 60) - { - if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) - { - printf("socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0); - pnode->fDisconnect = true; - } - else if (GetTime() - pnode->nLastSend > 90*60 && GetTime() - pnode->nLastSendEmpty > 90*60) - { - printf("socket not sending\n"); - pnode->fDisconnect = true; - } - else if (GetTime() - pnode->nLastRecv > 90*60) - { - printf("socket inactivity timeout\n"); - pnode->fDisconnect = true; - } - } - } - CRITICAL_BLOCK(cs_vNodes) - { - BOOST_FOREACH(CNode* pnode, vNodesCopy) - pnode->Release(); - } - - Sleep(10); - } -} - - - - - - - - - -#ifdef USE_UPNP -void ThreadMapPort(void* parg) -{ - IMPLEMENT_RANDOMIZE_STACK(ThreadMapPort(parg)); - try - { - vnThreadsRunning[5]++; - ThreadMapPort2(parg); - vnThreadsRunning[5]--; - } - catch (std::exception& e) { - vnThreadsRunning[5]--; - PrintException(&e, "ThreadMapPort()"); - } catch (...) { - vnThreadsRunning[5]--; - PrintException(NULL, "ThreadMapPort()"); - } - printf("ThreadMapPort exiting\n"); -} - -void ThreadMapPort2(void* parg) -{ - printf("ThreadMapPort started\n"); - - char port[6]; - sprintf(port, "%d", GetListenPort()); - - const char * rootdescurl = 0; - const char * multicastif = 0; - const char * minissdpdpath = 0; - struct UPNPDev * devlist = 0; - char lanaddr[64]; - - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); - - struct UPNPUrls urls; - struct IGDdatas data; - int r; - - r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); - if (r == 1) - { - char intClient[16]; - char intPort[6]; - -#ifndef __WXMSW__ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port, port, lanaddr, 0, "TCP", 0); -#else - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port, port, lanaddr, 0, "TCP", 0, "0"); -#endif - if(r!=UPNPCOMMAND_SUCCESS) - printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", - port, port, lanaddr, r, strupnperror(r)); - else - printf("UPnP Port Mapping successful.\n"); - loop { - if (fShutdown || !fUseUPnP) - { - r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port, "TCP", 0); - printf("UPNP_DeletePortMapping() returned : %d\n", r); - freeUPNPDevlist(devlist); devlist = 0; - FreeUPNPUrls(&urls); - return; - } - Sleep(2000); - } - } else { - printf("No valid UPnP IGDs found\n"); - freeUPNPDevlist(devlist); devlist = 0; - if (r != 0) - FreeUPNPUrls(&urls); - loop { - if (fShutdown || !fUseUPnP) - return; - Sleep(2000); - } - } -} - -void MapPort(bool fMapPort) -{ - if (fUseUPnP != fMapPort) - { - fUseUPnP = fMapPort; - CWalletDB().WriteSetting("fUseUPnP", fUseUPnP); - } - if (fUseUPnP && vnThreadsRunning[5] < 1) - { - if (!CreateThread(ThreadMapPort, NULL)) - printf("Error: ThreadMapPort(ThreadMapPort) failed\n"); - } -} -#endif - - - - - - - - - - -static const char *strDNSSeed[] = { - "bitseed.xf2.org", - "bitseed.bitcoin.org.uk", -}; - -void DNSAddressSeed() -{ - int found = 0; - - printf("Loading addresses from DNS seeds (could take a while)\n"); - - for (int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) { - vector vaddr; - if (Lookup(strDNSSeed[seed_idx], vaddr, NODE_NETWORK, -1, true)) - { - BOOST_FOREACH (CAddress& addr, vaddr) - { - if (addr.GetByte(3) != 127) - { - addr.nTime = 0; - AddAddress(addr); - found++; - } - } - } - } - - printf("%d addresses found from DNS seeds\n", found); -} - - - -unsigned int pnSeed[] = -{ - 0x1ddb1032, 0x6242ce40, 0x52d6a445, 0x2dd7a445, 0x8a53cd47, 0x73263750, 0xda23c257, 0xecd4ed57, - 0x0a40ec59, 0x75dce160, 0x7df76791, 0x89370bad, 0xa4f214ad, 0x767700ae, 0x638b0418, 0x868a1018, - 0xcd9f332e, 0x0129653e, 0xcc92dc3e, 0x96671640, 0x56487e40, 0x5b66f440, 0xb1d01f41, 0xf1dc6041, - 0xc1d12b42, 0x86ba1243, 0x6be4df43, 0x6d4cef43, 0xd18e0644, 0x1ab0b344, 0x6584a345, 0xe7c1a445, - 0x58cea445, 0xc5daa445, 0x21dda445, 0x3d3b5346, 0x13e55347, 0x1080d24a, 0x8e611e4b, 0x81518e4b, - 0x6c839e4b, 0xe2ad0a4c, 0xfbbc0a4c, 0x7f5b6e4c, 0x7244224e, 0x1300554e, 0x20690652, 0x5a48b652, - 0x75c5c752, 0x4335cc54, 0x340fd154, 0x87c07455, 0x087b2b56, 0x8a133a57, 0xac23c257, 0x70374959, - 0xfb63d45b, 0xb9a1685c, 0x180d765c, 0x674f645d, 0x04d3495e, 0x1de44b5e, 0x4ee8a362, 0x0ded1b63, - 0xc1b04b6d, 0x8d921581, 0x97b7ea82, 0x1cf83a8e, 0x91490bad, 0x09dc75ae, 0x9a6d79ae, 0xa26d79ae, - 0x0fd08fae, 0x0f3e3fb2, 0x4f944fb2, 0xcca448b8, 0x3ecd6ab8, 0xa9d5a5bc, 0x8d0119c1, 0x045997d5, - 0xca019dd9, 0x0d526c4d, 0xabf1ba44, 0x66b1ab55, 0x1165f462, 0x3ed7cbad, 0xa38fae6e, 0x3bd2cbad, - 0xd36f0547, 0x20df7840, 0x7a337742, 0x549f8e4b, 0x9062365c, 0xd399f562, 0x2b5274a1, 0x8edfa153, - 0x3bffb347, 0x7074bf58, 0xb74fcbad, 0x5b5a795b, 0x02fa29ce, 0x5a6738d4, 0xe8a1d23e, 0xef98c445, - 0x4b0f494c, 0xa2bc1e56, 0x7694ad63, 0xa4a800c3, 0x05fda6cd, 0x9f22175e, 0x364a795b, 0x536285d5, - 0xac44c9d4, 0x0b06254d, 0x150c2fd4, 0x32a50dcc, 0xfd79ce48, 0xf15cfa53, 0x66c01e60, 0x6bc26661, - 0xc03b47ae, 0x4dda1b81, 0x3285a4c1, 0x883ca96d, 0x35d60a4c, 0xdae09744, 0x2e314d61, 0x84e247cf, - 0x6c814552, 0x3a1cc658, 0x98d8f382, 0xe584cb5b, 0x15e86057, 0x7b01504e, 0xd852dd48, 0x56382f56, - 0x0a5df454, 0xa0d18d18, 0x2e89b148, 0xa79c114c, 0xcbdcd054, 0x5523bc43, 0xa9832640, 0x8a066144, - 0x3894c3bc, 0xab76bf58, 0x6a018ac1, 0xfebf4f43, 0x2f26c658, 0x31102f4e, 0x85e929d5, 0x2a1c175e, - 0xfc6c2cd1, 0x27b04b6d, 0xdf024650, 0x161748b8, 0x28be6580, 0x57be6580, 0x1cee677a, 0xaa6bb742, - 0x9a53964b, 0x0a5a2d4d, 0x2434c658, 0x9a494f57, 0x1ebb0e48, 0xf610b85d, 0x077ecf44, 0x085128bc, - 0x5ba17a18, 0x27ca1b42, 0xf8a00b56, 0xfcd4c257, 0xcf2fc15e, 0xd897e052, 0x4cada04f, 0x2f35f6d5, - 0x382ce8c9, 0xe523984b, 0x3f946846, 0x60c8be43, 0x41da6257, 0xde0be142, 0xae8a544b, 0xeff0c254, - 0x1e0f795b, 0xaeb28890, 0xca16acd9, 0x1e47ddd8, 0x8c8c4829, 0xd27dc747, 0xd53b1663, 0x4096b163, - 0x9c8dd958, 0xcb12f860, 0x9e79305c, 0x40c1a445, 0x4a90c2bc, 0x2c3a464d, 0x2727f23c, 0x30b04b6d, - 0x59024cb8, 0xa091e6ad, 0x31b04b6d, 0xc29d46a6, 0x63934fb2, 0xd9224dbe, 0x9f5910d8, 0x7f530a6b, - 0x752e9c95, 0x65453548, 0xa484be46, 0xce5a1b59, 0x710e0718, 0x46a13d18, 0xdaaf5318, 0xc4a8ff53, - 0x87abaa52, 0xb764cf51, 0xb2025d4a, 0x6d351e41, 0xc035c33e, 0xa432c162, 0x61ef34ae, 0xd16fddbc, - 0x0870e8c1, 0x3070e8c1, 0x9c71e8c1, 0xa4992363, 0x85a1f663, 0x4184e559, 0x18d96ed8, 0x17b8dbd5, - 0x60e7cd18, 0xe5ee104c, 0xab17ac62, 0x1e786e1b, 0x5d23b762, 0xf2388fae, 0x88270360, 0x9e5b3d80, - 0x7da518b2, 0xb5613b45, 0x1ad41f3e, 0xd550854a, 0x8617e9a9, 0x925b229c, 0xf2e92542, 0x47af0544, - 0x73b5a843, 0xb9b7a0ad, 0x03a748d0, 0x0a6ff862, 0x6694df62, 0x3bfac948, 0x8e098f4f, 0x746916c3, - 0x02f38e4f, 0x40bb1243, 0x6a54d162, 0x6008414b, 0xa513794c, 0x514aa343, 0x63781747, 0xdbb6795b, - 0xed065058, 0x42d24b46, 0x1518794c, 0x9b271681, 0x73e4ffad, 0x0654784f, 0x438dc945, 0x641846a6, - 0x2d1b0944, 0x94b59148, 0x8d369558, 0xa5a97662, 0x8b705b42, 0xce9204ae, 0x8d584450, 0x2df61555, - 0xeebff943, 0x2e75fb4d, 0x3ef8fc57, 0x9921135e, 0x8e31042e, 0xb5afad43, 0x89ecedd1, 0x9cfcc047, - 0x8fcd0f4c, 0xbe49f5ad, 0x146a8d45, 0x98669ab8, 0x98d9175e, 0xd1a8e46d, 0x839a3ab8, 0x40a0016c, - 0x6d27c257, 0x977fffad, 0x7baa5d5d, 0x1213be43, 0xb167e5a9, 0x640fe8ca, 0xbc9ea655, 0x0f820a4c, - 0x0f097059, 0x69ac957c, 0x366d8453, 0xb1ba2844, 0x8857f081, 0x70b5be63, 0xc545454b, 0xaf36ded1, - 0xb5a4b052, 0x21f062d1, 0x72ab89b2, 0x74a45318, 0x8312e6bc, 0xb916965f, 0x8aa7c858, 0xfe7effad, -}; - - - -void ThreadOpenConnections(void* parg) -{ - IMPLEMENT_RANDOMIZE_STACK(ThreadOpenConnections(parg)); - try - { - vnThreadsRunning[1]++; - ThreadOpenConnections2(parg); - vnThreadsRunning[1]--; - } - catch (std::exception& e) { - vnThreadsRunning[1]--; - PrintException(&e, "ThreadOpenConnections()"); - } catch (...) { - vnThreadsRunning[1]--; - PrintException(NULL, "ThreadOpenConnections()"); - } - printf("ThreadOpenConnections exiting\n"); -} - -void ThreadOpenConnections2(void* parg) -{ - printf("ThreadOpenConnections started\n"); - - // Connect to specific addresses - if (mapArgs.count("-connect")) - { - for (int64 nLoop = 0;; nLoop++) - { - BOOST_FOREACH(string strAddr, mapMultiArgs["-connect"]) - { - CAddress addr(strAddr, fAllowDNS); - if (addr.IsValid()) - OpenNetworkConnection(addr); - for (int i = 0; i < 10 && i < nLoop; i++) - { - Sleep(500); - if (fShutdown) - return; - } - } - } - } - - // Connect to manually added nodes first - if (mapArgs.count("-addnode")) - { - BOOST_FOREACH(string strAddr, mapMultiArgs["-addnode"]) - { - CAddress addr(strAddr, fAllowDNS); - if (addr.IsValid()) - { - OpenNetworkConnection(addr); - Sleep(500); - if (fShutdown) - return; - } - } - } - - // Initiate network connections - int64 nStart = GetTime(); - loop - { - // Limit outbound connections - vnThreadsRunning[1]--; - Sleep(500); - loop - { - int nOutbound = 0; - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (!pnode->fInbound) - nOutbound++; - int nMaxOutboundConnections = MAX_OUTBOUND_CONNECTIONS; - nMaxOutboundConnections = min(nMaxOutboundConnections, (int)GetArg("-maxconnections", 125)); - if (nOutbound < nMaxOutboundConnections) - break; - Sleep(2000); - if (fShutdown) - return; - } - vnThreadsRunning[1]++; - if (fShutdown) - return; - - CRITICAL_BLOCK(cs_mapAddresses) - { - // Add seed nodes if IRC isn't working - static bool fSeedUsed; - bool fTOR = (fUseProxy && addrProxy.port == htons(9050)); - if (mapAddresses.empty() && (GetTime() - nStart > 60 || fTOR) && !fTestNet) - { - for (int i = 0; i < ARRAYLEN(pnSeed); i++) - { - // It'll only connect to one or two seed nodes because once it connects, - // it'll get a pile of addresses with newer timestamps. - CAddress addr; - addr.ip = pnSeed[i]; - addr.nTime = 0; - AddAddress(addr); - } - fSeedUsed = true; - } - - if (fSeedUsed && mapAddresses.size() > ARRAYLEN(pnSeed) + 100) - { - // Disconnect seed nodes - set setSeed(pnSeed, pnSeed + ARRAYLEN(pnSeed)); - static int64 nSeedDisconnected; - if (nSeedDisconnected == 0) - { - nSeedDisconnected = GetTime(); - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (setSeed.count(pnode->addr.ip)) - pnode->fDisconnect = true; - } - - // Keep setting timestamps to 0 so they won't reconnect - if (GetTime() - nSeedDisconnected < 60 * 60) - { - BOOST_FOREACH(PAIRTYPE(const vector, CAddress)& item, mapAddresses) - { - if (setSeed.count(item.second.ip) && item.second.nTime != 0) - { - item.second.nTime = 0; - CAddrDB().WriteAddress(item.second); - } - } - } - } - } - - - // - // Choose an address to connect to based on most recently seen - // - CAddress addrConnect; - int64 nBest = INT64_MIN; - - // Only connect to one address per a.b.?.? range. - // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. - set setConnected; - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - setConnected.insert(pnode->addr.ip & 0x0000ffff); - - CRITICAL_BLOCK(cs_mapAddresses) - { - BOOST_FOREACH(const PAIRTYPE(vector, CAddress)& item, mapAddresses) - { - const CAddress& addr = item.second; - if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.ip & 0x0000ffff)) - continue; - int64 nSinceLastSeen = GetAdjustedTime() - addr.nTime; - int64 nSinceLastTry = GetAdjustedTime() - addr.nLastTry; - - // Randomize the order in a deterministic way, putting the standard port first - int64 nRandomizer = (uint64)(nStart * 4951 + addr.nLastTry * 9567851 + addr.ip * 7789) % (2 * 60 * 60); - if (addr.port != htons(GetDefaultPort())) - nRandomizer += 2 * 60 * 60; - - // Last seen Base retry frequency - // <1 hour 10 min - // 1 hour 1 hour - // 4 hours 2 hours - // 24 hours 5 hours - // 48 hours 7 hours - // 7 days 13 hours - // 30 days 27 hours - // 90 days 46 hours - // 365 days 93 hours - int64 nDelay = (int64)(3600.0 * sqrt(fabs((double)nSinceLastSeen) / 3600.0) + nRandomizer); - - // Fast reconnect for one hour after last seen - if (nSinceLastSeen < 60 * 60) - nDelay = 10 * 60; - - // Limit retry frequency - if (nSinceLastTry < nDelay) - continue; - - // If we have IRC, we'll be notified when they first come online, - // and again every 24 hours by the refresh broadcast. - if (nGotIRCAddresses > 0 && vNodes.size() >= 2 && nSinceLastSeen > 24 * 60 * 60) - continue; - - // Only try the old stuff if we don't have enough connections - if (vNodes.size() >= 8 && nSinceLastSeen > 24 * 60 * 60) - continue; - - // If multiple addresses are ready, prioritize by time since - // last seen and time since last tried. - int64 nScore = min(nSinceLastTry, (int64)24 * 60 * 60) - nSinceLastSeen - nRandomizer; - if (nScore > nBest) - { - nBest = nScore; - addrConnect = addr; - } - } - } - - if (addrConnect.IsValid()) - OpenNetworkConnection(addrConnect); - } -} - -bool OpenNetworkConnection(const CAddress& addrConnect) -{ - // - // Initiate outbound network connection - // - if (fShutdown) - return false; - if (addrConnect.ip == addrLocalHost.ip || !addrConnect.IsIPv4() || FindNode(addrConnect.ip)) - return false; - - vnThreadsRunning[1]--; - CNode* pnode = ConnectNode(addrConnect); - vnThreadsRunning[1]++; - if (fShutdown) - return false; - if (!pnode) - return false; - pnode->fNetworkNode = true; - - return true; -} - - - - - - - - -void ThreadMessageHandler(void* parg) -{ - IMPLEMENT_RANDOMIZE_STACK(ThreadMessageHandler(parg)); - try - { - vnThreadsRunning[2]++; - ThreadMessageHandler2(parg); - vnThreadsRunning[2]--; - } - catch (std::exception& e) { - vnThreadsRunning[2]--; - PrintException(&e, "ThreadMessageHandler()"); - } catch (...) { - vnThreadsRunning[2]--; - PrintException(NULL, "ThreadMessageHandler()"); - } - printf("ThreadMessageHandler exiting\n"); -} - -void ThreadMessageHandler2(void* parg) -{ - printf("ThreadMessageHandler started\n"); - SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); - while (!fShutdown) - { - vector vNodesCopy; - CRITICAL_BLOCK(cs_vNodes) - { - vNodesCopy = vNodes; - BOOST_FOREACH(CNode* pnode, vNodesCopy) - pnode->AddRef(); - } - - // Poll the connected nodes for messages - CNode* pnodeTrickle = NULL; - if (!vNodesCopy.empty()) - pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())]; - BOOST_FOREACH(CNode* pnode, vNodesCopy) - { - // Receive messages - TRY_CRITICAL_BLOCK(pnode->cs_vRecv) - ProcessMessages(pnode); - if (fShutdown) - return; - - // Send messages - TRY_CRITICAL_BLOCK(pnode->cs_vSend) - SendMessages(pnode, pnode == pnodeTrickle); - if (fShutdown) - return; - } - - CRITICAL_BLOCK(cs_vNodes) - { - BOOST_FOREACH(CNode* pnode, vNodesCopy) - pnode->Release(); - } - - // Wait and allow messages to bunch up. - // Reduce vnThreadsRunning so StopNode has permission to exit while - // we're sleeping, but we must always check fShutdown after doing this. - vnThreadsRunning[2]--; - Sleep(100); - if (fRequestShutdown) - Shutdown(NULL); - vnThreadsRunning[2]++; - if (fShutdown) - return; - } -} - - - - - - -bool BindListenPort(string& strError) -{ - strError = ""; - int nOne = 1; - addrLocalHost.port = htons(GetListenPort()); - -#ifdef __WXMSW__ - // Initialize Windows Sockets - WSADATA wsadata; - int ret = WSAStartup(MAKEWORD(2,2), &wsadata); - if (ret != NO_ERROR) - { - strError = strprintf("Error: TCP/IP socket library failed to start (WSAStartup returned error %d)", ret); - printf("%s\n", strError.c_str()); - return false; - } -#endif - - // Create socket for listening for incoming connections - hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (hListenSocket == INVALID_SOCKET) - { - strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); - return false; - } - -#ifdef BSD - // Different way of disabling SIGPIPE on BSD - setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int)); -#endif - -#ifndef __WXMSW__ - // Allow binding if the port is still in TIME_WAIT state after - // the program was closed and restarted. Not an issue on windows. - setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); -#endif - -#ifdef __WXMSW__ - // Set to nonblocking, incoming connections will also inherit this - if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR) -#else - if (fcntl(hListenSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) -#endif - { - strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); - return false; - } - - // The sockaddr_in structure specifies the address family, - // IP address, and port for the socket that is being bound - struct sockaddr_in sockaddr; - memset(&sockaddr, 0, sizeof(sockaddr)); - sockaddr.sin_family = AF_INET; - sockaddr.sin_addr.s_addr = INADDR_ANY; // bind to all IPs on this computer - sockaddr.sin_port = htons(GetListenPort()); - if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) - { - int nErr = WSAGetLastError(); - if (nErr == WSAEADDRINUSE) - strError = strprintf(_("Unable to bind to port %d on this computer. Bitcoin is probably already running."), ntohs(sockaddr.sin_port)); - else - strError = strprintf("Error: Unable to bind to port %d on this computer (bind returned error %d)", ntohs(sockaddr.sin_port), nErr); - printf("%s\n", strError.c_str()); - return false; - } - printf("Bound to port %d\n", ntohs(sockaddr.sin_port)); - - // Listen for incoming connections - if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) - { - strError = strprintf("Error: Listening for incoming connections failed (listen returned error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); - return false; - } - - return true; -} - -void StartNode(void* parg) -{ - if (pnodeLocalHost == NULL) - pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress("127.0.0.1", 0, false, nLocalServices)); - -#ifdef __WXMSW__ - // Get local host ip - char pszHostName[1000] = ""; - if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) - { - vector vaddr; - if (Lookup(pszHostName, vaddr, nLocalServices, -1, true)) - BOOST_FOREACH (const CAddress &addr, vaddr) - if (addr.GetByte(3) != 127) - { - addrLocalHost = addr; - break; - } - } -#else - // Get local host ip - struct ifaddrs* myaddrs; - if (getifaddrs(&myaddrs) == 0) - { - for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) - { - if (ifa->ifa_addr == NULL) continue; - if ((ifa->ifa_flags & IFF_UP) == 0) continue; - if (strcmp(ifa->ifa_name, "lo") == 0) continue; - if (strcmp(ifa->ifa_name, "lo0") == 0) continue; - char pszIP[100]; - if (ifa->ifa_addr->sa_family == AF_INET) - { - struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr); - if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s4->sin_addr), pszIP, sizeof(pszIP)) != NULL) - printf("ipv4 %s: %s\n", ifa->ifa_name, pszIP); - - // Take the first IP that isn't loopback 127.x.x.x - CAddress addr(*(unsigned int*)&s4->sin_addr, GetListenPort(), nLocalServices); - if (addr.IsValid() && addr.GetByte(3) != 127) - { - addrLocalHost = addr; - break; - } - } - else if (ifa->ifa_addr->sa_family == AF_INET6) - { - struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr); - if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s6->sin6_addr), pszIP, sizeof(pszIP)) != NULL) - printf("ipv6 %s: %s\n", ifa->ifa_name, pszIP); - } - } - freeifaddrs(myaddrs); - } -#endif - printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); - - if (fUseProxy || mapArgs.count("-connect") || fNoListen) - { - // Proxies can't take incoming connections - addrLocalHost.ip = CAddress("0.0.0.0").ip; - printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); - } - else - { - CreateThread(ThreadGetMyExternalIP, NULL); - } - - // - // Start threads - // - - // Map ports with UPnP - if (fHaveUPnP) - MapPort(fUseUPnP); - - // Get addresses from IRC and advertise ours - if (!CreateThread(ThreadIRCSeed, NULL)) - printf("Error: CreateThread(ThreadIRCSeed) failed\n"); - - // Send and receive from sockets, accept connections - pthread_t hThreadSocketHandler = CreateThread(ThreadSocketHandler, NULL, true); - - // Initiate outbound connections - if (!CreateThread(ThreadOpenConnections, NULL)) - printf("Error: CreateThread(ThreadOpenConnections) failed\n"); - - // Process messages - if (!CreateThread(ThreadMessageHandler, NULL)) - printf("Error: CreateThread(ThreadMessageHandler) failed\n"); - - // Generate coins in the background - GenerateBitcoins(fGenerateBitcoins); -} - -bool StopNode() -{ - printf("StopNode()\n"); - fShutdown = true; - nTransactionsUpdated++; - int64 nStart = GetTime(); - while (vnThreadsRunning[0] > 0 || vnThreadsRunning[2] > 0 || vnThreadsRunning[3] > 0 || vnThreadsRunning[4] > 0 -#ifdef USE_UPNP - || vnThreadsRunning[5] > 0 -#endif - ) - { - if (GetTime() - nStart > 20) - break; - Sleep(20); - } - if (vnThreadsRunning[0] > 0) printf("ThreadSocketHandler still running\n"); - if (vnThreadsRunning[1] > 0) printf("ThreadOpenConnections still running\n"); - if (vnThreadsRunning[2] > 0) printf("ThreadMessageHandler still running\n"); - if (vnThreadsRunning[3] > 0) printf("ThreadBitcoinMiner still running\n"); - if (vnThreadsRunning[4] > 0) printf("ThreadRPCServer still running\n"); - if (fHaveUPnP && vnThreadsRunning[5] > 0) printf("ThreadMapPort still running\n"); - while (vnThreadsRunning[2] > 0 || vnThreadsRunning[4] > 0) - Sleep(20); - Sleep(50); - - return true; -} - -class CNetCleanup -{ -public: - CNetCleanup() - { - } - ~CNetCleanup() - { - // Close sockets - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->hSocket != INVALID_SOCKET) - closesocket(pnode->hSocket); - if (hListenSocket != INVALID_SOCKET) - if (closesocket(hListenSocket) == SOCKET_ERROR) - printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError()); - -#ifdef __WXMSW__ - // Shutdown Windows Sockets - WSACleanup(); -#endif - } -} -instance_of_cnetcleanup; diff --git a/core/src/rpc.cpp b/core/src/rpc.cpp deleted file mode 100644 index 530bef4a43..0000000000 --- a/core/src/rpc.cpp +++ /dev/null @@ -1,2209 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. - -#include "headers.h" -#include "cryptopp/sha.h" -#undef printf -#include -#include -#include -#ifdef USE_SSL -#include -typedef boost::asio::ssl::stream SSLStream; -#endif -#include "json/json_spirit_reader_template.h" -#include "json/json_spirit_writer_template.h" -#include "json/json_spirit_utils.h" -#define printf OutputDebugStringF -// MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are -// precompiled in headers.h. The problem might be when the pch file goes over -// a certain size around 145MB. If we need access to json_spirit outside this -// file, we could use the compiled json_spirit option. - -using namespace std; -using namespace boost; -using namespace boost::asio; -using namespace json_spirit; - -void ThreadRPCServer2(void* parg); -typedef Value(*rpcfn_type)(const Array& params, bool fHelp); -extern map mapCallTable; - - -Object JSONRPCError(int code, const string& message) -{ - Object error; - error.push_back(Pair("code", code)); - error.push_back(Pair("message", message)); - return error; -} - - -void PrintConsole(const char* format, ...) -{ - char buffer[50000]; - int limit = sizeof(buffer); - va_list arg_ptr; - va_start(arg_ptr, format); - int ret = _vsnprintf(buffer, limit, format, arg_ptr); - va_end(arg_ptr); - if (ret < 0 || ret >= limit) - { - ret = limit - 1; - buffer[limit-1] = 0; - } - printf("%s", buffer); -#if defined(__WXMSW__) && defined(GUI) - MyMessageBox(buffer, "Bitcoin", wxOK | wxICON_EXCLAMATION); -#else - fprintf(stdout, "%s", buffer); -#endif -} - - -int64 AmountFromValue(const Value& value) -{ - double dAmount = value.get_real(); - if (dAmount <= 0.0 || dAmount > 21000000.0) - throw JSONRPCError(-3, "Invalid amount"); - int64 nAmount = roundint64(dAmount * COIN); - if (!MoneyRange(nAmount)) - throw JSONRPCError(-3, "Invalid amount"); - return nAmount; -} - -Value ValueFromAmount(int64 amount) -{ - return (double)amount / (double)COIN; -} - -void WalletTxToJSON(const CWalletTx& wtx, Object& entry) -{ - entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain())); - entry.push_back(Pair("txid", wtx.GetHash().GetHex())); - entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); - BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) - entry.push_back(Pair(item.first, item.second)); -} - -string AccountFromValue(const Value& value) -{ - string strAccount = value.get_str(); - if (strAccount == "*") - throw JSONRPCError(-11, "Invalid account name"); - return strAccount; -} - - - -/// -/// Note: This interface may still be subject to change. -/// - - -Value help(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "help [command]\n" - "List commands, or get help for a command."); - - string strCommand; - if (params.size() > 0) - strCommand = params[0].get_str(); - - string strRet; - set setDone; - for (map::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi) - { - string strMethod = (*mi).first; - // We already filter duplicates, but these deprecated screw up the sort order - if (strMethod == "getamountreceived" || - strMethod == "getallreceived" || - (strMethod.find("label") != string::npos)) - continue; - if (strCommand != "" && strMethod != strCommand) - continue; - try - { - Array params; - rpcfn_type pfn = (*mi).second; - if (setDone.insert(pfn).second) - (*pfn)(params, true); - } - catch (std::exception& e) - { - // Help text is returned in an exception - string strHelp = string(e.what()); - if (strCommand == "") - if (strHelp.find('\n') != -1) - strHelp = strHelp.substr(0, strHelp.find('\n')); - strRet += strHelp + "\n"; - } - } - if (strRet == "") - strRet = strprintf("help: unknown command: %s\n", strCommand.c_str()); - strRet = strRet.substr(0,strRet.size()-1); - return strRet; -} - - -Value stop(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "stop\n" - "Stop bitcoin server."); - - // Shutdown will take long enough that the response should get back - CreateThread(Shutdown, NULL); - return "bitcoin server stopping"; -} - - -Value getblockcount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblockcount\n" - "Returns the number of blocks in the longest block chain."); - - return nBestHeight; -} - - -Value getblocknumber(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblocknumber\n" - "Returns the block number of the latest block in the longest block chain."); - - return nBestHeight; -} - - -Value getconnectioncount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getconnectioncount\n" - "Returns the number of connections to other nodes."); - - return (int)vNodes.size(); -} - - -double GetDifficulty() -{ - // Floating point number that is a multiple of the minimum difficulty, - // minimum difficulty = 1.0. - - if (pindexBest == NULL) - return 1.0; - int nShift = (pindexBest->nBits >> 24) & 0xff; - - double dDiff = - (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff); - - while (nShift < 29) - { - dDiff *= 256.0; - nShift++; - } - while (nShift > 29) - { - dDiff /= 256.0; - nShift--; - } - - return dDiff; -} - -Value getdifficulty(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getdifficulty\n" - "Returns the proof-of-work difficulty as a multiple of the minimum difficulty."); - - return GetDifficulty(); -} - - -Value getgenerate(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getgenerate\n" - "Returns true or false."); - - return (bool)fGenerateBitcoins; -} - - -Value setgenerate(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "setgenerate [genproclimit]\n" - " is true or false to turn generation on or off.\n" - "Generation is limited to [genproclimit] processors, -1 is unlimited."); - - bool fGenerate = true; - if (params.size() > 0) - fGenerate = params[0].get_bool(); - - if (params.size() > 1) - { - int nGenProcLimit = params[1].get_int(); - fLimitProcessors = (nGenProcLimit != -1); - CWalletDB().WriteSetting("fLimitProcessors", fLimitProcessors); - if (nGenProcLimit != -1) - CWalletDB().WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit); - if (nGenProcLimit == 0) - fGenerate = false; - } - - GenerateBitcoins(fGenerate); - return Value::null; -} - - -Value gethashespersec(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "gethashespersec\n" - "Returns a recent hashes per second performance measurement while generating."); - - if (GetTimeMillis() - nHPSTimerStart > 8000) - return (boost::int64_t)0; - return (boost::int64_t)dHashesPerSec; -} - - -Value getinfo(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getinfo\n" - "Returns an object containing various state info."); - - Object obj; - obj.push_back(Pair("version", (int)VERSION)); - obj.push_back(Pair("balance", ValueFromAmount(GetBalance()))); - obj.push_back(Pair("blocks", (int)nBestHeight)); - obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string()))); - obj.push_back(Pair("generate", (bool)fGenerateBitcoins)); - obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1))); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("hashespersec", gethashespersec(params, false))); - obj.push_back(Pair("testnet", fTestNet)); - obj.push_back(Pair("keypoololdest", (boost::int64_t)GetOldestKeyPoolTime())); - obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - return obj; -} - - -Value getnewaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getnewaddress [account]\n" - "Returns a new bitcoin address for receiving payments. " - "If [account] is specified (recommended), it is added to the address book " - "so payments received with the address will be credited to [account]."); - - // Parse the account first so we don't generate a key if there's an error - string strAccount; - if (params.size() > 0) - strAccount = AccountFromValue(params[0]); - - // Generate a new key that is added to wallet - string strAddress = PubKeyToAddress(GetKeyFromKeyPool()); - - SetAddressBookName(strAddress, strAccount); - return strAddress; -} - - -// requires cs_main, cs_mapWallet locks -string GetAccountAddress(string strAccount, bool bForceNew=false) -{ - string strAddress; - - CWalletDB walletdb; - walletdb.TxnBegin(); - - CAccount account; - walletdb.ReadAccount(strAccount, account); - - // Check if the current key has been used - if (!account.vchPubKey.empty()) - { - CScript scriptPubKey; - scriptPubKey.SetBitcoinAddress(account.vchPubKey); - for (map::iterator it = mapWallet.begin(); - it != mapWallet.end() && !account.vchPubKey.empty(); - ++it) - { - const CWalletTx& wtx = (*it).second; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - account.vchPubKey.clear(); - } - } - - // Generate a new key - if (account.vchPubKey.empty() || bForceNew) - { - account.vchPubKey = GetKeyFromKeyPool(); - string strAddress = PubKeyToAddress(account.vchPubKey); - SetAddressBookName(strAddress, strAccount); - walletdb.WriteAccount(strAccount, account); - } - - walletdb.TxnCommit(); - strAddress = PubKeyToAddress(account.vchPubKey); - - return strAddress; -} - -Value getaccountaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaccountaddress \n" - "Returns the current bitcoin address for receiving payments to this account."); - - // Parse the account first so we don't generate a key if there's an error - string strAccount = AccountFromValue(params[0]); - - Value ret; - - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - { - ret = GetAccountAddress(strAccount); - } - - return ret; -} - - - -Value setaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "setaccount \n" - "Sets the account associated with the given address."); - - string strAddress = params[0].get_str(); - uint160 hash160; - bool isValid = AddressToHash160(strAddress, hash160); - if (!isValid) - throw JSONRPCError(-5, "Invalid bitcoin address"); - - - string strAccount; - if (params.size() > 1) - strAccount = AccountFromValue(params[1]); - - // Detect when changing the account of an address that is the 'unused current key' of another account: - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - CRITICAL_BLOCK(cs_mapAddressBook) - { - if (mapAddressBook.count(strAddress)) - { - string strOldAccount = mapAddressBook[strAddress]; - if (strAddress == GetAccountAddress(strOldAccount)) - GetAccountAddress(strOldAccount, true); - } - } - - SetAddressBookName(strAddress, strAccount); - return Value::null; -} - - -Value getaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaccount \n" - "Returns the account associated with the given address."); - - string strAddress = params[0].get_str(); - - string strAccount; - CRITICAL_BLOCK(cs_mapAddressBook) - { - map::iterator mi = mapAddressBook.find(strAddress); - if (mi != mapAddressBook.end() && !(*mi).second.empty()) - strAccount = (*mi).second; - } - return strAccount; -} - - -Value getaddressesbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaddressesbyaccount \n" - "Returns the list of addresses for the given account."); - - string strAccount = AccountFromValue(params[0]); - - // Find all addresses that have the given account - Array ret; - CRITICAL_BLOCK(cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook) - { - const string& strAddress = item.first; - const string& strName = item.second; - if (strName == strAccount) - { - // We're only adding valid bitcoin addresses and not ip addresses - CScript scriptPubKey; - if (scriptPubKey.SetBitcoinAddress(strAddress)) - ret.push_back(strAddress); - } - } - } - return ret; -} - -Value settxfee(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 1) - throw runtime_error( - "settxfee \n" - " is a real and is rounded to the nearest 0.00000001"); - - // Amount - int64 nAmount = 0; - if (params[0].get_real() != 0.0) - nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts - - nTransactionFee = nAmount; - return true; -} - -Value sendtoaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 4) - throw runtime_error( - "sendtoaddress [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001"); - - string strAddress = params[0].get_str(); - - // Amount - int64 nAmount = AmountFromValue(params[1]); - - // Wallet comments - CWalletTx wtx; - if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty()) - wtx.mapValue["comment"] = params[2].get_str(); - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) - wtx.mapValue["to"] = params[3].get_str(); - - CRITICAL_BLOCK(cs_main) - { - string strError = SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); - if (strError != "") - throw JSONRPCError(-4, strError); - } - - return wtx.GetHash().GetHex(); -} - - -Value getreceivedbyaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getreceivedbyaddress [minconf=1]\n" - "Returns the total amount received by in transactions with at least [minconf] confirmations."); - - // Bitcoin address - string strAddress = params[0].get_str(); - CScript scriptPubKey; - if (!scriptPubKey.SetBitcoinAddress(strAddress)) - throw JSONRPCError(-5, "Invalid bitcoin address"); - if (!IsMine(scriptPubKey)) - return (double)0.0; - - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - // Tally - int64 nAmount = 0; - CRITICAL_BLOCK(cs_mapWallet) - { - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; - } - } - - return ValueFromAmount(nAmount); -} - - -void GetAccountPubKeys(string strAccount, set& setPubKey) -{ - CRITICAL_BLOCK(cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook) - { - const string& strAddress = item.first; - const string& strName = item.second; - if (strName == strAccount) - { - // We're only counting our own valid bitcoin addresses and not ip addresses - CScript scriptPubKey; - if (scriptPubKey.SetBitcoinAddress(strAddress)) - if (IsMine(scriptPubKey)) - setPubKey.insert(scriptPubKey); - } - } - } -} - - -Value getreceivedbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getreceivedbyaccount [minconf=1]\n" - "Returns the total amount received by addresses with in transactions with at least [minconf] confirmations."); - - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - // Get the set of pub keys that have the label - string strAccount = AccountFromValue(params[0]); - set setPubKey; - GetAccountPubKeys(strAccount, setPubKey); - - // Tally - int64 nAmount = 0; - CRITICAL_BLOCK(cs_mapWallet) - { - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (setPubKey.count(txout.scriptPubKey)) - if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; - } - } - - return (double)nAmount / (double)COIN; -} - - -int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) -{ - int64 nBalance = 0; - CRITICAL_BLOCK(cs_mapWallet) - { - // Tally wallet transactions - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!wtx.IsFinal()) - continue; - - int64 nGenerated, nReceived, nSent, nFee; - wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee); - - if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) - nBalance += nReceived; - nBalance += nGenerated - nSent - nFee; - } - - // Tally internal accounting entries - nBalance += walletdb.GetAccountCreditDebit(strAccount); - } - - return nBalance; -} - -int64 GetAccountBalance(const string& strAccount, int nMinDepth) -{ - CWalletDB walletdb; - return GetAccountBalance(walletdb, strAccount, nMinDepth); -} - - -Value getbalance(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 0 || params.size() > 2) - throw runtime_error( - "getbalance [account] [minconf=1]\n" - "If [account] is not specified, returns the server's total available balance.\n" - "If [account] is specified, returns the balance in the account."); - - if (params.size() == 0) - return ValueFromAmount(GetBalance()); - - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - if (params[0].get_str() == "*") { - // Calculate total balance a different way from GetBalance() - // (GetBalance() sums up all unspent TxOuts) - // getbalance and getbalance '*' should always return the same number. - int64 nBalance = 0; - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!wtx.IsFinal()) - continue; - - int64 allGeneratedImmature, allGeneratedMature, allFee; - allGeneratedImmature = allGeneratedMature = allFee = 0; - string strSentAccount; - list > listReceived; - list > listSent; - wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); - if (wtx.GetDepthInMainChain() >= nMinDepth) - BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) - nBalance += r.second; - BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listSent) - nBalance -= r.second; - nBalance -= allFee; - nBalance += allGeneratedMature; - } - return ValueFromAmount(nBalance); - } - - string strAccount = AccountFromValue(params[0]); - - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - - return ValueFromAmount(nBalance); -} - - -Value movecmd(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 3 || params.size() > 5) - throw runtime_error( - "move [minconf=1] [comment]\n" - "Move from one account in your wallet to another."); - - string strFrom = AccountFromValue(params[0]); - string strTo = AccountFromValue(params[1]); - int64 nAmount = AmountFromValue(params[2]); - int nMinDepth = 1; - if (params.size() > 3) - nMinDepth = params[3].get_int(); - string strComment; - if (params.size() > 4) - strComment = params[4].get_str(); - - CRITICAL_BLOCK(cs_mapWallet) - { - CWalletDB walletdb; - walletdb.TxnBegin(); - - int64 nNow = GetAdjustedTime(); - - // Debit - CAccountingEntry debit; - debit.strAccount = strFrom; - debit.nCreditDebit = -nAmount; - debit.nTime = nNow; - debit.strOtherAccount = strTo; - debit.strComment = strComment; - walletdb.WriteAccountingEntry(debit); - - // Credit - CAccountingEntry credit; - credit.strAccount = strTo; - credit.nCreditDebit = nAmount; - credit.nTime = nNow; - credit.strOtherAccount = strFrom; - credit.strComment = strComment; - walletdb.WriteAccountingEntry(credit); - - walletdb.TxnCommit(); - } - return true; -} - - -Value sendfrom(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 3 || params.size() > 6) - throw runtime_error( - "sendfrom [minconf=1] [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001"); - - string strAccount = AccountFromValue(params[0]); - string strAddress = params[1].get_str(); - int64 nAmount = AmountFromValue(params[2]); - int nMinDepth = 1; - if (params.size() > 3) - nMinDepth = params[3].get_int(); - - CWalletTx wtx; - wtx.strFromAccount = strAccount; - if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) - wtx.mapValue["comment"] = params[4].get_str(); - if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) - wtx.mapValue["to"] = params[5].get_str(); - - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - { - // Check funds - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - if (nAmount > nBalance) - throw JSONRPCError(-6, "Account has insufficient funds"); - - // Send - string strError = SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); - if (strError != "") - throw JSONRPCError(-4, strError); - } - - return wtx.GetHash().GetHex(); -} - -Value sendmany(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 4) - throw runtime_error( - "sendmany {address:amount,...} [minconf=1] [comment]\n" - "amounts are double-precision floating point numbers"); - - string strAccount = AccountFromValue(params[0]); - Object sendTo = params[1].get_obj(); - int nMinDepth = 1; - if (params.size() > 2) - nMinDepth = params[2].get_int(); - - CWalletTx wtx; - wtx.strFromAccount = strAccount; - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) - wtx.mapValue["comment"] = params[3].get_str(); - - set setAddress; - vector > vecSend; - - int64 totalAmount = 0; - BOOST_FOREACH(const Pair& s, sendTo) - { - uint160 hash160; - string strAddress = s.name_; - - if (setAddress.count(strAddress)) - throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+strAddress); - setAddress.insert(strAddress); - - CScript scriptPubKey; - if (!scriptPubKey.SetBitcoinAddress(strAddress)) - throw JSONRPCError(-5, string("Invalid bitcoin address:")+strAddress); - int64 nAmount = AmountFromValue(s.value_); - totalAmount += nAmount; - - vecSend.push_back(make_pair(scriptPubKey, nAmount)); - } - - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - { - // Check funds - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - if (totalAmount > nBalance) - throw JSONRPCError(-6, "Account has insufficient funds"); - - // Send - CReserveKey keyChange; - int64 nFeeRequired = 0; - bool fCreated = CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); - if (!fCreated) - { - if (totalAmount + nFeeRequired > GetBalance()) - throw JSONRPCError(-6, "Insufficient funds"); - throw JSONRPCError(-4, "Transaction creation failed"); - } - if (!CommitTransaction(wtx, keyChange)) - throw JSONRPCError(-4, "Transaction commit failed"); - } - - return wtx.GetHash().GetHex(); -} - - -struct tallyitem -{ - int64 nAmount; - int nConf; - tallyitem() - { - nAmount = 0; - nConf = INT_MAX; - } -}; - -Value ListReceived(const Array& params, bool fByAccounts) -{ - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].get_int(); - - // Whether to include empty accounts - bool fIncludeEmpty = false; - if (params.size() > 1) - fIncludeEmpty = params[1].get_bool(); - - // Tally - map mapTally; - CRITICAL_BLOCK(cs_mapWallet) - { - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; - - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < nMinDepth) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - // Only counting our own bitcoin addresses and not ip addresses - uint160 hash160 = txout.scriptPubKey.GetBitcoinAddressHash160(); - if (hash160 == 0 || !mapPubKeys.count(hash160)) // IsMine - continue; - - tallyitem& item = mapTally[hash160]; - item.nAmount += txout.nValue; - item.nConf = min(item.nConf, nDepth); - } - } - } - - // Reply - Array ret; - map mapAccountTally; - CRITICAL_BLOCK(cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook) - { - const string& strAddress = item.first; - const string& strAccount = item.second; - uint160 hash160; - if (!AddressToHash160(strAddress, hash160)) - continue; - map::iterator it = mapTally.find(hash160); - if (it == mapTally.end() && !fIncludeEmpty) - continue; - - int64 nAmount = 0; - int nConf = INT_MAX; - if (it != mapTally.end()) - { - nAmount = (*it).second.nAmount; - nConf = (*it).second.nConf; - } - - if (fByAccounts) - { - tallyitem& item = mapAccountTally[strAccount]; - item.nAmount += nAmount; - item.nConf = min(item.nConf, nConf); - } - else - { - Object obj; - obj.push_back(Pair("address", strAddress)); - obj.push_back(Pair("account", strAccount)); - obj.push_back(Pair("label", strAccount)); // deprecated - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); - ret.push_back(obj); - } - } - } - - if (fByAccounts) - { - for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) - { - int64 nAmount = (*it).second.nAmount; - int nConf = (*it).second.nConf; - Object obj; - obj.push_back(Pair("account", (*it).first)); - obj.push_back(Pair("label", (*it).first)); // deprecated - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); - ret.push_back(obj); - } - } - - return ret; -} - -Value listreceivedbyaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "listreceivedbyaddress [minconf=1] [includeempty=false]\n" - "[minconf] is the minimum number of confirmations before payments are included.\n" - "[includeempty] whether to include addresses that haven't received any payments.\n" - "Returns an array of objects containing:\n" - " \"address\" : receiving address\n" - " \"account\" : the account of the receiving address\n" - " \"amount\" : total amount received by the address\n" - " \"confirmations\" : number of confirmations of the most recent transaction included"); - - return ListReceived(params, false); -} - -Value listreceivedbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "listreceivedbyaccount [minconf=1] [includeempty=false]\n" - "[minconf] is the minimum number of confirmations before payments are included.\n" - "[includeempty] whether to include accounts that haven't received any payments.\n" - "Returns an array of objects containing:\n" - " \"account\" : the account of the receiving addresses\n" - " \"amount\" : total amount received by addresses with this account\n" - " \"confirmations\" : number of confirmations of the most recent transaction included"); - - return ListReceived(params, true); -} - -void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret) -{ - int64 nGeneratedImmature, nGeneratedMature, nFee; - string strSentAccount; - list > listReceived; - list > listSent; - wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); - - bool fAllAccounts = (strAccount == string("*")); - - // Generated blocks assigned to account "" - if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == "")) - { - Object entry; - entry.push_back(Pair("account", string(""))); - if (nGeneratedImmature) - { - entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan")); - entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature))); - } - else - { - entry.push_back(Pair("category", "generate")); - entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature))); - } - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } - - // Sent - if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) - { - BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) - { - Object entry; - entry.push_back(Pair("account", strSentAccount)); - entry.push_back(Pair("address", s.first)); - entry.push_back(Pair("category", "send")); - entry.push_back(Pair("amount", ValueFromAmount(-s.second))); - entry.push_back(Pair("fee", ValueFromAmount(-nFee))); - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } - } - - // Received - if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) - CRITICAL_BLOCK(cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) - { - string account; - if (mapAddressBook.count(r.first)) - account = mapAddressBook[r.first]; - if (fAllAccounts || (account == strAccount)) - { - Object entry; - entry.push_back(Pair("account", account)); - entry.push_back(Pair("address", r.first)); - entry.push_back(Pair("category", "receive")); - entry.push_back(Pair("amount", ValueFromAmount(r.second))); - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } - } - } - -} - -void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) -{ - bool fAllAccounts = (strAccount == string("*")); - - if (fAllAccounts || acentry.strAccount == strAccount) - { - Object entry; - entry.push_back(Pair("account", acentry.strAccount)); - entry.push_back(Pair("category", "move")); - entry.push_back(Pair("time", (boost::int64_t)acentry.nTime)); - entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); - entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); - entry.push_back(Pair("comment", acentry.strComment)); - ret.push_back(entry); - } -} - -Value listtransactions(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 3) - throw runtime_error( - "listtransactions [account] [count=10] [from=0]\n" - "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account]."); - - string strAccount = "*"; - if (params.size() > 0) - strAccount = params[0].get_str(); - int nCount = 10; - if (params.size() > 1) - nCount = params[1].get_int(); - int nFrom = 0; - if (params.size() > 2) - nFrom = params[2].get_int(); - - Array ret; - CWalletDB walletdb; - - CRITICAL_BLOCK(cs_mapWallet) - { - // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap: - typedef pair TxPair; - typedef multimap TxItems; - TxItems txByTime; - - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - CWalletTx* wtx = &((*it).second); - txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0))); - } - list acentries; - walletdb.ListAccountCreditDebit(strAccount, acentries); - BOOST_FOREACH(CAccountingEntry& entry, acentries) - { - txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); - } - - // Now: iterate backwards until we have nCount items to return: - TxItems::reverse_iterator it = txByTime.rbegin(); - for (std::advance(it, nFrom); it != txByTime.rend(); ++it) - { - CWalletTx *const pwtx = (*it).second.first; - if (pwtx != 0) - ListTransactions(*pwtx, strAccount, 0, true, ret); - CAccountingEntry *const pacentry = (*it).second.second; - if (pacentry != 0) - AcentryToJSON(*pacentry, strAccount, ret); - - if (ret.size() >= nCount) break; - } - // ret is now newest to oldest - } - - // Make sure we return only last nCount items (sends-to-self might give us an extra): - if (ret.size() > nCount) - { - Array::iterator last = ret.begin(); - std::advance(last, nCount); - ret.erase(last, ret.end()); - } - std::reverse(ret.begin(), ret.end()); // oldest to newest - - return ret; -} - -Value listaccounts(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "listaccounts [minconf=1]\n" - "Returns Object that has account names as keys, account balances as values."); - - int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].get_int(); - - map mapAccountBalances; - CRITICAL_BLOCK(cs_mapWallet) - CRITICAL_BLOCK(cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, string)& entry, mapAddressBook) { - uint160 hash160; - if(AddressToHash160(entry.first, hash160) && mapPubKeys.count(hash160)) // This address belongs to me - mapAccountBalances[entry.second] = 0; - } - - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - int64 nGeneratedImmature, nGeneratedMature, nFee; - string strSentAccount; - list > listReceived; - list > listSent; - wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); - mapAccountBalances[strSentAccount] -= nFee; - BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) - mapAccountBalances[strSentAccount] -= s.second; - if (wtx.GetDepthInMainChain() >= nMinDepth) - { - mapAccountBalances[""] += nGeneratedMature; - BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) - if (mapAddressBook.count(r.first)) - mapAccountBalances[mapAddressBook[r.first]] += r.second; - else - mapAccountBalances[""] += r.second; - } - } - } - - list acentries; - CWalletDB().ListAccountCreditDebit("*", acentries); - BOOST_FOREACH(const CAccountingEntry& entry, acentries) - mapAccountBalances[entry.strAccount] += entry.nCreditDebit; - - Object ret; - BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) { - ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); - } - return ret; -} - -Value gettransaction(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "gettransaction \n" - "Get detailed information about "); - - uint256 hash; - hash.SetHex(params[0].get_str()); - - Object entry; - CRITICAL_BLOCK(cs_mapWallet) - { - if (!mapWallet.count(hash)) - throw JSONRPCError(-5, "Invalid or non-wallet transaction id"); - const CWalletTx& wtx = mapWallet[hash]; - - int64 nCredit = wtx.GetCredit(); - int64 nDebit = wtx.GetDebit(); - int64 nNet = nCredit - nDebit; - int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); - - entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); - if (wtx.IsFromMe()) - entry.push_back(Pair("fee", ValueFromAmount(nFee))); - - WalletTxToJSON(mapWallet[hash], entry); - - Array details; - ListTransactions(mapWallet[hash], "*", 0, false, details); - entry.push_back(Pair("details", details)); - } - - return entry; -} - - -Value backupwallet(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "backupwallet \n" - "Safely copies wallet.dat to destination, which can be a directory or a path with filename."); - - string strDest = params[0].get_str(); - BackupWallet(strDest); - - return Value::null; -} - - -Value validateaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "validateaddress \n" - "Return information about ."); - - string strAddress = params[0].get_str(); - uint160 hash160; - bool isValid = AddressToHash160(strAddress, hash160); - - Object ret; - ret.push_back(Pair("isvalid", isValid)); - if (isValid) - { - // Call Hash160ToAddress() so we always return current ADDRESSVERSION - // version of the address: - string currentAddress = Hash160ToAddress(hash160); - ret.push_back(Pair("address", currentAddress)); - ret.push_back(Pair("ismine", (mapPubKeys.count(hash160) > 0))); - CRITICAL_BLOCK(cs_mapAddressBook) - { - if (mapAddressBook.count(currentAddress)) - ret.push_back(Pair("account", mapAddressBook[currentAddress])); - } - } - return ret; -} - - -Value getwork(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getwork [data]\n" - "If [data] is not specified, returns formatted hash data to work on:\n" - " \"midstate\" : precomputed hash state after hashing the first half of the data\n" - " \"data\" : block data\n" - " \"hash1\" : formatted hash buffer for second hash\n" - " \"target\" : little endian hash target\n" - "If [data] is specified, tries to solve the block and returns true if it was successful."); - - if (vNodes.empty()) - throw JSONRPCError(-9, "Bitcoin is not connected!"); - - if (IsInitialBlockDownload()) - throw JSONRPCError(-10, "Bitcoin is downloading blocks..."); - - static map > mapNewBlock; - static vector vNewBlock; - static CReserveKey reservekey; - - if (params.size() == 0) - { - // Update block - static unsigned int nTransactionsUpdatedLast; - static CBlockIndex* pindexPrev; - static int64 nStart; - static CBlock* pblock; - if (pindexPrev != pindexBest || - (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) - { - if (pindexPrev != pindexBest) - { - // Deallocate old blocks since they're obsolete now - mapNewBlock.clear(); - BOOST_FOREACH(CBlock* pblock, vNewBlock) - delete pblock; - vNewBlock.clear(); - } - nTransactionsUpdatedLast = nTransactionsUpdated; - pindexPrev = pindexBest; - nStart = GetTime(); - - // Create new block - pblock = CreateNewBlock(reservekey); - if (!pblock) - throw JSONRPCError(-7, "Out of memory"); - vNewBlock.push_back(pblock); - } - - // Update nTime - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - pblock->nNonce = 0; - - // Update nExtraNonce - static unsigned int nExtraNonce = 0; - static int64 nPrevTime = 0; - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, nPrevTime); - - // Save - mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, nExtraNonce); - - // Prebuild hash buffers - char pmidstate[32]; - char pdata[128]; - char phash1[64]; - FormatHashBuffers(pblock, pmidstate, pdata, phash1); - - uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - - Object result; - result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); - result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); - result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); - result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); - return result; - } - else - { - // Parse parameters - vector vchData = ParseHex(params[0].get_str()); - if (vchData.size() != 128) - throw JSONRPCError(-8, "Invalid parameter"); - CBlock* pdata = (CBlock*)&vchData[0]; - - // Byte reverse - for (int i = 0; i < 128/4; i++) - ((unsigned int*)pdata)[i] = CryptoPP::ByteReverse(((unsigned int*)pdata)[i]); - - // Get saved block - if (!mapNewBlock.count(pdata->hashMerkleRoot)) - return false; - CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; - unsigned int nExtraNonce = mapNewBlock[pdata->hashMerkleRoot].second; - - pblock->nTime = pdata->nTime; - pblock->nNonce = pdata->nNonce; - pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce); - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - - return CheckWork(pblock, reservekey); - } -} - - - - - - - - - - - -// -// Call Table -// - -pair pCallTable[] = -{ - make_pair("help", &help), - make_pair("stop", &stop), - make_pair("getblockcount", &getblockcount), - make_pair("getblocknumber", &getblocknumber), - make_pair("getconnectioncount", &getconnectioncount), - make_pair("getdifficulty", &getdifficulty), - make_pair("getgenerate", &getgenerate), - make_pair("setgenerate", &setgenerate), - make_pair("gethashespersec", &gethashespersec), - make_pair("getinfo", &getinfo), - make_pair("getnewaddress", &getnewaddress), - make_pair("getaccountaddress", &getaccountaddress), - make_pair("setaccount", &setaccount), - make_pair("setlabel", &setaccount), // deprecated - make_pair("getaccount", &getaccount), - make_pair("getlabel", &getaccount), // deprecated - make_pair("getaddressesbyaccount", &getaddressesbyaccount), - make_pair("getaddressesbylabel", &getaddressesbyaccount), // deprecated - make_pair("sendtoaddress", &sendtoaddress), - make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress - make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress - make_pair("getreceivedbyaddress", &getreceivedbyaddress), - make_pair("getreceivedbyaccount", &getreceivedbyaccount), - make_pair("getreceivedbylabel", &getreceivedbyaccount), // deprecated - make_pair("listreceivedbyaddress", &listreceivedbyaddress), - make_pair("listreceivedbyaccount", &listreceivedbyaccount), - make_pair("listreceivedbylabel", &listreceivedbyaccount), // deprecated - make_pair("backupwallet", &backupwallet), - make_pair("validateaddress", &validateaddress), - make_pair("getbalance", &getbalance), - make_pair("move", &movecmd), - make_pair("sendfrom", &sendfrom), - make_pair("sendmany", &sendmany), - make_pair("gettransaction", &gettransaction), - make_pair("listtransactions", &listtransactions), - make_pair("getwork", &getwork), - make_pair("listaccounts", &listaccounts), - make_pair("settxfee", &settxfee), -}; -map mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0])); - -string pAllowInSafeMode[] = -{ - "help", - "stop", - "getblockcount", - "getblocknumber", - "getconnectioncount", - "getdifficulty", - "getgenerate", - "setgenerate", - "gethashespersec", - "getinfo", - "getnewaddress", - "getaccountaddress", - "setlabel", - "getaccount", - "getlabel", // deprecated - "getaddressesbyaccount", - "getaddressesbylabel", // deprecated - "backupwallet", - "validateaddress", - "getwork", -}; -set setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0])); - - - - -// -// HTTP protocol -// -// This ain't Apache. We're just using HTTP header for the length field -// and to be compatible with other JSON-RPC implementations. -// - -string HTTPPost(const string& strMsg, const map& mapRequestHeaders) -{ - ostringstream s; - s << "POST / HTTP/1.1\r\n" - << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n" - << "Host: 127.0.0.1\r\n" - << "Content-Type: application/json\r\n" - << "Content-Length: " << strMsg.size() << "\r\n" - << "Accept: application/json\r\n"; - BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders) - s << item.first << ": " << item.second << "\r\n"; - s << "\r\n" << strMsg; - - return s.str(); -} - -string rfc1123Time() -{ - char buffer[64]; - time_t now; - time(&now); - struct tm* now_gmt = gmtime(&now); - string locale(setlocale(LC_TIME, NULL)); - setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); - setlocale(LC_TIME, locale.c_str()); - return string(buffer); -} - -string HTTPReply(int nStatus, const string& strMsg) -{ - if (nStatus == 401) - return strprintf("HTTP/1.0 401 Authorization Required\r\n" - "Date: %s\r\n" - "Server: bitcoin-json-rpc/%s\r\n" - "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" - "Content-Type: text/html\r\n" - "Content-Length: 296\r\n" - "\r\n" - "\r\n" - "\r\n" - "\r\n" - "Error\r\n" - "\r\n" - "\r\n" - "

401 Unauthorized.

\r\n" - "\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); - string strStatus; - if (nStatus == 200) strStatus = "OK"; - else if (nStatus == 400) strStatus = "Bad Request"; - else if (nStatus == 404) strStatus = "Not Found"; - else if (nStatus == 500) strStatus = "Internal Server Error"; - return strprintf( - "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Connection: close\r\n" - "Content-Length: %d\r\n" - "Content-Type: application/json\r\n" - "Server: bitcoin-json-rpc/%s\r\n" - "\r\n" - "%s", - nStatus, - strStatus.c_str(), - rfc1123Time().c_str(), - strMsg.size(), - FormatFullVersion().c_str(), - strMsg.c_str()); -} - -int ReadHTTPStatus(std::basic_istream& stream) -{ - string str; - getline(stream, str); - vector vWords; - boost::split(vWords, str, boost::is_any_of(" ")); - if (vWords.size() < 2) - return 500; - return atoi(vWords[1].c_str()); -} - -int ReadHTTPHeader(std::basic_istream& stream, map& mapHeadersRet) -{ - int nLen = 0; - loop - { - string str; - std::getline(stream, str); - if (str.empty() || str == "\r") - break; - string::size_type nColon = str.find(":"); - if (nColon != string::npos) - { - string strHeader = str.substr(0, nColon); - boost::trim(strHeader); - boost::to_lower(strHeader); - string strValue = str.substr(nColon+1); - boost::trim(strValue); - mapHeadersRet[strHeader] = strValue; - if (strHeader == "content-length") - nLen = atoi(strValue.c_str()); - } - } - return nLen; -} - -int ReadHTTP(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet) -{ - mapHeadersRet.clear(); - strMessageRet = ""; - - // Read status - int nStatus = ReadHTTPStatus(stream); - - // Read header - int nLen = ReadHTTPHeader(stream, mapHeadersRet); - if (nLen < 0 || nLen > MAX_SIZE) - return 500; - - // Read message - if (nLen > 0) - { - vector vch(nLen); - stream.read(&vch[0], nLen); - strMessageRet = string(vch.begin(), vch.end()); - } - - return nStatus; -} - -string EncodeBase64(string s) -{ - BIO *b64, *bmem; - BUF_MEM *bptr; - - b64 = BIO_new(BIO_f_base64()); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_new(BIO_s_mem()); - b64 = BIO_push(b64, bmem); - BIO_write(b64, s.c_str(), s.size()); - BIO_flush(b64); - BIO_get_mem_ptr(b64, &bptr); - - string result(bptr->data, bptr->length); - BIO_free_all(b64); - - return result; -} - -string DecodeBase64(string s) -{ - BIO *b64, *bmem; - - char* buffer = static_cast(calloc(s.size(), sizeof(char))); - - b64 = BIO_new(BIO_f_base64()); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_new_mem_buf(const_cast(s.c_str()), s.size()); - bmem = BIO_push(b64, bmem); - BIO_read(bmem, buffer, s.size()); - BIO_free_all(bmem); - - string result(buffer); - free(buffer); - return result; -} - -bool HTTPAuthorized(map& mapHeaders) -{ - string strAuth = mapHeaders["authorization"]; - if (strAuth.substr(0,6) != "Basic ") - return false; - string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); - string strUserPass = DecodeBase64(strUserPass64); - string::size_type nColon = strUserPass.find(":"); - if (nColon == string::npos) - return false; - string strUser = strUserPass.substr(0, nColon); - string strPassword = strUserPass.substr(nColon+1); - return (strUser == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]); -} - -// -// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, -// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were -// unspecified (HTTP errors and contents of 'error'). -// -// 1.0 spec: http://json-rpc.org/wiki/specification -// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http -// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx -// - -string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) -{ - Object request; - request.push_back(Pair("method", strMethod)); - request.push_back(Pair("params", params)); - request.push_back(Pair("id", id)); - return write_string(Value(request), false) + "\n"; -} - -string JSONRPCReply(const Value& result, const Value& error, const Value& id) -{ - Object reply; - if (error.type() != null_type) - reply.push_back(Pair("result", Value::null)); - else - reply.push_back(Pair("result", result)); - reply.push_back(Pair("error", error)); - reply.push_back(Pair("id", id)); - return write_string(Value(reply), false) + "\n"; -} - -void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) -{ - // Send error reply from json-rpc error object - int nStatus = 500; - int code = find_value(objError, "code").get_int(); - if (code == -32600) nStatus = 400; - else if (code == -32601) nStatus = 404; - string strReply = JSONRPCReply(Value::null, objError, id); - stream << HTTPReply(nStatus, strReply) << std::flush; -} - -bool ClientAllowed(const string& strAddress) -{ - if (strAddress == asio::ip::address_v4::loopback().to_string()) - return true; - const vector& vAllow = mapMultiArgs["-rpcallowip"]; - BOOST_FOREACH(string strAllow, vAllow) - if (WildcardMatch(strAddress, strAllow)) - return true; - return false; -} - -#ifdef USE_SSL -// -// IOStream device that speaks SSL but can also speak non-SSL -// -class SSLIOStreamDevice : public iostreams::device { -public: - SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn) - { - fUseSSL = fUseSSLIn; - fNeedHandshake = fUseSSLIn; - } - - void handshake(ssl::stream_base::handshake_type role) - { - if (!fNeedHandshake) return; - fNeedHandshake = false; - stream.handshake(role); - } - std::streamsize read(char* s, std::streamsize n) - { - handshake(ssl::stream_base::server); // HTTPS servers read first - if (fUseSSL) return stream.read_some(asio::buffer(s, n)); - return stream.next_layer().read_some(asio::buffer(s, n)); - } - std::streamsize write(const char* s, std::streamsize n) - { - handshake(ssl::stream_base::client); // HTTPS clients write first - if (fUseSSL) return asio::write(stream, asio::buffer(s, n)); - return asio::write(stream.next_layer(), asio::buffer(s, n)); - } - bool connect(const std::string& server, const std::string& port) - { - ip::tcp::resolver resolver(stream.get_io_service()); - ip::tcp::resolver::query query(server.c_str(), port.c_str()); - ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); - ip::tcp::resolver::iterator end; - boost::system::error_code error = asio::error::host_not_found; - while (error && endpoint_iterator != end) - { - stream.lowest_layer().close(); - stream.lowest_layer().connect(*endpoint_iterator++, error); - } - if (error) - return false; - return true; - } - -private: - bool fNeedHandshake; - bool fUseSSL; - SSLStream& stream; -}; -#endif - -void ThreadRPCServer(void* parg) -{ - IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg)); - try - { - vnThreadsRunning[4]++; - ThreadRPCServer2(parg); - vnThreadsRunning[4]--; - } - catch (std::exception& e) { - vnThreadsRunning[4]--; - PrintException(&e, "ThreadRPCServer()"); - } catch (...) { - vnThreadsRunning[4]--; - PrintException(NULL, "ThreadRPCServer()"); - } - printf("ThreadRPCServer exiting\n"); -} - -void ThreadRPCServer2(void* parg) -{ - printf("ThreadRPCServer started\n"); - - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") - { - string strWhatAmI = "To use bitcoind"; - if (mapArgs.count("-server")) - strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); - else if (mapArgs.count("-daemon")) - strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); - PrintConsole( - _("Warning: %s, you must set rpcpassword=\nin the configuration file: %s\n" - "If the file does not exist, create it with owner-readable-only file permissions.\n"), - strWhatAmI.c_str(), - GetConfigFile().c_str()); - CreateThread(Shutdown, NULL); - return; - } - - bool fUseSSL = GetBoolArg("-rpcssl"); - asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); - - asio::io_service io_service; - ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332)); - ip::tcp::acceptor acceptor(io_service, endpoint); - - acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); - -#ifdef USE_SSL - ssl::context context(io_service, ssl::context::sslv23); - if (fUseSSL) - { - context.set_options(ssl::context::no_sslv2); - filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert"); - if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile; - if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str()); - else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str()); - filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem"); - if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile; - if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem); - else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str()); - - string ciphers = GetArg("-rpcsslciphers", - "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); - SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str()); - } -#else - if (fUseSSL) - throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); -#endif - - loop - { - // Accept connection -#ifdef USE_SSL - SSLStream sslStream(io_service, context); - SSLIOStreamDevice d(sslStream, fUseSSL); - iostreams::stream stream(d); -#else - ip::tcp::iostream stream; -#endif - - ip::tcp::endpoint peer; - vnThreadsRunning[4]--; -#ifdef USE_SSL - acceptor.accept(sslStream.lowest_layer(), peer); -#else - acceptor.accept(*stream.rdbuf(), peer); -#endif - vnThreadsRunning[4]++; - if (fShutdown) - return; - - // Restrict callers by IP - if (!ClientAllowed(peer.address().to_string())) - continue; - - map mapHeaders; - string strRequest; - - boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest)); - if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30)))) - { // Timed out: - acceptor.cancel(); - printf("ThreadRPCServer ReadHTTP timeout\n"); - continue; - } - - // Check authorization - if (mapHeaders.count("authorization") == 0) - { - stream << HTTPReply(401, "") << std::flush; - continue; - } - if (!HTTPAuthorized(mapHeaders)) - { - // Deter brute-forcing short passwords - if (mapArgs["-rpcpassword"].size() < 15) - Sleep(50); - - stream << HTTPReply(401, "") << std::flush; - printf("ThreadRPCServer incorrect password attempt\n"); - continue; - } - - Value id = Value::null; - try - { - // Parse request - Value valRequest; - if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type) - throw JSONRPCError(-32700, "Parse error"); - const Object& request = valRequest.get_obj(); - - // Parse id now so errors from here on will have the id - id = find_value(request, "id"); - - // Parse method - Value valMethod = find_value(request, "method"); - if (valMethod.type() == null_type) - throw JSONRPCError(-32600, "Missing method"); - if (valMethod.type() != str_type) - throw JSONRPCError(-32600, "Method must be a string"); - string strMethod = valMethod.get_str(); - if (strMethod != "getwork") - printf("ThreadRPCServer method=%s\n", strMethod.c_str()); - - // Parse params - Value valParams = find_value(request, "params"); - Array params; - if (valParams.type() == array_type) - params = valParams.get_array(); - else if (valParams.type() == null_type) - params = Array(); - else - throw JSONRPCError(-32600, "Params must be an array"); - - // Find method - map::iterator mi = mapCallTable.find(strMethod); - if (mi == mapCallTable.end()) - throw JSONRPCError(-32601, "Method not found"); - - // Observe safe mode - string strWarning = GetWarnings("rpc"); - if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod)) - throw JSONRPCError(-2, string("Safe mode: ") + strWarning); - - try - { - // Execute - Value result = (*(*mi).second)(params, false); - - // Send reply - string strReply = JSONRPCReply(result, Value::null, id); - stream << HTTPReply(200, strReply) << std::flush; - } - catch (std::exception& e) - { - ErrorReply(stream, JSONRPCError(-1, e.what()), id); - } - } - catch (Object& objError) - { - ErrorReply(stream, objError, id); - } - catch (std::exception& e) - { - ErrorReply(stream, JSONRPCError(-32700, e.what()), id); - } - } -} - - - - -Object CallRPC(const string& strMethod, const Array& params) -{ - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") - throw runtime_error(strprintf( - _("You must set rpcpassword= in the configuration file:\n%s\n" - "If the file does not exist, create it with owner-readable-only file permissions."), - GetConfigFile().c_str())); - - // Connect to localhost - bool fUseSSL = GetBoolArg("-rpcssl"); -#ifdef USE_SSL - asio::io_service io_service; - ssl::context context(io_service, ssl::context::sslv23); - context.set_options(ssl::context::no_sslv2); - SSLStream sslStream(io_service, context); - SSLIOStreamDevice d(sslStream, fUseSSL); - iostreams::stream stream(d); - if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"))) - throw runtime_error("couldn't connect to server"); -#else - if (fUseSSL) - throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); - - ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")); - if (stream.fail()) - throw runtime_error("couldn't connect to server"); -#endif - - - // HTTP basic authentication - string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); - map mapRequestHeaders; - mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; - - // Send request - string strRequest = JSONRPCRequest(strMethod, params, 1); - string strPost = HTTPPost(strRequest, mapRequestHeaders); - stream << strPost << std::flush; - - // Receive reply - map mapHeaders; - string strReply; - int nStatus = ReadHTTP(stream, mapHeaders, strReply); - if (nStatus == 401) - throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); - else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500) - throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); - else if (strReply.empty()) - throw runtime_error("no response from server"); - - // Parse reply - Value valReply; - if (!read_string(strReply, valReply)) - throw runtime_error("couldn't parse reply from server"); - const Object& reply = valReply.get_obj(); - if (reply.empty()) - throw runtime_error("expected reply to have result, error and id properties"); - - return reply; -} - - - - -template -void ConvertTo(Value& value) -{ - if (value.type() == str_type) - { - // reinterpret string as unquoted json value - Value value2; - if (!read_string(value.get_str(), value2)) - throw runtime_error("type mismatch"); - value = value2.get_value(); - } - else - { - value = value.get_value(); - } -} - -int CommandLineRPC(int argc, char *argv[]) -{ - string strPrint; - int nRet = 0; - try - { - // Skip switches - while (argc > 1 && IsSwitchChar(argv[1][0])) - { - argc--; - argv++; - } - - // Method - if (argc < 2) - throw runtime_error("too few parameters"); - string strMethod = argv[1]; - - // Parameters default to strings - Array params; - for (int i = 2; i < argc; i++) - params.push_back(argv[i]); - int n = params.size(); - - // - // Special case non-string parameter types - // - if (strMethod == "setgenerate" && n > 0) ConvertTo(params[0]); - if (strMethod == "setgenerate" && n > 1) ConvertTo(params[1]); - if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); - if (strMethod == "getamountreceived" && n > 1) ConvertTo(params[1]); // deprecated - if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo(params[1]); - if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated - if (strMethod == "getallreceived" && n > 0) ConvertTo(params[0]); // deprecated - if (strMethod == "getallreceived" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo(params[0]); - if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo(params[0]); - if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo(params[0]); // deprecated - if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated - if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); - if (strMethod == "move" && n > 2) ConvertTo(params[2]); - if (strMethod == "move" && n > 3) ConvertTo(params[3]); - if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]); - if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); - if (strMethod == "listtransactions" && n > 1) ConvertTo(params[1]); - if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); - if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); - if (strMethod == "sendmany" && n > 1) - { - string s = params[1].get_str(); - Value v; - if (!read_string(s, v) || v.type() != obj_type) - throw runtime_error("type mismatch"); - params[1] = v.get_obj(); - } - if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); - - // Execute - Object reply = CallRPC(strMethod, params); - - // Parse reply - const Value& result = find_value(reply, "result"); - const Value& error = find_value(reply, "error"); - const Value& id = find_value(reply, "id"); - - if (error.type() != null_type) - { - // Error - strPrint = "error: " + write_string(error, false); - int code = find_value(error.get_obj(), "code").get_int(); - nRet = abs(code); - } - else - { - // Result - if (result.type() == null_type) - strPrint = ""; - else if (result.type() == str_type) - strPrint = result.get_str(); - else - strPrint = write_string(result, true); - } - } - catch (std::exception& e) - { - strPrint = string("error: ") + e.what(); - nRet = 87; - } - catch (...) - { - PrintException(NULL, "CommandLineRPC()"); - } - - if (strPrint != "") - { -#if defined(__WXMSW__) && defined(GUI) - // Windows GUI apps can't print to command line, - // so settle for a message box yuck - MyMessageBox(strPrint, "Bitcoin", wxOK); -#else - fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); -#endif - } - return nRet; -} - - - - -#ifdef TEST -int main(int argc, char *argv[]) -{ -#ifdef _MSC_VER - // Turn off microsoft heap dump noise - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); -#endif - setbuf(stdin, NULL); - setbuf(stdout, NULL); - setbuf(stderr, NULL); - - try - { - if (argc >= 2 && string(argv[1]) == "-server") - { - printf("server ready\n"); - ThreadRPCServer(NULL); - } - else - { - return CommandLineRPC(argc, argv); - } - } - catch (std::exception& e) { - PrintException(&e, "main()"); - } catch (...) { - PrintException(NULL, "main()"); - } - return 0; -} -#endif diff --git a/core/src/script.cpp b/core/src/script.cpp deleted file mode 100644 index 97334ca0a0..0000000000 --- a/core/src/script.cpp +++ /dev/null @@ -1,1208 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#include "headers.h" - -using namespace std; -using namespace boost; - -bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); - - - -typedef vector valtype; -static const valtype vchFalse(0); -static const valtype vchZero(0); -static const valtype vchTrue(1, 1); -static const CBigNum bnZero(0); -static const CBigNum bnOne(1); -static const CBigNum bnFalse(0); -static const CBigNum bnTrue(1); -static const size_t nMaxNumSize = 4; - - -CBigNum CastToBigNum(const valtype& vch) -{ - if (vch.size() > nMaxNumSize) - throw runtime_error("CastToBigNum() : overflow"); - // Get rid of extra leading zeros - return CBigNum(CBigNum(vch).getvch()); -} - -bool CastToBool(const valtype& vch) -{ - for (int i = 0; i < vch.size(); i++) - { - if (vch[i] != 0) - { - // Can be negative zero - if (i == vch.size()-1 && vch[i] == 0x80) - return false; - return true; - } - } - return false; -} - -void MakeSameSize(valtype& vch1, valtype& vch2) -{ - // Lengthen the shorter one - if (vch1.size() < vch2.size()) - vch1.resize(vch2.size(), 0); - if (vch2.size() < vch1.size()) - vch2.resize(vch1.size(), 0); -} - - - -// -// Script is a stack machine (like Forth) that evaluates a predicate -// returning a bool indicating valid or not. There are no loops. -// -#define stacktop(i) (stack.at(stack.size()+(i))) -#define altstacktop(i) (altstack.at(altstack.size()+(i))) -static inline void popstack(vector& stack) -{ - if (stack.empty()) - throw runtime_error("popstack() : stack empty"); - stack.pop_back(); -} - - -bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType) -{ - CAutoBN_CTX pctx; - CScript::const_iterator pc = script.begin(); - CScript::const_iterator pend = script.end(); - CScript::const_iterator pbegincodehash = script.begin(); - opcodetype opcode; - valtype vchPushValue; - vector vfExec; - vector altstack; - if (script.size() > 10000) - return false; - int nOpCount = 0; - - - try - { - while (pc < pend) - { - bool fExec = !count(vfExec.begin(), vfExec.end(), false); - - // - // Read instruction - // - if (!script.GetOp(pc, opcode, vchPushValue)) - return false; - if (vchPushValue.size() > 520) - return false; - if (opcode > OP_16 && ++nOpCount > 201) - return false; - - if (opcode == OP_CAT || - opcode == OP_SUBSTR || - opcode == OP_LEFT || - opcode == OP_RIGHT || - opcode == OP_INVERT || - opcode == OP_AND || - opcode == OP_OR || - opcode == OP_XOR || - opcode == OP_2MUL || - opcode == OP_2DIV || - opcode == OP_MUL || - opcode == OP_DIV || - opcode == OP_MOD || - opcode == OP_LSHIFT || - opcode == OP_RSHIFT) - return false; - - if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) - stack.push_back(vchPushValue); - else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) - switch (opcode) - { - // - // Push value - // - case OP_1NEGATE: - case OP_1: - case OP_2: - case OP_3: - case OP_4: - case OP_5: - case OP_6: - case OP_7: - case OP_8: - case OP_9: - case OP_10: - case OP_11: - case OP_12: - case OP_13: - case OP_14: - case OP_15: - case OP_16: - { - // ( -- value) - CBigNum bn((int)opcode - (int)(OP_1 - 1)); - stack.push_back(bn.getvch()); - } - break; - - - // - // Control - // - case OP_NOP: - case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: - case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: - break; - - case OP_IF: - case OP_NOTIF: - { - // if [statements] [else [statements]] endif - bool fValue = false; - if (fExec) - { - if (stack.size() < 1) - return false; - valtype& vch = stacktop(-1); - fValue = CastToBool(vch); - if (opcode == OP_NOTIF) - fValue = !fValue; - popstack(stack); - } - vfExec.push_back(fValue); - } - break; - - case OP_ELSE: - { - if (vfExec.empty()) - return false; - vfExec.back() = !vfExec.back(); - } - break; - - case OP_ENDIF: - { - if (vfExec.empty()) - return false; - vfExec.pop_back(); - } - break; - - case OP_VERIFY: - { - // (true -- ) or - // (false -- false) and return - if (stack.size() < 1) - return false; - bool fValue = CastToBool(stacktop(-1)); - if (fValue) - popstack(stack); - else - return false; - } - break; - - case OP_RETURN: - { - return false; - } - break; - - - // - // Stack ops - // - case OP_TOALTSTACK: - { - if (stack.size() < 1) - return false; - altstack.push_back(stacktop(-1)); - popstack(stack); - } - break; - - case OP_FROMALTSTACK: - { - if (altstack.size() < 1) - return false; - stack.push_back(altstacktop(-1)); - popstack(altstack); - } - break; - - case OP_2DROP: - { - // (x1 x2 -- ) - if (stack.size() < 2) - return false; - popstack(stack); - popstack(stack); - } - break; - - case OP_2DUP: - { - // (x1 x2 -- x1 x2 x1 x2) - if (stack.size() < 2) - return false; - valtype vch1 = stacktop(-2); - valtype vch2 = stacktop(-1); - stack.push_back(vch1); - stack.push_back(vch2); - } - break; - - case OP_3DUP: - { - // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) - if (stack.size() < 3) - return false; - valtype vch1 = stacktop(-3); - valtype vch2 = stacktop(-2); - valtype vch3 = stacktop(-1); - stack.push_back(vch1); - stack.push_back(vch2); - stack.push_back(vch3); - } - break; - - case OP_2OVER: - { - // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) - if (stack.size() < 4) - return false; - valtype vch1 = stacktop(-4); - valtype vch2 = stacktop(-3); - stack.push_back(vch1); - stack.push_back(vch2); - } - break; - - case OP_2ROT: - { - // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) - if (stack.size() < 6) - return false; - valtype vch1 = stacktop(-6); - valtype vch2 = stacktop(-5); - stack.erase(stack.end()-6, stack.end()-4); - stack.push_back(vch1); - stack.push_back(vch2); - } - break; - - case OP_2SWAP: - { - // (x1 x2 x3 x4 -- x3 x4 x1 x2) - if (stack.size() < 4) - return false; - swap(stacktop(-4), stacktop(-2)); - swap(stacktop(-3), stacktop(-1)); - } - break; - - case OP_IFDUP: - { - // (x - 0 | x x) - if (stack.size() < 1) - return false; - valtype vch = stacktop(-1); - if (CastToBool(vch)) - stack.push_back(vch); - } - break; - - case OP_DEPTH: - { - // -- stacksize - CBigNum bn(stack.size()); - stack.push_back(bn.getvch()); - } - break; - - case OP_DROP: - { - // (x -- ) - if (stack.size() < 1) - return false; - popstack(stack); - } - break; - - case OP_DUP: - { - // (x -- x x) - if (stack.size() < 1) - return false; - valtype vch = stacktop(-1); - stack.push_back(vch); - } - break; - - case OP_NIP: - { - // (x1 x2 -- x2) - if (stack.size() < 2) - return false; - stack.erase(stack.end() - 2); - } - break; - - case OP_OVER: - { - // (x1 x2 -- x1 x2 x1) - if (stack.size() < 2) - return false; - valtype vch = stacktop(-2); - stack.push_back(vch); - } - break; - - case OP_PICK: - case OP_ROLL: - { - // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) - // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) - if (stack.size() < 2) - return false; - int n = CastToBigNum(stacktop(-1)).getint(); - popstack(stack); - if (n < 0 || n >= stack.size()) - return false; - valtype vch = stacktop(-n-1); - if (opcode == OP_ROLL) - stack.erase(stack.end()-n-1); - stack.push_back(vch); - } - break; - - case OP_ROT: - { - // (x1 x2 x3 -- x2 x3 x1) - // x2 x1 x3 after first swap - // x2 x3 x1 after second swap - if (stack.size() < 3) - return false; - swap(stacktop(-3), stacktop(-2)); - swap(stacktop(-2), stacktop(-1)); - } - break; - - case OP_SWAP: - { - // (x1 x2 -- x2 x1) - if (stack.size() < 2) - return false; - swap(stacktop(-2), stacktop(-1)); - } - break; - - case OP_TUCK: - { - // (x1 x2 -- x2 x1 x2) - if (stack.size() < 2) - return false; - valtype vch = stacktop(-1); - stack.insert(stack.end()-2, vch); - } - break; - - - // - // Splice ops - // - case OP_CAT: - { - // (x1 x2 -- out) - if (stack.size() < 2) - return false; - valtype& vch1 = stacktop(-2); - valtype& vch2 = stacktop(-1); - vch1.insert(vch1.end(), vch2.begin(), vch2.end()); - popstack(stack); - if (stacktop(-1).size() > 520) - return false; - } - break; - - case OP_SUBSTR: - { - // (in begin size -- out) - if (stack.size() < 3) - return false; - valtype& vch = stacktop(-3); - int nBegin = CastToBigNum(stacktop(-2)).getint(); - int nEnd = nBegin + CastToBigNum(stacktop(-1)).getint(); - if (nBegin < 0 || nEnd < nBegin) - return false; - if (nBegin > vch.size()) - nBegin = vch.size(); - if (nEnd > vch.size()) - nEnd = vch.size(); - vch.erase(vch.begin() + nEnd, vch.end()); - vch.erase(vch.begin(), vch.begin() + nBegin); - popstack(stack); - popstack(stack); - } - break; - - case OP_LEFT: - case OP_RIGHT: - { - // (in size -- out) - if (stack.size() < 2) - return false; - valtype& vch = stacktop(-2); - int nSize = CastToBigNum(stacktop(-1)).getint(); - if (nSize < 0) - return false; - if (nSize > vch.size()) - nSize = vch.size(); - if (opcode == OP_LEFT) - vch.erase(vch.begin() + nSize, vch.end()); - else - vch.erase(vch.begin(), vch.end() - nSize); - popstack(stack); - } - break; - - case OP_SIZE: - { - // (in -- in size) - if (stack.size() < 1) - return false; - CBigNum bn(stacktop(-1).size()); - stack.push_back(bn.getvch()); - } - break; - - - // - // Bitwise logic - // - case OP_INVERT: - { - // (in - out) - if (stack.size() < 1) - return false; - valtype& vch = stacktop(-1); - for (int i = 0; i < vch.size(); i++) - vch[i] = ~vch[i]; - } - break; - - case OP_AND: - case OP_OR: - case OP_XOR: - { - // (x1 x2 - out) - if (stack.size() < 2) - return false; - valtype& vch1 = stacktop(-2); - valtype& vch2 = stacktop(-1); - MakeSameSize(vch1, vch2); - if (opcode == OP_AND) - { - for (int i = 0; i < vch1.size(); i++) - vch1[i] &= vch2[i]; - } - else if (opcode == OP_OR) - { - for (int i = 0; i < vch1.size(); i++) - vch1[i] |= vch2[i]; - } - else if (opcode == OP_XOR) - { - for (int i = 0; i < vch1.size(); i++) - vch1[i] ^= vch2[i]; - } - popstack(stack); - } - break; - - case OP_EQUAL: - case OP_EQUALVERIFY: - //case OP_NOTEQUAL: // use OP_NUMNOTEQUAL - { - // (x1 x2 - bool) - if (stack.size() < 2) - return false; - valtype& vch1 = stacktop(-2); - valtype& vch2 = stacktop(-1); - bool fEqual = (vch1 == vch2); - // OP_NOTEQUAL is disabled because it would be too easy to say - // something like n != 1 and have some wiseguy pass in 1 with extra - // zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001) - //if (opcode == OP_NOTEQUAL) - // fEqual = !fEqual; - popstack(stack); - popstack(stack); - stack.push_back(fEqual ? vchTrue : vchFalse); - if (opcode == OP_EQUALVERIFY) - { - if (fEqual) - popstack(stack); - else - return false; - } - } - break; - - - // - // Numeric - // - case OP_1ADD: - case OP_1SUB: - case OP_2MUL: - case OP_2DIV: - case OP_NEGATE: - case OP_ABS: - case OP_NOT: - case OP_0NOTEQUAL: - { - // (in -- out) - if (stack.size() < 1) - return false; - CBigNum bn = CastToBigNum(stacktop(-1)); - switch (opcode) - { - case OP_1ADD: bn += bnOne; break; - case OP_1SUB: bn -= bnOne; break; - case OP_2MUL: bn <<= 1; break; - case OP_2DIV: bn >>= 1; break; - case OP_NEGATE: bn = -bn; break; - case OP_ABS: if (bn < bnZero) bn = -bn; break; - case OP_NOT: bn = (bn == bnZero); break; - case OP_0NOTEQUAL: bn = (bn != bnZero); break; - } - popstack(stack); - stack.push_back(bn.getvch()); - } - break; - - case OP_ADD: - case OP_SUB: - case OP_MUL: - case OP_DIV: - case OP_MOD: - case OP_LSHIFT: - case OP_RSHIFT: - case OP_BOOLAND: - case OP_BOOLOR: - case OP_NUMEQUAL: - case OP_NUMEQUALVERIFY: - case OP_NUMNOTEQUAL: - case OP_LESSTHAN: - case OP_GREATERTHAN: - case OP_LESSTHANOREQUAL: - case OP_GREATERTHANOREQUAL: - case OP_MIN: - case OP_MAX: - { - // (x1 x2 -- out) - if (stack.size() < 2) - return false; - CBigNum bn1 = CastToBigNum(stacktop(-2)); - CBigNum bn2 = CastToBigNum(stacktop(-1)); - CBigNum bn; - switch (opcode) - { - case OP_ADD: - bn = bn1 + bn2; - break; - - case OP_SUB: - bn = bn1 - bn2; - break; - - case OP_MUL: - if (!BN_mul(&bn, &bn1, &bn2, pctx)) - return false; - break; - - case OP_DIV: - if (!BN_div(&bn, NULL, &bn1, &bn2, pctx)) - return false; - break; - - case OP_MOD: - if (!BN_mod(&bn, &bn1, &bn2, pctx)) - return false; - break; - - case OP_LSHIFT: - if (bn2 < bnZero || bn2 > CBigNum(2048)) - return false; - bn = bn1 << bn2.getulong(); - break; - - case OP_RSHIFT: - if (bn2 < bnZero || bn2 > CBigNum(2048)) - return false; - bn = bn1 >> bn2.getulong(); - break; - - case OP_BOOLAND: bn = (bn1 != bnZero && bn2 != bnZero); break; - case OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break; - case OP_NUMEQUAL: bn = (bn1 == bn2); break; - case OP_NUMEQUALVERIFY: bn = (bn1 == bn2); break; - case OP_NUMNOTEQUAL: bn = (bn1 != bn2); break; - case OP_LESSTHAN: bn = (bn1 < bn2); break; - case OP_GREATERTHAN: bn = (bn1 > bn2); break; - case OP_LESSTHANOREQUAL: bn = (bn1 <= bn2); break; - case OP_GREATERTHANOREQUAL: bn = (bn1 >= bn2); break; - case OP_MIN: bn = (bn1 < bn2 ? bn1 : bn2); break; - case OP_MAX: bn = (bn1 > bn2 ? bn1 : bn2); break; - } - popstack(stack); - popstack(stack); - stack.push_back(bn.getvch()); - - if (opcode == OP_NUMEQUALVERIFY) - { - if (CastToBool(stacktop(-1))) - popstack(stack); - else - return false; - } - } - break; - - case OP_WITHIN: - { - // (x min max -- out) - if (stack.size() < 3) - return false; - CBigNum bn1 = CastToBigNum(stacktop(-3)); - CBigNum bn2 = CastToBigNum(stacktop(-2)); - CBigNum bn3 = CastToBigNum(stacktop(-1)); - bool fValue = (bn2 <= bn1 && bn1 < bn3); - popstack(stack); - popstack(stack); - popstack(stack); - stack.push_back(fValue ? vchTrue : vchFalse); - } - break; - - - // - // Crypto - // - case OP_RIPEMD160: - case OP_SHA1: - case OP_SHA256: - case OP_HASH160: - case OP_HASH256: - { - // (in -- hash) - if (stack.size() < 1) - return false; - valtype& vch = stacktop(-1); - valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); - if (opcode == OP_RIPEMD160) - RIPEMD160(&vch[0], vch.size(), &vchHash[0]); - else if (opcode == OP_SHA1) - SHA1(&vch[0], vch.size(), &vchHash[0]); - else if (opcode == OP_SHA256) - SHA256(&vch[0], vch.size(), &vchHash[0]); - else if (opcode == OP_HASH160) - { - uint160 hash160 = Hash160(vch); - memcpy(&vchHash[0], &hash160, sizeof(hash160)); - } - else if (opcode == OP_HASH256) - { - uint256 hash = Hash(vch.begin(), vch.end()); - memcpy(&vchHash[0], &hash, sizeof(hash)); - } - popstack(stack); - stack.push_back(vchHash); - } - break; - - case OP_CODESEPARATOR: - { - // Hash starts after the code separator - pbegincodehash = pc; - } - break; - - case OP_CHECKSIG: - case OP_CHECKSIGVERIFY: - { - // (sig pubkey -- bool) - if (stack.size() < 2) - return false; - - valtype& vchSig = stacktop(-2); - valtype& vchPubKey = stacktop(-1); - - ////// debug print - //PrintHex(vchSig.begin(), vchSig.end(), "sig: %s\n"); - //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n"); - - // Subset of script starting at the most recent codeseparator - CScript scriptCode(pbegincodehash, pend); - - // Drop the signature, since there's no way for a signature to sign itself - scriptCode.FindAndDelete(CScript(vchSig)); - - bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); - - popstack(stack); - popstack(stack); - stack.push_back(fSuccess ? vchTrue : vchFalse); - if (opcode == OP_CHECKSIGVERIFY) - { - if (fSuccess) - popstack(stack); - else - return false; - } - } - break; - - case OP_CHECKMULTISIG: - case OP_CHECKMULTISIGVERIFY: - { - // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) - - int i = 1; - if (stack.size() < i) - return false; - - int nKeysCount = CastToBigNum(stacktop(-i)).getint(); - if (nKeysCount < 0 || nKeysCount > 20) - return false; - nOpCount += nKeysCount; - if (nOpCount > 201) - return false; - int ikey = ++i; - i += nKeysCount; - if (stack.size() < i) - return false; - - int nSigsCount = CastToBigNum(stacktop(-i)).getint(); - if (nSigsCount < 0 || nSigsCount > nKeysCount) - return false; - int isig = ++i; - i += nSigsCount; - if (stack.size() < i) - return false; - - // Subset of script starting at the most recent codeseparator - CScript scriptCode(pbegincodehash, pend); - - // Drop the signatures, since there's no way for a signature to sign itself - for (int k = 0; k < nSigsCount; k++) - { - valtype& vchSig = stacktop(-isig-k); - scriptCode.FindAndDelete(CScript(vchSig)); - } - - bool fSuccess = true; - while (fSuccess && nSigsCount > 0) - { - valtype& vchSig = stacktop(-isig); - valtype& vchPubKey = stacktop(-ikey); - - // Check signature - if (CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType)) - { - isig++; - nSigsCount--; - } - ikey++; - nKeysCount--; - - // If there are more signatures left than keys left, - // then too many signatures have failed - if (nSigsCount > nKeysCount) - fSuccess = false; - } - - while (i-- > 0) - popstack(stack); - stack.push_back(fSuccess ? vchTrue : vchFalse); - - if (opcode == OP_CHECKMULTISIGVERIFY) - { - if (fSuccess) - popstack(stack); - else - return false; - } - } - break; - - default: - return false; - } - - // Size limits - if (stack.size() + altstack.size() > 1000) - return false; - } - } - catch (...) - { - return false; - } - - - if (!vfExec.empty()) - return false; - - return true; -} - - - - - - - - - -uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) -{ - if (nIn >= txTo.vin.size()) - { - printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); - return 1; - } - CTransaction txTmp(txTo); - - // In case concatenating two scripts ends up with two codeseparators, - // or an extra one at the end, this prevents all those possible incompatibilities. - scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); - - // Blank out other inputs' signatures - for (int i = 0; i < txTmp.vin.size(); i++) - txTmp.vin[i].scriptSig = CScript(); - txTmp.vin[nIn].scriptSig = scriptCode; - - // Blank out some of the outputs - if ((nHashType & 0x1f) == SIGHASH_NONE) - { - // Wildcard payee - txTmp.vout.clear(); - - // Let the others update at will - for (int i = 0; i < txTmp.vin.size(); i++) - if (i != nIn) - txTmp.vin[i].nSequence = 0; - } - else if ((nHashType & 0x1f) == SIGHASH_SINGLE) - { - // Only lockin the txout payee at same index as txin - unsigned int nOut = nIn; - if (nOut >= txTmp.vout.size()) - { - printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); - return 1; - } - txTmp.vout.resize(nOut+1); - for (int i = 0; i < nOut; i++) - txTmp.vout[i].SetNull(); - - // Let the others update at will - for (int i = 0; i < txTmp.vin.size(); i++) - if (i != nIn) - txTmp.vin[i].nSequence = 0; - } - - // Blank out other inputs completely, not recommended for open transactions - if (nHashType & SIGHASH_ANYONECANPAY) - { - txTmp.vin[0] = txTmp.vin[nIn]; - txTmp.vin.resize(1); - } - - // Serialize and hash - CDataStream ss(SER_GETHASH); - ss.reserve(10000); - ss << txTmp << nHashType; - return Hash(ss.begin(), ss.end()); -} - - -bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, - const CTransaction& txTo, unsigned int nIn, int nHashType) -{ - CKey key; - if (!key.SetPubKey(vchPubKey)) - return false; - - // Hash type is one byte tacked on to the end of the signature - if (vchSig.empty()) - return false; - if (nHashType == 0) - nHashType = vchSig.back(); - else if (nHashType != vchSig.back()) - return false; - vchSig.pop_back(); - - return key.Verify(SignatureHash(scriptCode, txTo, nIn, nHashType), vchSig); -} - - - - - - - - - - -bool Solver(const CScript& scriptPubKey, vector >& vSolutionRet) -{ - // Templates - static vector vTemplates; - if (vTemplates.empty()) - { - // Standard tx, sender provides pubkey, receiver adds signature - vTemplates.push_back(CScript() << OP_PUBKEY << OP_CHECKSIG); - - // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey - vTemplates.push_back(CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG); - } - - // Scan templates - const CScript& script1 = scriptPubKey; - BOOST_FOREACH(const CScript& script2, vTemplates) - { - vSolutionRet.clear(); - opcodetype opcode1, opcode2; - vector vch1, vch2; - - // Compare - CScript::const_iterator pc1 = script1.begin(); - CScript::const_iterator pc2 = script2.begin(); - loop - { - if (pc1 == script1.end() && pc2 == script2.end()) - { - // Found a match - reverse(vSolutionRet.begin(), vSolutionRet.end()); - return true; - } - if (!script1.GetOp(pc1, opcode1, vch1)) - break; - if (!script2.GetOp(pc2, opcode2, vch2)) - break; - if (opcode2 == OP_PUBKEY) - { - if (vch1.size() < 33 || vch1.size() > 120) - break; - vSolutionRet.push_back(make_pair(opcode2, vch1)); - } - else if (opcode2 == OP_PUBKEYHASH) - { - if (vch1.size() != sizeof(uint160)) - break; - vSolutionRet.push_back(make_pair(opcode2, vch1)); - } - else if (opcode1 != opcode2 || vch1 != vch2) - { - break; - } - } - } - - vSolutionRet.clear(); - return false; -} - - -bool Solver(const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet) -{ - scriptSigRet.clear(); - - vector > vSolution; - if (!Solver(scriptPubKey, vSolution)) - return false; - - // Compile solution - CRITICAL_BLOCK(cs_mapKeys) - { - BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) - { - if (item.first == OP_PUBKEY) - { - // Sign - const valtype& vchPubKey = item.second; - if (!mapKeys.count(vchPubKey)) - return false; - if (hash != 0) - { - vector vchSig; - if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig)) - return false; - vchSig.push_back((unsigned char)nHashType); - scriptSigRet << vchSig; - } - } - else if (item.first == OP_PUBKEYHASH) - { - // Sign and give pubkey - map::iterator mi = mapPubKeys.find(uint160(item.second)); - if (mi == mapPubKeys.end()) - return false; - const vector& vchPubKey = (*mi).second; - if (!mapKeys.count(vchPubKey)) - return false; - if (hash != 0) - { - vector vchSig; - if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig)) - return false; - vchSig.push_back((unsigned char)nHashType); - scriptSigRet << vchSig << vchPubKey; - } - } - else - { - return false; - } - } - } - - return true; -} - - -bool IsStandard(const CScript& scriptPubKey) -{ - vector > vSolution; - return Solver(scriptPubKey, vSolution); -} - - -bool IsMine(const CScript& scriptPubKey) -{ - CScript scriptSig; - return Solver(scriptPubKey, 0, 0, scriptSig); -} - - -bool ExtractPubKey(const CScript& scriptPubKey, bool fMineOnly, vector& vchPubKeyRet) -{ - vchPubKeyRet.clear(); - - vector > vSolution; - if (!Solver(scriptPubKey, vSolution)) - return false; - - CRITICAL_BLOCK(cs_mapKeys) - { - BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) - { - valtype vchPubKey; - if (item.first == OP_PUBKEY) - { - vchPubKey = item.second; - } - else if (item.first == OP_PUBKEYHASH) - { - map::iterator mi = mapPubKeys.find(uint160(item.second)); - if (mi == mapPubKeys.end()) - continue; - vchPubKey = (*mi).second; - } - if (!fMineOnly || mapKeys.count(vchPubKey)) - { - vchPubKeyRet = vchPubKey; - return true; - } - } - } - return false; -} - - -bool ExtractHash160(const CScript& scriptPubKey, uint160& hash160Ret) -{ - hash160Ret = 0; - - vector > vSolution; - if (!Solver(scriptPubKey, vSolution)) - return false; - - BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) - { - if (item.first == OP_PUBKEYHASH) - { - hash160Ret = uint160(item.second); - return true; - } - } - return false; -} - - -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType) -{ - vector > stack; - if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType)) - return false; - if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType)) - return false; - if (stack.empty()) - return false; - return CastToBool(stack.back()); -} - - -bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType, CScript scriptPrereq) -{ - assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; - assert(txin.prevout.n < txFrom.vout.size()); - const CTxOut& txout = txFrom.vout[txin.prevout.n]; - - // Leave out the signature from the hash, since a signature can't sign itself. - // The checksig op will also drop the signatures from its hash. - uint256 hash = SignatureHash(scriptPrereq + txout.scriptPubKey, txTo, nIn, nHashType); - - if (!Solver(txout.scriptPubKey, hash, nHashType, txin.scriptSig)) - return false; - - txin.scriptSig = scriptPrereq + txin.scriptSig; - - // Test solution - if (scriptPrereq.empty()) - if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, 0)) - return false; - - return true; -} - - -bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType) -{ - assert(nIn < txTo.vin.size()); - const CTxIn& txin = txTo.vin[nIn]; - if (txin.prevout.n >= txFrom.vout.size()) - return false; - const CTxOut& txout = txFrom.vout[txin.prevout.n]; - - if (txin.prevout.hash != txFrom.GetHash()) - return false; - - if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nHashType)) - return false; - - // Anytime a signature is successfully verified, it's proof the outpoint is spent, - // so lets update the wallet spent flag if it doesn't know due to wallet.dat being - // restored from backup or the user making copies of wallet.dat. - WalletUpdateSpent(txin.prevout); - - return true; -} diff --git a/core/src/util.cpp b/core/src/util.cpp deleted file mode 100644 index 6199109289..0000000000 --- a/core/src/util.cpp +++ /dev/null @@ -1,906 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#include "headers.h" - -using namespace std; -using namespace boost; - -map mapArgs; -map > mapMultiArgs; -bool fDebug = false; -bool fPrintToConsole = false; -bool fPrintToDebugger = false; -char pszSetDataDir[MAX_PATH] = ""; -bool fRequestShutdown = false; -bool fShutdown = false; -bool fDaemon = false; -bool fServer = false; -bool fCommandLine = false; -string strMiscWarning; -bool fTestNet = false; -bool fNoListen = false; -bool fLogTimestamps = false; - - - - -// Workaround for "multiple definition of `_tls_used'" -// http://svn.boost.org/trac/boost/ticket/4258 -extern "C" void tss_cleanup_implemented() { } - - - - - -// Init openssl library multithreading support -static boost::interprocess::interprocess_mutex** ppmutexOpenSSL; -void locking_callback(int mode, int i, const char* file, int line) -{ - if (mode & CRYPTO_LOCK) - ppmutexOpenSSL[i]->lock(); - else - ppmutexOpenSSL[i]->unlock(); -} - -// Init -class CInit -{ -public: - CInit() - { - // Init openssl library multithreading support - ppmutexOpenSSL = (boost::interprocess::interprocess_mutex**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(boost::interprocess::interprocess_mutex*)); - for (int i = 0; i < CRYPTO_num_locks(); i++) - ppmutexOpenSSL[i] = new boost::interprocess::interprocess_mutex(); - CRYPTO_set_locking_callback(locking_callback); - -#ifdef __WXMSW__ - // Seed random number generator with screen scrape and other hardware sources - RAND_screen(); -#endif - - // Seed random number generator with performance counter - RandAddSeed(); - } - ~CInit() - { - // Shutdown openssl library multithreading support - CRYPTO_set_locking_callback(NULL); - for (int i = 0; i < CRYPTO_num_locks(); i++) - delete ppmutexOpenSSL[i]; - OPENSSL_free(ppmutexOpenSSL); - } -} -instance_of_cinit; - - - - - - - - -void RandAddSeed() -{ - // Seed with CPU performance counter - int64 nCounter = GetPerformanceCounter(); - RAND_add(&nCounter, sizeof(nCounter), 1.5); - memset(&nCounter, 0, sizeof(nCounter)); -} - -void RandAddSeedPerfmon() -{ - RandAddSeed(); - - // This can take up to 2 seconds, so only do it every 10 minutes - static int64 nLastPerfmon; - if (GetTime() < nLastPerfmon + 10 * 60) - return; - nLastPerfmon = GetTime(); - -#ifdef __WXMSW__ - // Don't need this on Linux, OpenSSL automatically uses /dev/urandom - // Seed with the entire set of perfmon data - unsigned char pdata[250000]; - memset(pdata, 0, sizeof(pdata)); - unsigned long nSize = sizeof(pdata); - long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize); - RegCloseKey(HKEY_PERFORMANCE_DATA); - if (ret == ERROR_SUCCESS) - { - RAND_add(pdata, nSize, nSize/100.0); - memset(pdata, 0, nSize); - printf("%s RandAddSeed() %d bytes\n", DateTimeStrFormat("%x %H:%M", GetTime()).c_str(), nSize); - } -#endif -} - -uint64 GetRand(uint64 nMax) -{ - if (nMax == 0) - return 0; - - // The range of the random source must be a multiple of the modulus - // to give every possible output value an equal possibility - uint64 nRange = (UINT64_MAX / nMax) * nMax; - uint64 nRand = 0; - do - RAND_bytes((unsigned char*)&nRand, sizeof(nRand)); - while (nRand >= nRange); - return (nRand % nMax); -} - -int GetRandInt(int nMax) -{ - return GetRand(nMax); -} - - - - - - - - - - - -inline int OutputDebugStringF(const char* pszFormat, ...) -{ - int ret = 0; - if (fPrintToConsole) - { - // print to console - va_list arg_ptr; - va_start(arg_ptr, pszFormat); - ret = vprintf(pszFormat, arg_ptr); - va_end(arg_ptr); - } - else - { - // print to debug.log - static FILE* fileout = NULL; - - if (!fileout) - { - char pszFile[MAX_PATH+100]; - GetDataDir(pszFile); - strlcat(pszFile, "/debug.log", sizeof(pszFile)); - fileout = fopen(pszFile, "a"); - if (fileout) setbuf(fileout, NULL); // unbuffered - } - if (fileout) - { - static bool fStartedNewLine = true; - - // Debug print useful for profiling - if (fLogTimestamps && fStartedNewLine) - fprintf(fileout, "%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); - if (pszFormat[strlen(pszFormat) - 1] == '\n') - fStartedNewLine = true; - else - fStartedNewLine = false; - - va_list arg_ptr; - va_start(arg_ptr, pszFormat); - ret = vfprintf(fileout, pszFormat, arg_ptr); - va_end(arg_ptr); - } - } - -#ifdef __WXMSW__ - if (fPrintToDebugger) - { - static CCriticalSection cs_OutputDebugStringF; - - // accumulate a line at a time - CRITICAL_BLOCK(cs_OutputDebugStringF) - { - static char pszBuffer[50000]; - static char* pend; - if (pend == NULL) - pend = pszBuffer; - va_list arg_ptr; - va_start(arg_ptr, pszFormat); - int limit = END(pszBuffer) - pend - 2; - int ret = _vsnprintf(pend, limit, pszFormat, arg_ptr); - va_end(arg_ptr); - if (ret < 0 || ret >= limit) - { - pend = END(pszBuffer) - 2; - *pend++ = '\n'; - } - else - pend += ret; - *pend = '\0'; - char* p1 = pszBuffer; - char* p2; - while (p2 = strchr(p1, '\n')) - { - p2++; - char c = *p2; - *p2 = '\0'; - OutputDebugStringA(p1); - *p2 = c; - p1 = p2; - } - if (p1 != pszBuffer) - memmove(pszBuffer, p1, pend - p1 + 1); - pend -= (p1 - pszBuffer); - } - } -#endif - return ret; -} - - -// Safer snprintf -// - prints up to limit-1 characters -// - output string is always null terminated even if limit reached -// - return value is the number of characters actually printed -int my_snprintf(char* buffer, size_t limit, const char* format, ...) -{ - if (limit == 0) - return 0; - va_list arg_ptr; - va_start(arg_ptr, format); - int ret = _vsnprintf(buffer, limit, format, arg_ptr); - va_end(arg_ptr); - if (ret < 0 || ret >= limit) - { - ret = limit - 1; - buffer[limit-1] = 0; - } - return ret; -} - - -string strprintf(const char* format, ...) -{ - char buffer[50000]; - char* p = buffer; - int limit = sizeof(buffer); - int ret; - loop - { - va_list arg_ptr; - va_start(arg_ptr, format); - ret = _vsnprintf(p, limit, format, arg_ptr); - va_end(arg_ptr); - if (ret >= 0 && ret < limit) - break; - if (p != buffer) - delete[] p; - limit *= 2; - p = new char[limit]; - if (p == NULL) - throw std::bad_alloc(); - } - string str(p, p+ret); - if (p != buffer) - delete[] p; - return str; -} - - -bool error(const char* format, ...) -{ - char buffer[50000]; - int limit = sizeof(buffer); - va_list arg_ptr; - va_start(arg_ptr, format); - int ret = _vsnprintf(buffer, limit, format, arg_ptr); - va_end(arg_ptr); - if (ret < 0 || ret >= limit) - { - ret = limit - 1; - buffer[limit-1] = 0; - } - printf("ERROR: %s\n", buffer); - return false; -} - - -void ParseString(const string& str, char c, vector& v) -{ - if (str.empty()) - return; - string::size_type i1 = 0; - string::size_type i2; - loop - { - i2 = str.find(c, i1); - if (i2 == str.npos) - { - v.push_back(str.substr(i1)); - return; - } - v.push_back(str.substr(i1, i2-i1)); - i1 = i2+1; - } -} - - -string FormatMoney(int64 n, bool fPlus) -{ - // Note: not using straight sprintf here because we do NOT want - // localized number formatting. - int64 n_abs = (n > 0 ? n : -n); - int64 quotient = n_abs/COIN; - int64 remainder = n_abs%COIN; - string str = strprintf("%"PRI64d".%08"PRI64d, quotient, remainder); - - // Right-trim excess 0's before the decimal point: - int nTrim = 0; - for (int i = str.size()-1; (str[i] == '0' && isdigit(str[i-2])); --i) - ++nTrim; - if (nTrim) - str.erase(str.size()-nTrim, nTrim); - - // Insert thousands-separators: - size_t point = str.find("."); - for (int i = (str.size()-point)+3; i < str.size(); i += 4) - if (isdigit(str[str.size() - i - 1])) - str.insert(str.size() - i, 1, ','); - if (n < 0) - str.insert((unsigned int)0, 1, '-'); - else if (fPlus && n > 0) - str.insert((unsigned int)0, 1, '+'); - return str; -} - - -bool ParseMoney(const string& str, int64& nRet) -{ - return ParseMoney(str.c_str(), nRet); -} - -bool ParseMoney(const char* pszIn, int64& nRet) -{ - string strWhole; - int64 nUnits = 0; - const char* p = pszIn; - while (isspace(*p)) - p++; - for (; *p; p++) - { - if (*p == ',' && p > pszIn && isdigit(p[-1]) && isdigit(p[1]) && isdigit(p[2]) && isdigit(p[3]) && !isdigit(p[4])) - continue; - if (*p == '.') - { - p++; - int64 nMult = CENT*10; - while (isdigit(*p) && (nMult > 0)) - { - nUnits += nMult * (*p++ - '0'); - nMult /= 10; - } - break; - } - if (isspace(*p)) - break; - if (!isdigit(*p)) - return false; - strWhole.insert(strWhole.end(), *p); - } - for (; *p; p++) - if (!isspace(*p)) - return false; - if (strWhole.size() > 14) - return false; - if (nUnits < 0 || nUnits > COIN) - return false; - int64 nWhole = atoi64(strWhole); - int64 nValue = nWhole*COIN + nUnits; - - nRet = nValue; - return true; -} - - -vector ParseHex(const char* psz) -{ - static char phexdigit[256] = - { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1, - -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, }; - - // convert hex dump to vector - vector vch; - loop - { - while (isspace(*psz)) - psz++; - char c = phexdigit[(unsigned char)*psz++]; - if (c == (char)-1) - break; - unsigned char n = (c << 4); - c = phexdigit[(unsigned char)*psz++]; - if (c == (char)-1) - break; - n |= c; - vch.push_back(n); - } - return vch; -} - -vector ParseHex(const string& str) -{ - return ParseHex(str.c_str()); -} - - -void ParseParameters(int argc, char* argv[]) -{ - mapArgs.clear(); - mapMultiArgs.clear(); - for (int i = 1; i < argc; i++) - { - char psz[10000]; - strlcpy(psz, argv[i], sizeof(psz)); - char* pszValue = (char*)""; - if (strchr(psz, '=')) - { - pszValue = strchr(psz, '='); - *pszValue++ = '\0'; - } - #ifdef __WXMSW__ - _strlwr(psz); - if (psz[0] == '/') - psz[0] = '-'; - #endif - if (psz[0] != '-') - break; - mapArgs[psz] = pszValue; - mapMultiArgs[psz].push_back(pszValue); - } -} - - -const char* wxGetTranslation(const char* pszEnglish) -{ -#ifdef GUI - // Wrapper of wxGetTranslation returning the same const char* type as was passed in - static CCriticalSection cs; - CRITICAL_BLOCK(cs) - { - // Look in cache - static map mapCache; - map::iterator mi = mapCache.find(pszEnglish); - if (mi != mapCache.end()) - return (*mi).second; - - // wxWidgets translation - wxString strTranslated = wxGetTranslation(wxString(pszEnglish, wxConvUTF8)); - - // We don't cache unknown strings because caller might be passing in a - // dynamic string and we would keep allocating memory for each variation. - if (strcmp(pszEnglish, strTranslated.utf8_str()) == 0) - return pszEnglish; - - // Add to cache, memory doesn't need to be freed. We only cache because - // we must pass back a pointer to permanently allocated memory. - char* pszCached = new char[strlen(strTranslated.utf8_str())+1]; - strcpy(pszCached, strTranslated.utf8_str()); - mapCache[pszEnglish] = pszCached; - return pszCached; - } - return NULL; -#else - return pszEnglish; -#endif -} - - -bool WildcardMatch(const char* psz, const char* mask) -{ - loop - { - switch (*mask) - { - case '\0': - return (*psz == '\0'); - case '*': - return WildcardMatch(psz, mask+1) || (*psz && WildcardMatch(psz+1, mask)); - case '?': - if (*psz == '\0') - return false; - break; - default: - if (*psz != *mask) - return false; - break; - } - psz++; - mask++; - } -} - -bool WildcardMatch(const string& str, const string& mask) -{ - return WildcardMatch(str.c_str(), mask.c_str()); -} - - - - - - - - -void FormatException(char* pszMessage, std::exception* pex, const char* pszThread) -{ -#ifdef __WXMSW__ - char pszModule[MAX_PATH]; - pszModule[0] = '\0'; - GetModuleFileNameA(NULL, pszModule, sizeof(pszModule)); -#else - const char* pszModule = "bitcoin"; -#endif - if (pex) - snprintf(pszMessage, 1000, - "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread); - else - snprintf(pszMessage, 1000, - "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread); -} - -void LogException(std::exception* pex, const char* pszThread) -{ - char pszMessage[10000]; - FormatException(pszMessage, pex, pszThread); - printf("\n%s", pszMessage); -} - -void PrintException(std::exception* pex, const char* pszThread) -{ - char pszMessage[10000]; - FormatException(pszMessage, pex, pszThread); - printf("\n\n************************\n%s\n", pszMessage); - fprintf(stderr, "\n\n************************\n%s\n", pszMessage); - strMiscWarning = pszMessage; -#ifdef GUI - if (wxTheApp && !fDaemon) - MyMessageBox(pszMessage, "Bitcoin", wxOK | wxICON_ERROR); -#endif - throw; -} - -void ThreadOneMessageBox(string strMessage) -{ - // Skip message boxes if one is already open - static bool fMessageBoxOpen; - if (fMessageBoxOpen) - return; - fMessageBoxOpen = true; - ThreadSafeMessageBox(strMessage, "Bitcoin", wxOK | wxICON_EXCLAMATION); - fMessageBoxOpen = false; -} - -void PrintExceptionContinue(std::exception* pex, const char* pszThread) -{ - char pszMessage[10000]; - FormatException(pszMessage, pex, pszThread); - printf("\n\n************************\n%s\n", pszMessage); - fprintf(stderr, "\n\n************************\n%s\n", pszMessage); - strMiscWarning = pszMessage; -#ifdef GUI - if (wxTheApp && !fDaemon) - boost::thread(boost::bind(ThreadOneMessageBox, string(pszMessage))); -#endif -} - - - - - - - - -#ifdef __WXMSW__ -typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate); - -string MyGetSpecialFolderPath(int nFolder, bool fCreate) -{ - char pszPath[MAX_PATH+100] = ""; - - // SHGetSpecialFolderPath isn't always available on old Windows versions - HMODULE hShell32 = LoadLibraryA("shell32.dll"); - if (hShell32) - { - PSHGETSPECIALFOLDERPATHA pSHGetSpecialFolderPath = - (PSHGETSPECIALFOLDERPATHA)GetProcAddress(hShell32, "SHGetSpecialFolderPathA"); - if (pSHGetSpecialFolderPath) - (*pSHGetSpecialFolderPath)(NULL, pszPath, nFolder, fCreate); - FreeModule(hShell32); - } - - // Backup option - if (pszPath[0] == '\0') - { - if (nFolder == CSIDL_STARTUP) - { - strcpy(pszPath, getenv("USERPROFILE")); - strcat(pszPath, "\\Start Menu\\Programs\\Startup"); - } - else if (nFolder == CSIDL_APPDATA) - { - strcpy(pszPath, getenv("APPDATA")); - } - } - - return pszPath; -} -#endif - -string GetDefaultDataDir() -{ - // Windows: C:\Documents and Settings\username\Application Data\Bitcoin - // Mac: ~/Library/Application Support/Bitcoin - // Unix: ~/.bitcoin -#ifdef __WXMSW__ - // Windows - return MyGetSpecialFolderPath(CSIDL_APPDATA, true) + "\\Bitcoin"; -#else - char* pszHome = getenv("HOME"); - if (pszHome == NULL || strlen(pszHome) == 0) - pszHome = (char*)"/"; - string strHome = pszHome; - if (strHome[strHome.size()-1] != '/') - strHome += '/'; -#ifdef __WXMAC_OSX__ - // Mac - strHome += "Library/Application Support/"; - filesystem::create_directory(strHome.c_str()); - return strHome + "Bitcoin"; -#else - // Unix - return strHome + ".bitcoin"; -#endif -#endif -} - -void GetDataDir(char* pszDir) -{ - // pszDir must be at least MAX_PATH length. - int nVariation; - if (pszSetDataDir[0] != 0) - { - strlcpy(pszDir, pszSetDataDir, MAX_PATH); - nVariation = 0; - } - else - { - // This can be called during exceptions by printf, so we cache the - // value so we don't have to do memory allocations after that. - static char pszCachedDir[MAX_PATH]; - if (pszCachedDir[0] == 0) - strlcpy(pszCachedDir, GetDefaultDataDir().c_str(), sizeof(pszCachedDir)); - strlcpy(pszDir, pszCachedDir, MAX_PATH); - nVariation = 1; - } - if (fTestNet) - { - char* p = pszDir + strlen(pszDir); - if (p > pszDir && p[-1] != '/' && p[-1] != '\\') - *p++ = '/'; - strcpy(p, "testnet"); - nVariation += 2; - } - static bool pfMkdir[4]; - if (!pfMkdir[nVariation]) - { - pfMkdir[nVariation] = true; - boost::filesystem::create_directory(pszDir); - } -} - -string GetDataDir() -{ - char pszDir[MAX_PATH]; - GetDataDir(pszDir); - return pszDir; -} - -string GetConfigFile() -{ - namespace fs = boost::filesystem; - fs::path pathConfig(GetArg("-conf", "bitcoin.conf")); - if (!pathConfig.is_complete()) - pathConfig = fs::path(GetDataDir()) / pathConfig; - return pathConfig.string(); -} - -void ReadConfigFile(map& mapSettingsRet, - map >& mapMultiSettingsRet) -{ - namespace fs = boost::filesystem; - namespace pod = boost::program_options::detail; - - fs::ifstream streamConfig(GetConfigFile()); - if (!streamConfig.good()) - return; - - set setOptions; - setOptions.insert("*"); - - for (pod::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) - { - // Don't overwrite existing settings so command line settings override bitcoin.conf - string strKey = string("-") + it->string_key; - if (mapSettingsRet.count(strKey) == 0) - mapSettingsRet[strKey] = it->value[0]; - mapMultiSettingsRet[strKey].push_back(it->value[0]); - } -} - -string GetPidFile() -{ - namespace fs = boost::filesystem; - fs::path pathConfig(GetArg("-pid", "bitcoind.pid")); - if (!pathConfig.is_complete()) - pathConfig = fs::path(GetDataDir()) / pathConfig; - return pathConfig.string(); -} - -void CreatePidFile(string pidFile, pid_t pid) -{ - FILE* file; - if (file = fopen(pidFile.c_str(), "w")) - { - fprintf(file, "%d\n", pid); - fclose(file); - } -} - -int GetFilesize(FILE* file) -{ - int nSavePos = ftell(file); - int nFilesize = -1; - if (fseek(file, 0, SEEK_END) == 0) - nFilesize = ftell(file); - fseek(file, nSavePos, SEEK_SET); - return nFilesize; -} - -void ShrinkDebugFile() -{ - // Scroll debug.log if it's getting too big - string strFile = GetDataDir() + "/debug.log"; - FILE* file = fopen(strFile.c_str(), "r"); - if (file && GetFilesize(file) > 10 * 1000000) - { - // Restart the file with some of the end - char pch[200000]; - fseek(file, -sizeof(pch), SEEK_END); - int nBytes = fread(pch, 1, sizeof(pch), file); - fclose(file); - if (file = fopen(strFile.c_str(), "w")) - { - fwrite(pch, 1, nBytes, file); - fclose(file); - } - } -} - - - - - - - - -// -// "Never go to sea with two chronometers; take one or three." -// Our three time sources are: -// - System clock -// - Median of other nodes's clocks -// - The user (asking the user to fix the system clock if the first two disagree) -// -int64 GetTime() -{ - return time(NULL); -} - -static int64 nTimeOffset = 0; - -int64 GetAdjustedTime() -{ - return GetTime() + nTimeOffset; -} - -void AddTimeData(unsigned int ip, int64 nTime) -{ - int64 nOffsetSample = nTime - GetTime(); - - // Ignore duplicates - static set setKnown; - if (!setKnown.insert(ip).second) - return; - - // Add data - static vector vTimeOffsets; - if (vTimeOffsets.empty()) - vTimeOffsets.push_back(0); - vTimeOffsets.push_back(nOffsetSample); - printf("Added time data, samples %d, offset %+"PRI64d" (%+"PRI64d" minutes)\n", vTimeOffsets.size(), vTimeOffsets.back(), vTimeOffsets.back()/60); - if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) - { - sort(vTimeOffsets.begin(), vTimeOffsets.end()); - int64 nMedian = vTimeOffsets[vTimeOffsets.size()/2]; - // Only let other nodes change our time by so much - if (abs64(nMedian) < 70 * 60) - { - nTimeOffset = nMedian; - } - else - { - nTimeOffset = 0; - - static bool fDone; - if (!fDone) - { - // If nobody has a time different than ours but within 5 minutes of ours, give a warning - bool fMatch = false; - BOOST_FOREACH(int64 nOffset, vTimeOffsets) - if (nOffset != 0 && abs64(nOffset) < 5 * 60) - fMatch = true; - - if (!fMatch) - { - fDone = true; - string strMessage = _("Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly."); - strMiscWarning = strMessage; - printf("*** %s\n", strMessage.c_str()); - boost::thread(boost::bind(ThreadSafeMessageBox, strMessage+" ", string("Bitcoin"), wxOK | wxICON_EXCLAMATION, (wxWindow*)NULL, -1, -1)); - } - } - } - BOOST_FOREACH(int64 n, vTimeOffsets) - printf("%+"PRI64d" ", n); - printf("| nTimeOffset = %+"PRI64d" (%+"PRI64d" minutes)\n", nTimeOffset, nTimeOffset/60); - } -} - - - - - - - - - -string FormatVersion(int nVersion) -{ - if (nVersion%100 == 0) - return strprintf("%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100); - else - return strprintf("%d.%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100, nVersion%100); -} - -string FormatFullVersion() -{ - string s = FormatVersion(VERSION) + pszSubVer; - if (VERSION_IS_BETA) - s += _("-beta"); - return s; -} - - - - - diff --git a/cryptopp/include/cryptopp/config.h b/cryptopp/include/cryptopp/config.h deleted file mode 100644 index 0737027f41..0000000000 --- a/cryptopp/include/cryptopp/config.h +++ /dev/null @@ -1,462 +0,0 @@ -#ifndef CRYPTOPP_CONFIG_H -#define CRYPTOPP_CONFIG_H - -//// Bitcoin: disable SSE2 on 32-bit -#if !defined(_M_X64) && !defined(__x86_64__) -#define CRYPTOPP_DISABLE_SSE2 1 -#endif -//////////// end of Bitcoin changes - - -// ***************** Important Settings ******************** - -// define this if running on a big-endian CPU -#if !defined(IS_LITTLE_ENDIAN) && (defined(__BIG_ENDIAN__) || defined(__sparc) || defined(__sparc__) || defined(__hppa__) || defined(__mips__) || (defined(__MWERKS__) && !defined(__INTEL__))) -# define IS_BIG_ENDIAN -#endif - -// define this if running on a little-endian CPU -// big endian will be assumed if IS_LITTLE_ENDIAN is not defined -#ifndef IS_BIG_ENDIAN -# define IS_LITTLE_ENDIAN -#endif - -// define this if you want to disable all OS-dependent features, -// such as sockets and OS-provided random number generators -// #define NO_OS_DEPENDENCE - -// Define this to use features provided by Microsoft's CryptoAPI. -// Currently the only feature used is random number generation. -// This macro will be ignored if NO_OS_DEPENDENCE is defined. -#define USE_MS_CRYPTOAPI - -// Define this to 1 to enforce the requirement in FIPS 186-2 Change Notice 1 that only 1024 bit moduli be used -#ifndef DSA_1024_BIT_MODULUS_ONLY -# define DSA_1024_BIT_MODULUS_ONLY 1 -#endif - -// ***************** Less Important Settings *************** - -// define this to retain (as much as possible) old deprecated function and class names -// #define CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - -#define GZIP_OS_CODE 0 - -// Try this if your CPU has 256K internal cache or a slow multiply instruction -// and you want a (possibly) faster IDEA implementation using log tables -// #define IDEA_LARGECACHE - -// Define this if, for the linear congruential RNG, you want to use -// the original constants as specified in S.K. Park and K.W. Miller's -// CACM paper. -// #define LCRNG_ORIGINAL_NUMBERS - -// choose which style of sockets to wrap (mostly useful for cygwin which has both) -#define PREFER_BERKELEY_STYLE_SOCKETS -// #define PREFER_WINDOWS_STYLE_SOCKETS - -// set the name of Rijndael cipher, was "Rijndael" before version 5.3 -#define CRYPTOPP_RIJNDAEL_NAME "AES" - -// ***************** Important Settings Again ******************** -// But the defaults should be ok. - -// namespace support is now required -#ifdef NO_NAMESPACE -# error namespace support is now required -#endif - -// Define this to workaround a Microsoft CryptoAPI bug where -// each call to CryptAcquireContext causes a 100 KB memory leak. -// Defining this will cause Crypto++ to make only one call to CryptAcquireContext. -#define WORKAROUND_MS_BUG_Q258000 - -#ifdef CRYPTOPP_DOXYGEN_PROCESSING -// Avoid putting "CryptoPP::" in front of everything in Doxygen output -# define CryptoPP -# define NAMESPACE_BEGIN(x) -# define NAMESPACE_END -// Get Doxygen to generate better documentation for these typedefs -# define DOCUMENTED_TYPEDEF(x, y) class y : public x {}; -#else -# define NAMESPACE_BEGIN(x) namespace x { -# define NAMESPACE_END } -# define DOCUMENTED_TYPEDEF(x, y) typedef x y; -#endif -#define ANONYMOUS_NAMESPACE_BEGIN namespace { -#define USING_NAMESPACE(x) using namespace x; -#define DOCUMENTED_NAMESPACE_BEGIN(x) namespace x { -#define DOCUMENTED_NAMESPACE_END } - -// What is the type of the third parameter to bind? -// For Unix, the new standard is ::socklen_t (typically unsigned int), and the old standard is int. -// Unfortunately there is no way to tell whether or not socklen_t is defined. -// To work around this, TYPE_OF_SOCKLEN_T is a macro so that you can change it from the makefile. -#ifndef TYPE_OF_SOCKLEN_T -# if defined(_WIN32) || defined(__CYGWIN__) -# define TYPE_OF_SOCKLEN_T int -# else -# define TYPE_OF_SOCKLEN_T ::socklen_t -# endif -#endif - -#if defined(__CYGWIN__) && defined(PREFER_WINDOWS_STYLE_SOCKETS) -# define __USE_W32_SOCKETS -#endif - -typedef unsigned char byte; // put in global namespace to avoid ambiguity with other byte typedefs - -NAMESPACE_BEGIN(CryptoPP) - -typedef unsigned short word16; -typedef unsigned int word32; - -#if defined(_MSC_VER) || defined(__BORLANDC__) - typedef unsigned __int64 word64; - #define W64LIT(x) x##ui64 -#else - typedef unsigned long long word64; - #define W64LIT(x) x##ULL -#endif - -// define large word type, used for file offsets and such -typedef word64 lword; -const lword LWORD_MAX = W64LIT(0xffffffffffffffff); - -#ifdef __GNUC__ - #define CRYPTOPP_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) -#endif - -// define hword, word, and dword. these are used for multiprecision integer arithmetic -// Intel compiler won't have _umul128 until version 10.0. See http://softwarecommunity.intel.com/isn/Community/en-US/forums/thread/30231625.aspx -#if (defined(_MSC_VER) && (!defined(__INTEL_COMPILER) || __INTEL_COMPILER >= 1000) && (defined(_M_X64) || defined(_M_IA64))) || (defined(__DECCXX) && defined(__alpha__)) || (defined(__INTEL_COMPILER) && defined(__x86_64__)) || (defined(__SUNPRO_CC) && defined(__x86_64__)) - typedef word32 hword; - typedef word64 word; -#else - #define CRYPTOPP_NATIVE_DWORD_AVAILABLE - #if defined(__alpha__) || defined(__ia64__) || defined(_ARCH_PPC64) || defined(__x86_64__) || defined(__mips64) || defined(__sparc64__) - #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !(CRYPTOPP_GCC_VERSION == 40001 && defined(__APPLE__)) && CRYPTOPP_GCC_VERSION >= 30400 - // GCC 4.0.1 on MacOS X is missing __umodti3 and __udivti3 - // mode(TI) division broken on amd64 with GCC earlier than GCC 3.4 - typedef word32 hword; - typedef word64 word; - typedef __uint128_t dword; - typedef __uint128_t word128; - #define CRYPTOPP_WORD128_AVAILABLE - #else - // if we're here, it means we're on a 64-bit CPU but we don't have a way to obtain 128-bit multiplication results - typedef word16 hword; - typedef word32 word; - typedef word64 dword; - #endif - #else - // being here means the native register size is probably 32 bits or less - #define CRYPTOPP_BOOL_SLOW_WORD64 1 - typedef word16 hword; - typedef word32 word; - typedef word64 dword; - #endif -#endif -#ifndef CRYPTOPP_BOOL_SLOW_WORD64 - #define CRYPTOPP_BOOL_SLOW_WORD64 0 -#endif - -const unsigned int WORD_SIZE = sizeof(word); -const unsigned int WORD_BITS = WORD_SIZE * 8; - -NAMESPACE_END - -#ifndef CRYPTOPP_L1_CACHE_LINE_SIZE - // This should be a lower bound on the L1 cache line size. It's used for defense against timing attacks. - #if defined(_M_X64) || defined(__x86_64__) - #define CRYPTOPP_L1_CACHE_LINE_SIZE 64 - #else - // L1 cache line size is 32 on Pentium III and earlier - #define CRYPTOPP_L1_CACHE_LINE_SIZE 32 - #endif -#endif - -#if defined(_MSC_VER) - #if _MSC_VER == 1200 - #include - #endif - #if _MSC_VER > 1200 || defined(_mm_free) - #define CRYPTOPP_MSVC6PP_OR_LATER // VC 6 processor pack or later - #else - #define CRYPTOPP_MSVC6_NO_PP // VC 6 without processor pack - #endif -#endif - -#ifndef CRYPTOPP_ALIGN_DATA - #if defined(CRYPTOPP_MSVC6PP_OR_LATER) - #define CRYPTOPP_ALIGN_DATA(x) __declspec(align(x)) - #elif defined(__GNUC__) - #define CRYPTOPP_ALIGN_DATA(x) __attribute__((aligned(x))) - #else - #define CRYPTOPP_ALIGN_DATA(x) - #endif -#endif - -#ifndef CRYPTOPP_SECTION_ALIGN16 - #if defined(__GNUC__) && !defined(__APPLE__) - // the alignment attribute doesn't seem to work without this section attribute when -fdata-sections is turned on - #define CRYPTOPP_SECTION_ALIGN16 __attribute__((section ("CryptoPP_Align16"))) - #else - #define CRYPTOPP_SECTION_ALIGN16 - #endif -#endif - -#if defined(_MSC_VER) || defined(__fastcall) - #define CRYPTOPP_FASTCALL __fastcall -#else - #define CRYPTOPP_FASTCALL -#endif - -// VC60 workaround: it doesn't allow typename in some places -#if defined(_MSC_VER) && (_MSC_VER < 1300) -#define CPP_TYPENAME -#else -#define CPP_TYPENAME typename -#endif - -// VC60 workaround: can't cast unsigned __int64 to float or double -#if defined(_MSC_VER) && !defined(CRYPTOPP_MSVC6PP_OR_LATER) -#define CRYPTOPP_VC6_INT64 (__int64) -#else -#define CRYPTOPP_VC6_INT64 -#endif - -#ifdef _MSC_VER -#define CRYPTOPP_NO_VTABLE __declspec(novtable) -#else -#define CRYPTOPP_NO_VTABLE -#endif - -#ifdef _MSC_VER - // 4231: nonstandard extension used : 'extern' before template explicit instantiation - // 4250: dominance - // 4251: member needs to have dll-interface - // 4275: base needs to have dll-interface - // 4660: explicitly instantiating a class that's already implicitly instantiated - // 4661: no suitable definition provided for explicit template instantiation request - // 4786: identifer was truncated in debug information - // 4355: 'this' : used in base member initializer list - // 4910: '__declspec(dllexport)' and 'extern' are incompatible on an explicit instantiation -# pragma warning(disable: 4231 4250 4251 4275 4660 4661 4786 4355 4910) -#endif - -#ifdef __BORLANDC__ -// 8037: non-const function called for const object. needed to work around BCB2006 bug -# pragma warn -8037 -#endif - -#if (defined(_MSC_VER) && _MSC_VER <= 1300) || defined(__MWERKS__) || defined(_STLPORT_VERSION) -#define CRYPTOPP_DISABLE_UNCAUGHT_EXCEPTION -#endif - -#ifndef CRYPTOPP_DISABLE_UNCAUGHT_EXCEPTION -#define CRYPTOPP_UNCAUGHT_EXCEPTION_AVAILABLE -#endif - -#ifdef CRYPTOPP_DISABLE_X86ASM // for backwards compatibility: this macro had both meanings -#define CRYPTOPP_DISABLE_ASM -#define CRYPTOPP_DISABLE_SSE2 -#endif - -#if !defined(CRYPTOPP_DISABLE_ASM) && ((defined(_MSC_VER) && defined(_M_IX86)) || (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))) - #define CRYPTOPP_X86_ASM_AVAILABLE - - #if !defined(CRYPTOPP_DISABLE_SSE2) && (defined(CRYPTOPP_MSVC6PP_OR_LATER) || CRYPTOPP_GCC_VERSION >= 30300) - #define CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE 1 - #else - #define CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE 0 - #endif - - // SSSE3 was actually introduced in GNU as 2.17, which was released 6/23/2006, but we can't tell what version of binutils is installed. - // GCC 4.1.2 was released on 2/13/2007, so we'll use that as a proxy for the binutils version. - #if !defined(CRYPTOPP_DISABLE_SSSE3) && (_MSC_VER >= 1400 || CRYPTOPP_GCC_VERSION >= 40102) - #define CRYPTOPP_BOOL_SSSE3_ASM_AVAILABLE 1 - #else - #define CRYPTOPP_BOOL_SSSE3_ASM_AVAILABLE 0 - #endif -#endif - -#if !defined(CRYPTOPP_DISABLE_ASM) && defined(_MSC_VER) && defined(_M_X64) - #define CRYPTOPP_X64_MASM_AVAILABLE -#endif - -#if !defined(CRYPTOPP_DISABLE_ASM) && defined(__GNUC__) && defined(__x86_64__) - #define CRYPTOPP_X64_ASM_AVAILABLE -#endif - -#if !defined(CRYPTOPP_DISABLE_SSE2) && (defined(CRYPTOPP_MSVC6PP_OR_LATER) || defined(__SSE2__)) - #define CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE 1 -#else - #define CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE 0 -#endif - -#if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE || CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE) - #define CRYPTOPP_BOOL_ALIGN16_ENABLED 1 -#else - #define CRYPTOPP_BOOL_ALIGN16_ENABLED 0 -#endif - -// how to allocate 16-byte aligned memory (for SSE2) -#if defined(CRYPTOPP_MSVC6PP_OR_LATER) - #define CRYPTOPP_MM_MALLOC_AVAILABLE -#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define CRYPTOPP_MALLOC_ALIGNMENT_IS_16 -#elif defined(__linux__) || defined(__sun__) || defined(__CYGWIN__) - #define CRYPTOPP_MEMALIGN_AVAILABLE -#else - #define CRYPTOPP_NO_ALIGNED_ALLOC -#endif - -// how to disable inlining -#if defined(_MSC_VER) && _MSC_VER >= 1300 -# define CRYPTOPP_NOINLINE_DOTDOTDOT -# define CRYPTOPP_NOINLINE __declspec(noinline) -#elif defined(__GNUC__) -# define CRYPTOPP_NOINLINE_DOTDOTDOT -# define CRYPTOPP_NOINLINE __attribute__((noinline)) -#else -# define CRYPTOPP_NOINLINE_DOTDOTDOT ... -# define CRYPTOPP_NOINLINE -#endif - -// how to declare class constants -#if (defined(_MSC_VER) && _MSC_VER <= 1300) || defined(__INTEL_COMPILER) -# define CRYPTOPP_CONSTANT(x) enum {x}; -#else -# define CRYPTOPP_CONSTANT(x) static const int x; -#endif - -#if defined(_M_X64) || defined(__x86_64__) - #define CRYPTOPP_BOOL_X64 1 -#else - #define CRYPTOPP_BOOL_X64 0 -#endif - -// see http://predef.sourceforge.net/prearch.html -#if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(_X86_) || defined(__I86__) || defined(__INTEL__) - #define CRYPTOPP_BOOL_X86 1 -#else - #define CRYPTOPP_BOOL_X86 0 -#endif - -#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X86 || defined(__powerpc__) - #define CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS -#endif - -#define CRYPTOPP_VERSION 560 - -// ***************** determine availability of OS features ******************** - -#ifndef NO_OS_DEPENDENCE - -#if defined(_WIN32) || defined(__CYGWIN__) -#define CRYPTOPP_WIN32_AVAILABLE -#endif - -#if defined(__unix__) || defined(__MACH__) || defined(__NetBSD__) || defined(__sun) -#define CRYPTOPP_UNIX_AVAILABLE -#endif - -#if defined(CRYPTOPP_WIN32_AVAILABLE) || defined(CRYPTOPP_UNIX_AVAILABLE) -# define HIGHRES_TIMER_AVAILABLE -#endif - -#ifdef CRYPTOPP_UNIX_AVAILABLE -# define HAS_BERKELEY_STYLE_SOCKETS -#endif - -#ifdef CRYPTOPP_WIN32_AVAILABLE -# define HAS_WINDOWS_STYLE_SOCKETS -#endif - -#if defined(HIGHRES_TIMER_AVAILABLE) && (defined(HAS_BERKELEY_STYLE_SOCKETS) || defined(HAS_WINDOWS_STYLE_SOCKETS)) -# define SOCKETS_AVAILABLE -#endif - -#if defined(HAS_WINDOWS_STYLE_SOCKETS) && (!defined(HAS_BERKELEY_STYLE_SOCKETS) || defined(PREFER_WINDOWS_STYLE_SOCKETS)) -# define USE_WINDOWS_STYLE_SOCKETS -#else -# define USE_BERKELEY_STYLE_SOCKETS -#endif - -#if defined(HIGHRES_TIMER_AVAILABLE) && defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(USE_BERKELEY_STYLE_SOCKETS) -# define WINDOWS_PIPES_AVAILABLE -#endif - -#if defined(CRYPTOPP_WIN32_AVAILABLE) && defined(USE_MS_CRYPTOAPI) -# define NONBLOCKING_RNG_AVAILABLE -# define OS_RNG_AVAILABLE -#endif - -#if defined(CRYPTOPP_UNIX_AVAILABLE) || defined(CRYPTOPP_DOXYGEN_PROCESSING) -# define NONBLOCKING_RNG_AVAILABLE -# define BLOCKING_RNG_AVAILABLE -# define OS_RNG_AVAILABLE -# define HAS_PTHREADS -# define THREADS_AVAILABLE -#endif - -#ifdef CRYPTOPP_WIN32_AVAILABLE -# define HAS_WINTHREADS -# define THREADS_AVAILABLE -#endif - -#endif // NO_OS_DEPENDENCE - -// ***************** DLL related ******************** - -#ifdef CRYPTOPP_WIN32_AVAILABLE - -#ifdef CRYPTOPP_EXPORTS -#define CRYPTOPP_IS_DLL -#define CRYPTOPP_DLL __declspec(dllexport) -#elif defined(CRYPTOPP_IMPORTS) -#define CRYPTOPP_IS_DLL -#define CRYPTOPP_DLL __declspec(dllimport) -#else -#define CRYPTOPP_DLL -#endif - -#define CRYPTOPP_API __cdecl - -#else // CRYPTOPP_WIN32_AVAILABLE - -#define CRYPTOPP_DLL -#define CRYPTOPP_API - -#endif // CRYPTOPP_WIN32_AVAILABLE - -#if defined(__MWERKS__) -#define CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS extern class CRYPTOPP_DLL -#elif defined(__BORLANDC__) || defined(__SUNPRO_CC) -#define CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS template class CRYPTOPP_DLL -#else -#define CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS extern template class CRYPTOPP_DLL -#endif - -#if defined(CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES) && !defined(CRYPTOPP_IMPORTS) -#define CRYPTOPP_DLL_TEMPLATE_CLASS template class CRYPTOPP_DLL -#else -#define CRYPTOPP_DLL_TEMPLATE_CLASS CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS -#endif - -#if defined(__MWERKS__) -#define CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS extern class -#elif defined(__BORLANDC__) || defined(__SUNPRO_CC) -#define CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS template class -#else -#define CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS extern template class -#endif - -#if defined(CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES) && !defined(CRYPTOPP_EXPORTS) -#define CRYPTOPP_STATIC_TEMPLATE_CLASS template class -#else -#define CRYPTOPP_STATIC_TEMPLATE_CLASS CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS -#endif - -#endif diff --git a/cryptopp/include/cryptopp/cpu.h b/cryptopp/include/cryptopp/cpu.h deleted file mode 100644 index 113b8a1f3b..0000000000 --- a/cryptopp/include/cryptopp/cpu.h +++ /dev/null @@ -1,263 +0,0 @@ -#ifndef CRYPTOPP_CPU_H -#define CRYPTOPP_CPU_H - -#ifdef CRYPTOPP_GENERATE_X64_MASM - -#define CRYPTOPP_X86_ASM_AVAILABLE -#define CRYPTOPP_BOOL_X64 1 -#define CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE 1 -#define NAMESPACE_END - -#else - -#include "cryptopp/config.h" - -#ifdef CRYPTOPP_MSVC6PP_OR_LATER - #include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || (_MSC_VER >= 1400 && CRYPTOPP_BOOL_X64) - -#define CRYPTOPP_CPUID_AVAILABLE - -// these should not be used directly -extern CRYPTOPP_DLL bool g_x86DetectionDone; -extern CRYPTOPP_DLL bool g_hasSSE2; -extern CRYPTOPP_DLL bool g_hasISSE; -extern CRYPTOPP_DLL bool g_hasMMX; -extern CRYPTOPP_DLL bool g_hasSSSE3; -extern CRYPTOPP_DLL bool g_isP4; -extern CRYPTOPP_DLL word32 g_cacheLineSize; -CRYPTOPP_DLL void CRYPTOPP_API DetectX86Features(); - -CRYPTOPP_DLL bool CRYPTOPP_API CpuId(word32 input, word32 *output); - -#if CRYPTOPP_BOOL_X64 -inline bool HasSSE2() {return true;} -inline bool HasISSE() {return true;} -inline bool HasMMX() {return true;} -#else - -inline bool HasSSE2() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_hasSSE2; -} - -inline bool HasISSE() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_hasISSE; -} - -inline bool HasMMX() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_hasMMX; -} - -#endif - -inline bool HasSSSE3() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_hasSSSE3; -} - -inline bool IsP4() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_isP4; -} - -inline int GetCacheLineSize() -{ - if (!g_x86DetectionDone) - DetectX86Features(); - return g_cacheLineSize; -} - -#else - -inline int GetCacheLineSize() -{ - return CRYPTOPP_L1_CACHE_LINE_SIZE; -} - -inline bool HasSSSE3() {return false;} -inline bool IsP4() {return false;} - -// assume MMX and SSE2 if intrinsics are enabled -#if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE || CRYPTOPP_BOOL_X64 -inline bool HasSSE2() {return true;} -inline bool HasISSE() {return true;} -inline bool HasMMX() {return true;} -#else -inline bool HasSSE2() {return false;} -inline bool HasISSE() {return false;} -inline bool HasMMX() {return false;} -#endif - -#endif // #ifdef CRYPTOPP_X86_ASM_AVAILABLE || _MSC_VER >= 1400 - -#endif - -#ifdef CRYPTOPP_GENERATE_X64_MASM - #define AS1(x) x*newline* - #define AS2(x, y) x, y*newline* - #define AS3(x, y, z) x, y, z*newline* - #define ASS(x, y, a, b, c, d) x, y, a*64+b*16+c*4+d*newline* - #define ASL(x) label##x:*newline* - #define ASJ(x, y, z) x label##y*newline* - #define ASC(x, y) x label##y*newline* - #define AS_HEX(y) 0##y##h -#elif defined(__GNUC__) - // define these in two steps to allow arguments to be expanded - #define GNU_AS1(x) #x ";" - #define GNU_AS2(x, y) #x ", " #y ";" - #define GNU_AS3(x, y, z) #x ", " #y ", " #z ";" - #define GNU_ASL(x) "\n" #x ":" - #define GNU_ASJ(x, y, z) #x " " #y #z ";" - #define AS1(x) GNU_AS1(x) - #define AS2(x, y) GNU_AS2(x, y) - #define AS3(x, y, z) GNU_AS3(x, y, z) - #define ASS(x, y, a, b, c, d) #x ", " #y ", " #a "*64+" #b "*16+" #c "*4+" #d ";" - #define ASL(x) GNU_ASL(x) - #define ASJ(x, y, z) GNU_ASJ(x, y, z) - #define ASC(x, y) #x " " #y ";" - #define CRYPTOPP_NAKED - #define AS_HEX(y) 0x##y -#else - #define AS1(x) __asm {x} - #define AS2(x, y) __asm {x, y} - #define AS3(x, y, z) __asm {x, y, z} - #define ASS(x, y, a, b, c, d) __asm {x, y, _MM_SHUFFLE(a, b, c, d)} - #define ASL(x) __asm {label##x:} - #define ASJ(x, y, z) __asm {x label##y} - #define ASC(x, y) __asm {x label##y} - #define CRYPTOPP_NAKED __declspec(naked) - #define AS_HEX(y) 0x##y -#endif - -#define IF0(y) -#define IF1(y) y - -#ifdef CRYPTOPP_GENERATE_X64_MASM -#define ASM_MOD(x, y) ((x) MOD (y)) -#define XMMWORD_PTR XMMWORD PTR -#else -// GNU assembler doesn't seem to have mod operator -#define ASM_MOD(x, y) ((x)-((x)/(y))*(y)) -// GAS 2.15 doesn't support XMMWORD PTR. it seems necessary only for MASM -#define XMMWORD_PTR -#endif - -#if CRYPTOPP_BOOL_X86 - #define AS_REG_1 ecx - #define AS_REG_2 edx - #define AS_REG_3 esi - #define AS_REG_4 edi - #define AS_REG_5 eax - #define AS_REG_6 ebx - #define AS_REG_7 ebp - #define AS_REG_1d ecx - #define AS_REG_2d edx - #define AS_REG_3d esi - #define AS_REG_4d edi - #define AS_REG_5d eax - #define AS_REG_6d ebx - #define AS_REG_7d ebp - #define WORD_SZ 4 - #define WORD_REG(x) e##x - #define WORD_PTR DWORD PTR - #define AS_PUSH_IF86(x) AS1(push e##x) - #define AS_POP_IF86(x) AS1(pop e##x) - #define AS_JCXZ jecxz -#elif CRYPTOPP_BOOL_X64 - #ifdef CRYPTOPP_GENERATE_X64_MASM - #define AS_REG_1 rcx - #define AS_REG_2 rdx - #define AS_REG_3 r8 - #define AS_REG_4 r9 - #define AS_REG_5 rax - #define AS_REG_6 r10 - #define AS_REG_7 r11 - #define AS_REG_1d ecx - #define AS_REG_2d edx - #define AS_REG_3d r8d - #define AS_REG_4d r9d - #define AS_REG_5d eax - #define AS_REG_6d r10d - #define AS_REG_7d r11d - #else - #define AS_REG_1 rdi - #define AS_REG_2 rsi - #define AS_REG_3 rdx - #define AS_REG_4 rcx - #define AS_REG_5 r8 - #define AS_REG_6 r9 - #define AS_REG_7 r10 - #define AS_REG_1d edi - #define AS_REG_2d esi - #define AS_REG_3d edx - #define AS_REG_4d ecx - #define AS_REG_5d r8d - #define AS_REG_6d r9d - #define AS_REG_7d r10d - #endif - #define WORD_SZ 8 - #define WORD_REG(x) r##x - #define WORD_PTR QWORD PTR - #define AS_PUSH_IF86(x) - #define AS_POP_IF86(x) - #define AS_JCXZ jrcxz -#endif - -// helper macro for stream cipher output -#define AS_XMM_OUTPUT4(labelPrefix, inputPtr, outputPtr, x0, x1, x2, x3, t, p0, p1, p2, p3, increment)\ - AS2( test inputPtr, inputPtr)\ - ASC( jz, labelPrefix##3)\ - AS2( test inputPtr, 15)\ - ASC( jnz, labelPrefix##7)\ - AS2( pxor xmm##x0, [inputPtr+p0*16])\ - AS2( pxor xmm##x1, [inputPtr+p1*16])\ - AS2( pxor xmm##x2, [inputPtr+p2*16])\ - AS2( pxor xmm##x3, [inputPtr+p3*16])\ - AS2( add inputPtr, increment*16)\ - ASC( jmp, labelPrefix##3)\ - ASL(labelPrefix##7)\ - AS2( movdqu xmm##t, [inputPtr+p0*16])\ - AS2( pxor xmm##x0, xmm##t)\ - AS2( movdqu xmm##t, [inputPtr+p1*16])\ - AS2( pxor xmm##x1, xmm##t)\ - AS2( movdqu xmm##t, [inputPtr+p2*16])\ - AS2( pxor xmm##x2, xmm##t)\ - AS2( movdqu xmm##t, [inputPtr+p3*16])\ - AS2( pxor xmm##x3, xmm##t)\ - AS2( add inputPtr, increment*16)\ - ASL(labelPrefix##3)\ - AS2( test outputPtr, 15)\ - ASC( jnz, labelPrefix##8)\ - AS2( movdqa [outputPtr+p0*16], xmm##x0)\ - AS2( movdqa [outputPtr+p1*16], xmm##x1)\ - AS2( movdqa [outputPtr+p2*16], xmm##x2)\ - AS2( movdqa [outputPtr+p3*16], xmm##x3)\ - ASC( jmp, labelPrefix##9)\ - ASL(labelPrefix##8)\ - AS2( movdqu [outputPtr+p0*16], xmm##x0)\ - AS2( movdqu [outputPtr+p1*16], xmm##x1)\ - AS2( movdqu [outputPtr+p2*16], xmm##x2)\ - AS2( movdqu [outputPtr+p3*16], xmm##x3)\ - ASL(labelPrefix##9)\ - AS2( add outputPtr, increment*16) - -NAMESPACE_END - -#endif diff --git a/cryptopp/include/cryptopp/cryptlib.h b/cryptopp/include/cryptopp/cryptlib.h deleted file mode 100644 index 1461af7b7b..0000000000 --- a/cryptopp/include/cryptopp/cryptlib.h +++ /dev/null @@ -1,1668 +0,0 @@ -// cryptlib.h - written and placed in the public domain by Wei Dai -/*! \file - This file contains the declarations for the abstract base - classes that provide a uniform interface to this library. -*/ - -/*! \mainpage Crypto++ Library 5.6.0 API Reference -
-
Abstract Base Classes
- cryptlib.h -
Authenticated Encryption
- AuthenticatedSymmetricCipherDocumentation -
Symmetric Ciphers
- SymmetricCipherDocumentation -
Hash Functions
- SHA1, SHA224, SHA256, SHA384, SHA512, Tiger, Whirlpool, RIPEMD160, RIPEMD320, RIPEMD128, RIPEMD256, Weak1::MD2, Weak1::MD4, Weak1::MD5 -
Non-Cryptographic Checksums
- CRC32, Adler32 -
Message Authentication Codes
- VMAC, HMAC, CBC_MAC, CMAC, DMAC, TTMAC, GCM (GMAC) -
Random Number Generators
- NullRNG(), LC_RNG, RandomPool, BlockingRng, NonblockingRng, AutoSeededRandomPool, AutoSeededX917RNG, DefaultAutoSeededRNG -
Password-based Cryptography
- PasswordBasedKeyDerivationFunction -
Public Key Cryptosystems
- DLIES, ECIES, LUCES, RSAES, RabinES, LUC_IES -
Public Key Signature Schemes
- DSA, GDSA, ECDSA, NR, ECNR, LUCSS, RSASS, RSASS_ISO, RabinSS, RWSS, ESIGN -
Key Agreement
- #DH, DH2, #MQV, ECDH, ECMQV, XTR_DH -
Algebraic Structures
- Integer, PolynomialMod2, PolynomialOver, RingOfPolynomialsOver, - ModularArithmetic, MontgomeryRepresentation, GFP2_ONB, - GF2NP, GF256, GF2_32, EC2N, ECP -
Secret Sharing and Information Dispersal
- SecretSharing, SecretRecovery, InformationDispersal, InformationRecovery -
Compression
- Deflator, Inflator, Gzip, Gunzip, ZlibCompressor, ZlibDecompressor -
Input Source Classes
- StringSource, ArraySource, FileSource, SocketSource, WindowsPipeSource, RandomNumberSource -
Output Sink Classes
- StringSinkTemplate, ArraySink, FileSink, SocketSink, WindowsPipeSink, RandomNumberSink -
Filter Wrappers
- StreamTransformationFilter, HashFilter, HashVerificationFilter, SignerFilter, SignatureVerificationFilter -
Binary to Text Encoders and Decoders
- HexEncoder, HexDecoder, Base64Encoder, Base64Decoder, Base32Encoder, Base32Decoder -
Wrappers for OS features
- Timer, Socket, WindowsHandle, ThreadLocalStorage, ThreadUserTimer -
FIPS 140 related
- fips140.h -
- -In the FIPS 140-2 validated DLL version of Crypto++, only the following implementation class are available. -
-
Block Ciphers
- AES, DES_EDE2, DES_EDE3, SKIPJACK -
Cipher Modes (replace template parameter BC with one of the block ciphers above)
- ECB_Mode\, CTR_Mode\, CBC_Mode\, CFB_FIPS_Mode\, OFB_Mode\ -
Hash Functions
- SHA1, SHA224, SHA256, SHA384, SHA512 -
Public Key Signature Schemes (replace template parameter H with one of the hash functions above)
- RSASS\, RSASS\, RSASS_ISO\, RWSS\, DSA, ECDSA\, ECDSA\ -
Message Authentication Codes (replace template parameter H with one of the hash functions above)
- HMAC\, CBC_MAC\, CBC_MAC\ -
Random Number Generators
- DefaultAutoSeededRNG (AutoSeededX917RNG\) -
Key Agreement
- #DH -
Public Key Cryptosystems
- RSAES\ \> -
- -

This reference manual is a work in progress. Some classes are still lacking detailed descriptions. -

Click here to download a zip archive containing this manual. -

Thanks to Ryan Phillips for providing the Doxygen configuration file -and getting me started with this manual. -*/ - -#ifndef CRYPTOPP_CRYPTLIB_H -#define CRYPTOPP_CRYPTLIB_H - -#include "cryptopp/config.h" -#include "cryptopp/stdcpp.h" - -NAMESPACE_BEGIN(CryptoPP) - -// forward declarations -class Integer; -class RandomNumberGenerator; -class BufferedTransformation; - -//! used to specify a direction for a cipher to operate in (encrypt or decrypt) -enum CipherDir {ENCRYPTION, DECRYPTION}; - -//! used to represent infinite time -const unsigned long INFINITE_TIME = ULONG_MAX; - -// VC60 workaround: using enums as template parameters causes problems -template -struct EnumToType -{ - static ENUM_TYPE ToEnum() {return (ENUM_TYPE)VALUE;} -}; - -enum ByteOrder {LITTLE_ENDIAN_ORDER = 0, BIG_ENDIAN_ORDER = 1}; -typedef EnumToType LittleEndian; -typedef EnumToType BigEndian; - -//! base class for all exceptions thrown by Crypto++ -class CRYPTOPP_DLL Exception : public std::exception -{ -public: - //! error types - enum ErrorType { - //! a method is not implemented - NOT_IMPLEMENTED, - //! invalid function argument - INVALID_ARGUMENT, - //! BufferedTransformation received a Flush(true) signal but can't flush buffers - CANNOT_FLUSH, - //! data integerity check (such as CRC or MAC) failed - DATA_INTEGRITY_CHECK_FAILED, - //! received input data that doesn't conform to expected format - INVALID_DATA_FORMAT, - //! error reading from input device or writing to output device - IO_ERROR, - //! some error not belong to any of the above categories - OTHER_ERROR - }; - - explicit Exception(ErrorType errorType, const std::string &s) : m_errorType(errorType), m_what(s) {} - virtual ~Exception() throw() {} - const char *what() const throw() {return (m_what.c_str());} - const std::string &GetWhat() const {return m_what;} - void SetWhat(const std::string &s) {m_what = s;} - ErrorType GetErrorType() const {return m_errorType;} - void SetErrorType(ErrorType errorType) {m_errorType = errorType;} - -private: - ErrorType m_errorType; - std::string m_what; -}; - -//! exception thrown when an invalid argument is detected -class CRYPTOPP_DLL InvalidArgument : public Exception -{ -public: - explicit InvalidArgument(const std::string &s) : Exception(INVALID_ARGUMENT, s) {} -}; - -//! exception thrown when input data is received that doesn't conform to expected format -class CRYPTOPP_DLL InvalidDataFormat : public Exception -{ -public: - explicit InvalidDataFormat(const std::string &s) : Exception(INVALID_DATA_FORMAT, s) {} -}; - -//! exception thrown by decryption filters when trying to decrypt an invalid ciphertext -class CRYPTOPP_DLL InvalidCiphertext : public InvalidDataFormat -{ -public: - explicit InvalidCiphertext(const std::string &s) : InvalidDataFormat(s) {} -}; - -//! exception thrown by a class if a non-implemented method is called -class CRYPTOPP_DLL NotImplemented : public Exception -{ -public: - explicit NotImplemented(const std::string &s) : Exception(NOT_IMPLEMENTED, s) {} -}; - -//! exception thrown by a class when Flush(true) is called but it can't completely flush its buffers -class CRYPTOPP_DLL CannotFlush : public Exception -{ -public: - explicit CannotFlush(const std::string &s) : Exception(CANNOT_FLUSH, s) {} -}; - -//! error reported by the operating system -class CRYPTOPP_DLL OS_Error : public Exception -{ -public: - OS_Error(ErrorType errorType, const std::string &s, const std::string& operation, int errorCode) - : Exception(errorType, s), m_operation(operation), m_errorCode(errorCode) {} - ~OS_Error() throw() {} - - // the operating system API that reported the error - const std::string & GetOperation() const {return m_operation;} - // the error code return by the operating system - int GetErrorCode() const {return m_errorCode;} - -protected: - std::string m_operation; - int m_errorCode; -}; - -//! used to return decoding results -struct CRYPTOPP_DLL DecodingResult -{ - explicit DecodingResult() : isValidCoding(false), messageLength(0) {} - explicit DecodingResult(size_t len) : isValidCoding(true), messageLength(len) {} - - bool operator==(const DecodingResult &rhs) const {return isValidCoding == rhs.isValidCoding && messageLength == rhs.messageLength;} - bool operator!=(const DecodingResult &rhs) const {return !operator==(rhs);} - - bool isValidCoding; - size_t messageLength; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - operator size_t() const {return isValidCoding ? messageLength : 0;} -#endif -}; - -//! interface for retrieving values given their names -/*! \note This class is used to safely pass a variable number of arbitrarily typed arguments to functions - and to read values from keys and crypto parameters. - \note To obtain an object that implements NameValuePairs for the purpose of parameter - passing, use the MakeParameters() function. - \note To get a value from NameValuePairs, you need to know the name and the type of the value. - Call GetValueNames() on a NameValuePairs object to obtain a list of value names that it supports. - Then look at the Name namespace documentation to see what the type of each value is, or - alternatively, call GetIntValue() with the value name, and if the type is not int, a - ValueTypeMismatch exception will be thrown and you can get the actual type from the exception object. -*/ -class CRYPTOPP_NO_VTABLE NameValuePairs -{ -public: - virtual ~NameValuePairs() {} - - //! exception thrown when trying to retrieve a value using a different type than expected - class CRYPTOPP_DLL ValueTypeMismatch : public InvalidArgument - { - public: - ValueTypeMismatch(const std::string &name, const std::type_info &stored, const std::type_info &retrieving) - : InvalidArgument("NameValuePairs: type mismatch for '" + name + "', stored '" + stored.name() + "', trying to retrieve '" + retrieving.name() + "'") - , m_stored(stored), m_retrieving(retrieving) {} - - const std::type_info & GetStoredTypeInfo() const {return m_stored;} - const std::type_info & GetRetrievingTypeInfo() const {return m_retrieving;} - - private: - const std::type_info &m_stored; - const std::type_info &m_retrieving; - }; - - //! get a copy of this object or a subobject of it - template - bool GetThisObject(T &object) const - { - return GetValue((std::string("ThisObject:")+typeid(T).name()).c_str(), object); - } - - //! get a pointer to this object, as a pointer to T - template - bool GetThisPointer(T *&p) const - { - return GetValue((std::string("ThisPointer:")+typeid(T).name()).c_str(), p); - } - - //! get a named value, returns true if the name exists - template - bool GetValue(const char *name, T &value) const - { - return GetVoidValue(name, typeid(T), &value); - } - - //! get a named value, returns the default if the name doesn't exist - template - T GetValueWithDefault(const char *name, T defaultValue) const - { - GetValue(name, defaultValue); - return defaultValue; - } - - //! get a list of value names that can be retrieved - CRYPTOPP_DLL std::string GetValueNames() const - {std::string result; GetValue("ValueNames", result); return result;} - - //! get a named value with type int - /*! used to ensure we don't accidentally try to get an unsigned int - or some other type when we mean int (which is the most common case) */ - CRYPTOPP_DLL bool GetIntValue(const char *name, int &value) const - {return GetValue(name, value);} - - //! get a named value with type int, with default - CRYPTOPP_DLL int GetIntValueWithDefault(const char *name, int defaultValue) const - {return GetValueWithDefault(name, defaultValue);} - - //! used by derived classes to check for type mismatch - CRYPTOPP_DLL static void CRYPTOPP_API ThrowIfTypeMismatch(const char *name, const std::type_info &stored, const std::type_info &retrieving) - {if (stored != retrieving) throw ValueTypeMismatch(name, stored, retrieving);} - - template - void GetRequiredParameter(const char *className, const char *name, T &value) const - { - if (!GetValue(name, value)) - throw InvalidArgument(std::string(className) + ": missing required parameter '" + name + "'"); - } - - CRYPTOPP_DLL void GetRequiredIntParameter(const char *className, const char *name, int &value) const - { - if (!GetIntValue(name, value)) - throw InvalidArgument(std::string(className) + ": missing required parameter '" + name + "'"); - } - - //! to be implemented by derived classes, users should use one of the above functions instead - CRYPTOPP_DLL virtual bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const =0; -}; - -//! namespace containing value name definitions -/*! value names, types and semantics: - - ThisObject:ClassName (ClassName, copy of this object or a subobject) - ThisPointer:ClassName (const ClassName *, pointer to this object or a subobject) -*/ -DOCUMENTED_NAMESPACE_BEGIN(Name) -// more names defined in argnames.h -DOCUMENTED_NAMESPACE_END - -//! empty set of name-value pairs -class CRYPTOPP_DLL NullNameValuePairs : public NameValuePairs -{ -public: - bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const {return false;} -}; - -//! _ -extern CRYPTOPP_DLL const NullNameValuePairs g_nullNameValuePairs; - -// ******************************************************** - -//! interface for cloning objects, this is not implemented by most classes yet -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Clonable -{ -public: - virtual ~Clonable() {} - //! this is not implemented by most classes yet - virtual Clonable* Clone() const {throw NotImplemented("Clone() is not implemented yet.");} // TODO: make this =0 -}; - -//! interface for all crypto algorithms - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Algorithm : public Clonable -{ -public: - /*! When FIPS 140-2 compliance is enabled and checkSelfTestStatus == true, - this constructor throws SelfTestFailure if the self test hasn't been run or fails. */ - Algorithm(bool checkSelfTestStatus = true); - //! returns name of this algorithm, not universally implemented yet - virtual std::string AlgorithmName() const {return "unknown";} -}; - -//! keying interface for crypto algorithms that take byte strings as keys -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE SimpleKeyingInterface -{ -public: - virtual ~SimpleKeyingInterface() {} - - //! returns smallest valid key length in bytes */ - virtual size_t MinKeyLength() const =0; - //! returns largest valid key length in bytes */ - virtual size_t MaxKeyLength() const =0; - //! returns default (recommended) key length in bytes */ - virtual size_t DefaultKeyLength() const =0; - - //! returns the smallest valid key length in bytes that is >= min(n, GetMaxKeyLength()) - virtual size_t GetValidKeyLength(size_t n) const =0; - - //! returns whether n is a valid key length - virtual bool IsValidKeyLength(size_t n) const - {return n == GetValidKeyLength(n);} - - //! set or reset the key of this object - /*! \param params is used to specify Rounds, BlockSize, etc. */ - virtual void SetKey(const byte *key, size_t length, const NameValuePairs ¶ms = g_nullNameValuePairs); - - //! calls SetKey() with an NameValuePairs object that just specifies "Rounds" - void SetKeyWithRounds(const byte *key, size_t length, int rounds); - - //! calls SetKey() with an NameValuePairs object that just specifies "IV" - void SetKeyWithIV(const byte *key, size_t length, const byte *iv, size_t ivLength); - - //! calls SetKey() with an NameValuePairs object that just specifies "IV" - void SetKeyWithIV(const byte *key, size_t length, const byte *iv) - {SetKeyWithIV(key, length, iv, IVSize());} - - enum IV_Requirement {UNIQUE_IV = 0, RANDOM_IV, UNPREDICTABLE_RANDOM_IV, INTERNALLY_GENERATED_IV, NOT_RESYNCHRONIZABLE}; - //! returns the minimal requirement for secure IVs - virtual IV_Requirement IVRequirement() const =0; - - //! returns whether this object can be resynchronized (i.e. supports initialization vectors) - /*! If this function returns true, and no IV is passed to SetKey() and CanUseStructuredIVs()==true, an IV of all 0's will be assumed. */ - bool IsResynchronizable() const {return IVRequirement() < NOT_RESYNCHRONIZABLE;} - //! returns whether this object can use random IVs (in addition to ones returned by GetNextIV) - bool CanUseRandomIVs() const {return IVRequirement() <= UNPREDICTABLE_RANDOM_IV;} - //! returns whether this object can use random but possibly predictable IVs (in addition to ones returned by GetNextIV) - bool CanUsePredictableIVs() const {return IVRequirement() <= RANDOM_IV;} - //! returns whether this object can use structured IVs, for example a counter (in addition to ones returned by GetNextIV) - bool CanUseStructuredIVs() const {return IVRequirement() <= UNIQUE_IV;} - - virtual unsigned int IVSize() const {throw NotImplemented(GetAlgorithm().AlgorithmName() + ": this object doesn't support resynchronization");} - //! returns default length of IVs accepted by this object - unsigned int DefaultIVLength() const {return IVSize();} - //! returns minimal length of IVs accepted by this object - virtual unsigned int MinIVLength() const {return IVSize();} - //! returns maximal length of IVs accepted by this object - virtual unsigned int MaxIVLength() const {return IVSize();} - //! resynchronize with an IV. ivLength=-1 means use IVSize() - virtual void Resynchronize(const byte *iv, int ivLength=-1) {throw NotImplemented(GetAlgorithm().AlgorithmName() + ": this object doesn't support resynchronization");} - //! get a secure IV for the next message - /*! This method should be called after you finish encrypting one message and are ready to start the next one. - After calling it, you must call SetKey() or Resynchronize() before using this object again. - This method is not implemented on decryption objects. */ - virtual void GetNextIV(RandomNumberGenerator &rng, byte *IV); - -protected: - virtual const Algorithm & GetAlgorithm() const =0; - virtual void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) =0; - - void ThrowIfInvalidKeyLength(size_t length); - void ThrowIfResynchronizable(); // to be called when no IV is passed - void ThrowIfInvalidIV(const byte *iv); // check for NULL IV if it can't be used - size_t ThrowIfInvalidIVLength(int size); - const byte * GetIVAndThrowIfInvalid(const NameValuePairs ¶ms, size_t &size); - inline void AssertValidKeyLength(size_t length) const - {assert(IsValidKeyLength(length));} -}; - -//! interface for the data processing part of block ciphers - -/*! Classes derived from BlockTransformation are block ciphers - in ECB mode (for example the DES::Encryption class), which are stateless. - These classes should not be used directly, but only in combination with - a mode class (see CipherModeDocumentation in modes.h). -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE BlockTransformation : public Algorithm -{ -public: - //! encrypt or decrypt inBlock, xor with xorBlock, and write to outBlock - virtual void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const =0; - - //! encrypt or decrypt one block - /*! \pre size of inBlock and outBlock == BlockSize() */ - void ProcessBlock(const byte *inBlock, byte *outBlock) const - {ProcessAndXorBlock(inBlock, NULL, outBlock);} - - //! encrypt or decrypt one block in place - void ProcessBlock(byte *inoutBlock) const - {ProcessAndXorBlock(inoutBlock, NULL, inoutBlock);} - - //! block size of the cipher in bytes - virtual unsigned int BlockSize() const =0; - - //! returns how inputs and outputs should be aligned for optimal performance - virtual unsigned int OptimalDataAlignment() const; - - //! returns true if this is a permutation (i.e. there is an inverse transformation) - virtual bool IsPermutation() const {return true;} - - //! returns true if this is an encryption object - virtual bool IsForwardTransformation() const =0; - - //! return number of blocks that can be processed in parallel, for bit-slicing implementations - virtual unsigned int OptimalNumberOfParallelBlocks() const {return 1;} - - enum {BT_InBlockIsCounter=1, BT_DontIncrementInOutPointers=2, BT_XorInput=4, BT_ReverseDirection=8} FlagsForAdvancedProcessBlocks; - - //! encrypt and xor blocks according to flags (see FlagsForAdvancedProcessBlocks) - /*! /note If BT_InBlockIsCounter is set, last byte of inBlocks may be modified. */ - virtual size_t AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags) const; - - inline CipherDir GetCipherDirection() const {return IsForwardTransformation() ? ENCRYPTION : DECRYPTION;} -}; - -//! interface for the data processing part of stream ciphers - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE StreamTransformation : public Algorithm -{ -public: - //! return a reference to this object, - /*! This function is useful for passing a temporary StreamTransformation object to a - function that takes a non-const reference. */ - StreamTransformation& Ref() {return *this;} - - //! returns block size, if input must be processed in blocks, otherwise 1 - virtual unsigned int MandatoryBlockSize() const {return 1;} - - //! returns the input block size that is most efficient for this cipher - /*! \note optimal input length is n * OptimalBlockSize() - GetOptimalBlockSizeUsed() for any n > 0 */ - virtual unsigned int OptimalBlockSize() const {return MandatoryBlockSize();} - //! returns how much of the current block is used up - virtual unsigned int GetOptimalBlockSizeUsed() const {return 0;} - - //! returns how input should be aligned for optimal performance - virtual unsigned int OptimalDataAlignment() const; - - //! encrypt or decrypt an array of bytes of specified length - /*! \note either inString == outString, or they don't overlap */ - virtual void ProcessData(byte *outString, const byte *inString, size_t length) =0; - - //! for ciphers where the last block of data is special, encrypt or decrypt the last block of data - /*! For now the only use of this function is for CBC-CTS mode. */ - virtual void ProcessLastBlock(byte *outString, const byte *inString, size_t length); - //! returns the minimum size of the last block, 0 indicating the last block is not special - virtual unsigned int MinLastBlockSize() const {return 0;} - - //! same as ProcessData(inoutString, inoutString, length) - inline void ProcessString(byte *inoutString, size_t length) - {ProcessData(inoutString, inoutString, length);} - //! same as ProcessData(outString, inString, length) - inline void ProcessString(byte *outString, const byte *inString, size_t length) - {ProcessData(outString, inString, length);} - //! implemented as {ProcessData(&input, &input, 1); return input;} - inline byte ProcessByte(byte input) - {ProcessData(&input, &input, 1); return input;} - - //! returns whether this cipher supports random access - virtual bool IsRandomAccess() const =0; - //! for random access ciphers, seek to an absolute position - virtual void Seek(lword n) - { - assert(!IsRandomAccess()); - throw NotImplemented("StreamTransformation: this object doesn't support random access"); - } - - //! returns whether this transformation is self-inverting (e.g. xor with a keystream) - virtual bool IsSelfInverting() const =0; - //! returns whether this is an encryption object - virtual bool IsForwardTransformation() const =0; -}; - -//! interface for hash functions and data processing part of MACs - -/*! HashTransformation objects are stateful. They are created in an initial state, - change state as Update() is called, and return to the initial - state when Final() is called. This interface allows a large message to - be hashed in pieces by calling Update() on each piece followed by - calling Final(). -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE HashTransformation : public Algorithm -{ -public: - //! return a reference to this object, - /*! This function is useful for passing a temporary HashTransformation object to a - function that takes a non-const reference. */ - HashTransformation& Ref() {return *this;} - - //! process more input - virtual void Update(const byte *input, size_t length) =0; - - //! request space to write input into - virtual byte * CreateUpdateSpace(size_t &size) {size=0; return NULL;} - - //! compute hash for current message, then restart for a new message - /*! \pre size of digest == DigestSize(). */ - virtual void Final(byte *digest) - {TruncatedFinal(digest, DigestSize());} - - //! discard the current state, and restart with a new message - virtual void Restart() - {TruncatedFinal(NULL, 0);} - - //! size of the hash/digest/MAC returned by Final() - virtual unsigned int DigestSize() const =0; - - //! same as DigestSize() - unsigned int TagSize() const {return DigestSize();} - - - //! block size of underlying compression function, or 0 if not block based - virtual unsigned int BlockSize() const {return 0;} - - //! input to Update() should have length a multiple of this for optimal speed - virtual unsigned int OptimalBlockSize() const {return 1;} - - //! returns how input should be aligned for optimal performance - virtual unsigned int OptimalDataAlignment() const; - - //! use this if your input is in one piece and you don't want to call Update() and Final() separately - virtual void CalculateDigest(byte *digest, const byte *input, size_t length) - {Update(input, length); Final(digest);} - - //! verify that digest is a valid digest for the current message, then reinitialize the object - /*! Default implementation is to call Final() and do a bitwise comparison - between its output and digest. */ - virtual bool Verify(const byte *digest) - {return TruncatedVerify(digest, DigestSize());} - - //! use this if your input is in one piece and you don't want to call Update() and Verify() separately - virtual bool VerifyDigest(const byte *digest, const byte *input, size_t length) - {Update(input, length); return Verify(digest);} - - //! truncated version of Final() - virtual void TruncatedFinal(byte *digest, size_t digestSize) =0; - - //! truncated version of CalculateDigest() - virtual void CalculateTruncatedDigest(byte *digest, size_t digestSize, const byte *input, size_t length) - {Update(input, length); TruncatedFinal(digest, digestSize);} - - //! truncated version of Verify() - virtual bool TruncatedVerify(const byte *digest, size_t digestLength); - - //! truncated version of VerifyDigest() - virtual bool VerifyTruncatedDigest(const byte *digest, size_t digestLength, const byte *input, size_t length) - {Update(input, length); return TruncatedVerify(digest, digestLength);} - -protected: - void ThrowIfInvalidTruncatedSize(size_t size) const; -}; - -typedef HashTransformation HashFunction; - -//! interface for one direction (encryption or decryption) of a block cipher -/*! \note These objects usually should not be used directly. See BlockTransformation for more details. */ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE BlockCipher : public SimpleKeyingInterface, public BlockTransformation -{ -protected: - const Algorithm & GetAlgorithm() const {return *this;} -}; - -//! interface for one direction (encryption or decryption) of a stream cipher or cipher mode -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE SymmetricCipher : public SimpleKeyingInterface, public StreamTransformation -{ -protected: - const Algorithm & GetAlgorithm() const {return *this;} -}; - -//! interface for message authentication codes -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE MessageAuthenticationCode : public SimpleKeyingInterface, public HashTransformation -{ -protected: - const Algorithm & GetAlgorithm() const {return *this;} -}; - -//! interface for for one direction (encryption or decryption) of a stream cipher or block cipher mode with authentication -/*! The StreamTransformation part of this interface is used to encrypt/decrypt the data, and the MessageAuthenticationCode part of this - interface is used to input additional authenticated data (AAD, which is MAC'ed but not encrypted), and to generate/verify the MAC. */ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AuthenticatedSymmetricCipher : public MessageAuthenticationCode, public StreamTransformation -{ -public: - //! this indicates that a member function was called in the wrong state, for example trying to encrypt a message before having set the key or IV - class BadState : public Exception - { - public: - explicit BadState(const std::string &name, const char *message) : Exception(OTHER_ERROR, name + ": " + message) {} - explicit BadState(const std::string &name, const char *function, const char *state) : Exception(OTHER_ERROR, name + ": " + function + " was called before " + state) {} - }; - - //! the maximum length of AAD that can be input before the encrypted data - virtual lword MaxHeaderLength() const =0; - //! the maximum length of encrypted data - virtual lword MaxMessageLength() const =0; - //! the maximum length of AAD that can be input after the encrypted data - virtual lword MaxFooterLength() const {return 0;} - //! if this function returns true, SpecifyDataLengths() must be called before attempting to input data - /*! This is the case for some schemes, such as CCM. */ - virtual bool NeedsPrespecifiedDataLengths() const {return false;} - //! this function only needs to be called if NeedsPrespecifiedDataLengths() returns true - void SpecifyDataLengths(lword headerLength, lword messageLength, lword footerLength=0); - //! encrypt and generate MAC in one call. will truncate MAC if macSize < TagSize() - virtual void EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *header, size_t headerLength, const byte *message, size_t messageLength); - //! decrypt and verify MAC in one call, returning true iff MAC is valid. will assume MAC is truncated if macLength < TagSize() - virtual bool DecryptAndVerify(byte *message, const byte *mac, size_t macLength, const byte *iv, int ivLength, const byte *header, size_t headerLength, const byte *ciphertext, size_t ciphertextLength); - - // redeclare this to avoid compiler ambiguity errors - virtual std::string AlgorithmName() const =0; - -protected: - const Algorithm & GetAlgorithm() const {return *static_cast(this);} - virtual void UncheckedSpecifyDataLengths(lword headerLength, lword messageLength, lword footerLength) {} -}; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY -typedef SymmetricCipher StreamCipher; -#endif - -//! interface for random number generators -/*! All return values are uniformly distributed over the range specified. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE RandomNumberGenerator : public Algorithm -{ -public: - //! update RNG state with additional unpredictable values - virtual void IncorporateEntropy(const byte *input, size_t length) {throw NotImplemented("RandomNumberGenerator: IncorporateEntropy not implemented");} - - //! returns true if IncorporateEntropy is implemented - virtual bool CanIncorporateEntropy() const {return false;} - - //! generate new random byte and return it - virtual byte GenerateByte(); - - //! generate new random bit and return it - /*! Default implementation is to call GenerateByte() and return its lowest bit. */ - virtual unsigned int GenerateBit(); - - //! generate a random 32 bit word in the range min to max, inclusive - virtual word32 GenerateWord32(word32 a=0, word32 b=0xffffffffL); - - //! generate random array of bytes - virtual void GenerateBlock(byte *output, size_t size); - - //! generate and discard n bytes - virtual void DiscardBytes(size_t n); - - //! generate random bytes as input to a BufferedTransformation - virtual void GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword length); - - //! randomly shuffle the specified array, resulting permutation is uniformly distributed - template void Shuffle(IT begin, IT end) - { - for (; begin != end; ++begin) - std::iter_swap(begin, begin + GenerateWord32(0, end-begin-1)); - } - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - byte GetByte() {return GenerateByte();} - unsigned int GetBit() {return GenerateBit();} - word32 GetLong(word32 a=0, word32 b=0xffffffffL) {return GenerateWord32(a, b);} - word16 GetShort(word16 a=0, word16 b=0xffff) {return (word16)GenerateWord32(a, b);} - void GetBlock(byte *output, size_t size) {GenerateBlock(output, size);} -#endif -}; - -//! returns a reference that can be passed to functions that ask for a RNG but doesn't actually use it -CRYPTOPP_DLL RandomNumberGenerator & CRYPTOPP_API NullRNG(); - -class WaitObjectContainer; -class CallStack; - -//! interface for objects that you can wait for - -class CRYPTOPP_NO_VTABLE Waitable -{ -public: - virtual ~Waitable() {} - - //! maximum number of wait objects that this object can return - virtual unsigned int GetMaxWaitObjectCount() const =0; - //! put wait objects into container - /*! \param callStack is used for tracing no wait loops, example: - something.GetWaitObjects(c, CallStack("my func after X", 0)); - - or in an outer GetWaitObjects() method that itself takes a callStack parameter: - innerThing.GetWaitObjects(c, CallStack("MyClass::GetWaitObjects at X", &callStack)); */ - virtual void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) =0; - //! wait on this object - /*! same as creating an empty container, calling GetWaitObjects(), and calling Wait() on the container */ - bool Wait(unsigned long milliseconds, CallStack const& callStack); -}; - -//! the default channel for BufferedTransformation, equal to the empty string -extern CRYPTOPP_DLL const std::string DEFAULT_CHANNEL; - -//! channel for additional authenticated data, equal to "AAD" -extern CRYPTOPP_DLL const std::string AAD_CHANNEL; - -//! interface for buffered transformations - -/*! BufferedTransformation is a generalization of BlockTransformation, - StreamTransformation, and HashTransformation. - - A buffered transformation is an object that takes a stream of bytes - as input (this may be done in stages), does some computation on them, and - then places the result into an internal buffer for later retrieval. Any - partial result already in the output buffer is not modified by further - input. - - If a method takes a "blocking" parameter, and you - pass "false" for it, the method will return before all input has been processed if - the input cannot be processed without waiting (for network buffers to become available, for example). - In this case the method will return true - or a non-zero integer value. When this happens you must continue to call the method with the same - parameters until it returns false or zero, before calling any other method on it or - attached BufferedTransformation. The integer return value in this case is approximately - the number of bytes left to be processed, and can be used to implement a progress bar. - - For functions that take a "propagation" parameter, propagation != 0 means pass on the signal to attached - BufferedTransformation objects, with propagation decremented at each step until it reaches 0. - -1 means unlimited propagation. - - \nosubgrouping -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE BufferedTransformation : public Algorithm, public Waitable -{ -public: - // placed up here for CW8 - static const std::string &NULL_CHANNEL; // same as DEFAULT_CHANNEL, for backwards compatibility - - BufferedTransformation() : Algorithm(false) {} - - //! return a reference to this object - /*! This function is useful for passing a temporary BufferedTransformation object to a - function that takes a non-const reference. */ - BufferedTransformation& Ref() {return *this;} - - //! \name INPUT - //@{ - //! input a byte for processing - size_t Put(byte inByte, bool blocking=true) - {return Put(&inByte, 1, blocking);} - //! input multiple bytes - size_t Put(const byte *inString, size_t length, bool blocking=true) - {return Put2(inString, length, 0, blocking);} - - //! input a 16-bit word - size_t PutWord16(word16 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); - //! input a 32-bit word - size_t PutWord32(word32 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); - - //! request space which can be written into by the caller, and then used as input to Put() - /*! \param size is requested size (as a hint) for input, and size of the returned space for output */ - /*! \note The purpose of this method is to help avoid doing extra memory allocations. */ - virtual byte * CreatePutSpace(size_t &size) {size=0; return NULL;} - - virtual bool CanModifyInput() const {return false;} - - //! input multiple bytes that may be modified by callee - size_t PutModifiable(byte *inString, size_t length, bool blocking=true) - {return PutModifiable2(inString, length, 0, blocking);} - - bool MessageEnd(int propagation=-1, bool blocking=true) - {return !!Put2(NULL, 0, propagation < 0 ? -1 : propagation+1, blocking);} - size_t PutMessageEnd(const byte *inString, size_t length, int propagation=-1, bool blocking=true) - {return Put2(inString, length, propagation < 0 ? -1 : propagation+1, blocking);} - - //! input multiple bytes for blocking or non-blocking processing - /*! \param messageEnd means how many filters to signal MessageEnd to, including this one */ - virtual size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking) =0; - //! input multiple bytes that may be modified by callee for blocking or non-blocking processing - /*! \param messageEnd means how many filters to signal MessageEnd to, including this one */ - virtual size_t PutModifiable2(byte *inString, size_t length, int messageEnd, bool blocking) - {return Put2(inString, length, messageEnd, blocking);} - - //! thrown by objects that have not implemented nonblocking input processing - struct BlockingInputOnly : public NotImplemented - {BlockingInputOnly(const std::string &s) : NotImplemented(s + ": Nonblocking input is not implemented by this object.") {}}; - //@} - - //! \name WAITING - //@{ - unsigned int GetMaxWaitObjectCount() const; - void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); - //@} - - //! \name SIGNALS - //@{ - virtual void IsolatedInitialize(const NameValuePairs ¶meters) {throw NotImplemented("BufferedTransformation: this object can't be reinitialized");} - virtual bool IsolatedFlush(bool hardFlush, bool blocking) =0; - virtual bool IsolatedMessageSeriesEnd(bool blocking) {return false;} - - //! initialize or reinitialize this object - virtual void Initialize(const NameValuePairs ¶meters=g_nullNameValuePairs, int propagation=-1); - //! flush buffered input and/or output - /*! \param hardFlush is used to indicate whether all data should be flushed - \note Hard flushes must be used with care. It means try to process and output everything, even if - there may not be enough data to complete the action. For example, hard flushing a HexDecoder would - cause an error if you do it after inputing an odd number of hex encoded characters. - For some types of filters, for example ZlibDecompressor, hard flushes can only - be done at "synchronization points". These synchronization points are positions in the data - stream that are created by hard flushes on the corresponding reverse filters, in this - example ZlibCompressor. This is useful when zlib compressed data is moved across a - network in packets and compression state is preserved across packets, as in the ssh2 protocol. - */ - virtual bool Flush(bool hardFlush, int propagation=-1, bool blocking=true); - //! mark end of a series of messages - /*! There should be a MessageEnd immediately before MessageSeriesEnd. */ - virtual bool MessageSeriesEnd(int propagation=-1, bool blocking=true); - - //! set propagation of automatically generated and transferred signals - /*! propagation == 0 means do not automaticly generate signals */ - virtual void SetAutoSignalPropagation(int propagation) {} - - //! - virtual int GetAutoSignalPropagation() const {return 0;} -public: - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - void Close() {MessageEnd();} -#endif - //@} - - //! \name RETRIEVAL OF ONE MESSAGE - //@{ - //! returns number of bytes that is currently ready for retrieval - /*! All retrieval functions return the actual number of bytes - retrieved, which is the lesser of the request number and - MaxRetrievable(). */ - virtual lword MaxRetrievable() const; - - //! returns whether any bytes are currently ready for retrieval - virtual bool AnyRetrievable() const; - - //! try to retrieve a single byte - virtual size_t Get(byte &outByte); - //! try to retrieve multiple bytes - virtual size_t Get(byte *outString, size_t getMax); - - //! peek at the next byte without removing it from the output buffer - virtual size_t Peek(byte &outByte) const; - //! peek at multiple bytes without removing them from the output buffer - virtual size_t Peek(byte *outString, size_t peekMax) const; - - //! try to retrieve a 16-bit word - size_t GetWord16(word16 &value, ByteOrder order=BIG_ENDIAN_ORDER); - //! try to retrieve a 32-bit word - size_t GetWord32(word32 &value, ByteOrder order=BIG_ENDIAN_ORDER); - - //! try to peek at a 16-bit word - size_t PeekWord16(word16 &value, ByteOrder order=BIG_ENDIAN_ORDER) const; - //! try to peek at a 32-bit word - size_t PeekWord32(word32 &value, ByteOrder order=BIG_ENDIAN_ORDER) const; - - //! move transferMax bytes of the buffered output to target as input - lword TransferTo(BufferedTransformation &target, lword transferMax=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL) - {TransferTo2(target, transferMax, channel); return transferMax;} - - //! discard skipMax bytes from the output buffer - virtual lword Skip(lword skipMax=LWORD_MAX); - - //! copy copyMax bytes of the buffered output to target as input - lword CopyTo(BufferedTransformation &target, lword copyMax=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL) const - {return CopyRangeTo(target, 0, copyMax, channel);} - - //! copy copyMax bytes of the buffered output, starting at position (relative to current position), to target as input - lword CopyRangeTo(BufferedTransformation &target, lword position, lword copyMax=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL) const - {lword i = position; CopyRangeTo2(target, i, i+copyMax, channel); return i-position;} - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - unsigned long MaxRetrieveable() const {return MaxRetrievable();} -#endif - //@} - - //! \name RETRIEVAL OF MULTIPLE MESSAGES - //@{ - //! - virtual lword TotalBytesRetrievable() const; - //! number of times MessageEnd() has been received minus messages retrieved or skipped - virtual unsigned int NumberOfMessages() const; - //! returns true if NumberOfMessages() > 0 - virtual bool AnyMessages() const; - //! start retrieving the next message - /*! - Returns false if no more messages exist or this message - is not completely retrieved. - */ - virtual bool GetNextMessage(); - //! skip count number of messages - virtual unsigned int SkipMessages(unsigned int count=UINT_MAX); - //! - unsigned int TransferMessagesTo(BufferedTransformation &target, unsigned int count=UINT_MAX, const std::string &channel=DEFAULT_CHANNEL) - {TransferMessagesTo2(target, count, channel); return count;} - //! - unsigned int CopyMessagesTo(BufferedTransformation &target, unsigned int count=UINT_MAX, const std::string &channel=DEFAULT_CHANNEL) const; - - //! - virtual void SkipAll(); - //! - void TransferAllTo(BufferedTransformation &target, const std::string &channel=DEFAULT_CHANNEL) - {TransferAllTo2(target, channel);} - //! - void CopyAllTo(BufferedTransformation &target, const std::string &channel=DEFAULT_CHANNEL) const; - - virtual bool GetNextMessageSeries() {return false;} - virtual unsigned int NumberOfMessagesInThisSeries() const {return NumberOfMessages();} - virtual unsigned int NumberOfMessageSeries() const {return 0;} - //@} - - //! \name NON-BLOCKING TRANSFER OF OUTPUT - //@{ - //! upon return, byteCount contains number of bytes that have finished being transfered, and returns the number of bytes left in the current transfer block - virtual size_t TransferTo2(BufferedTransformation &target, lword &byteCount, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) =0; - //! upon return, begin contains the start position of data yet to be finished copying, and returns the number of bytes left in the current transfer block - virtual size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const =0; - //! upon return, messageCount contains number of messages that have finished being transfered, and returns the number of bytes left in the current transfer block - size_t TransferMessagesTo2(BufferedTransformation &target, unsigned int &messageCount, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); - //! returns the number of bytes left in the current transfer block - size_t TransferAllTo2(BufferedTransformation &target, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); - //@} - - //! \name CHANNELS - //@{ - struct NoChannelSupport : public NotImplemented - {NoChannelSupport(const std::string &name) : NotImplemented(name + ": this object doesn't support multiple channels") {}}; - struct InvalidChannelName : public InvalidArgument - {InvalidChannelName(const std::string &name, const std::string &channel) : InvalidArgument(name + ": unexpected channel name \"" + channel + "\"") {}}; - - size_t ChannelPut(const std::string &channel, byte inByte, bool blocking=true) - {return ChannelPut(channel, &inByte, 1, blocking);} - size_t ChannelPut(const std::string &channel, const byte *inString, size_t length, bool blocking=true) - {return ChannelPut2(channel, inString, length, 0, blocking);} - - size_t ChannelPutModifiable(const std::string &channel, byte *inString, size_t length, bool blocking=true) - {return ChannelPutModifiable2(channel, inString, length, 0, blocking);} - - size_t ChannelPutWord16(const std::string &channel, word16 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); - size_t ChannelPutWord32(const std::string &channel, word32 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); - - bool ChannelMessageEnd(const std::string &channel, int propagation=-1, bool blocking=true) - {return !!ChannelPut2(channel, NULL, 0, propagation < 0 ? -1 : propagation+1, blocking);} - size_t ChannelPutMessageEnd(const std::string &channel, const byte *inString, size_t length, int propagation=-1, bool blocking=true) - {return ChannelPut2(channel, inString, length, propagation < 0 ? -1 : propagation+1, blocking);} - - virtual byte * ChannelCreatePutSpace(const std::string &channel, size_t &size); - - virtual size_t ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking); - virtual size_t ChannelPutModifiable2(const std::string &channel, byte *begin, size_t length, int messageEnd, bool blocking); - - virtual bool ChannelFlush(const std::string &channel, bool hardFlush, int propagation=-1, bool blocking=true); - virtual bool ChannelMessageSeriesEnd(const std::string &channel, int propagation=-1, bool blocking=true); - - virtual void SetRetrievalChannel(const std::string &channel); - //@} - - //! \name ATTACHMENT - /*! Some BufferedTransformation objects (e.g. Filter objects) - allow other BufferedTransformation objects to be attached. When - this is done, the first object instead of buffering its output, - sents that output to the attached object as input. The entire - attachment chain is deleted when the anchor object is destructed. - */ - //@{ - //! returns whether this object allows attachment - virtual bool Attachable() {return false;} - //! returns the object immediately attached to this object or NULL for no attachment - virtual BufferedTransformation *AttachedTransformation() {assert(!Attachable()); return 0;} - //! - virtual const BufferedTransformation *AttachedTransformation() const - {return const_cast(this)->AttachedTransformation();} - //! delete the current attachment chain and replace it with newAttachment - virtual void Detach(BufferedTransformation *newAttachment = 0) - {assert(!Attachable()); throw NotImplemented("BufferedTransformation: this object is not attachable");} - //! add newAttachment to the end of attachment chain - virtual void Attach(BufferedTransformation *newAttachment); - //@} - -protected: - static int DecrementPropagation(int propagation) - {return propagation != 0 ? propagation - 1 : 0;} - -private: - byte m_buf[4]; // for ChannelPutWord16 and ChannelPutWord32, to ensure buffer isn't deallocated before non-blocking operation completes -}; - -//! returns a reference to a BufferedTransformation object that discards all input -BufferedTransformation & TheBitBucket(); - -//! interface for crypto material, such as public and private keys, and crypto parameters - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CryptoMaterial : public NameValuePairs -{ -public: - //! exception thrown when invalid crypto material is detected - class CRYPTOPP_DLL InvalidMaterial : public InvalidDataFormat - { - public: - explicit InvalidMaterial(const std::string &s) : InvalidDataFormat(s) {} - }; - - //! assign values from source to this object - /*! \note This function can be used to create a public key from a private key. */ - virtual void AssignFrom(const NameValuePairs &source) =0; - - //! check this object for errors - /*! \param level denotes the level of thoroughness: - 0 - using this object won't cause a crash or exception (rng is ignored) - 1 - this object will probably function (encrypt, sign, etc.) correctly (but may not check for weak keys and such) - 2 - make sure this object will function correctly, and do reasonable security checks - 3 - do checks that may take a long time - \return true if the tests pass */ - virtual bool Validate(RandomNumberGenerator &rng, unsigned int level) const =0; - - //! throws InvalidMaterial if this object fails Validate() test - virtual void ThrowIfInvalid(RandomNumberGenerator &rng, unsigned int level) const - {if (!Validate(rng, level)) throw InvalidMaterial("CryptoMaterial: this object contains invalid values");} - -// virtual std::vector GetSupportedFormats(bool includeSaveOnly=false, bool includeLoadOnly=false); - - //! save key into a BufferedTransformation - virtual void Save(BufferedTransformation &bt) const - {throw NotImplemented("CryptoMaterial: this object does not support saving");} - - //! load key from a BufferedTransformation - /*! \throws KeyingErr if decode fails - \note Generally does not check that the key is valid. - Call ValidateKey() or ThrowIfInvalidKey() to check that. */ - virtual void Load(BufferedTransformation &bt) - {throw NotImplemented("CryptoMaterial: this object does not support loading");} - - //! \return whether this object supports precomputation - virtual bool SupportsPrecomputation() const {return false;} - //! do precomputation - /*! The exact semantics of Precompute() is varies, but - typically it means calculate a table of n objects - that can be used later to speed up computation. */ - virtual void Precompute(unsigned int n) - {assert(!SupportsPrecomputation()); throw NotImplemented("CryptoMaterial: this object does not support precomputation");} - //! retrieve previously saved precomputation - virtual void LoadPrecomputation(BufferedTransformation &storedPrecomputation) - {assert(!SupportsPrecomputation()); throw NotImplemented("CryptoMaterial: this object does not support precomputation");} - //! save precomputation for later use - virtual void SavePrecomputation(BufferedTransformation &storedPrecomputation) const - {assert(!SupportsPrecomputation()); throw NotImplemented("CryptoMaterial: this object does not support precomputation");} - - // for internal library use - void DoQuickSanityCheck() const {ThrowIfInvalid(NullRNG(), 0);} - -#if (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) - // Sun Studio 11/CC 5.8 workaround: it generates incorrect code when casting to an empty virtual base class - char m_sunCCworkaround; -#endif -}; - -//! interface for generatable crypto material, such as private keys and crypto parameters - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE GeneratableCryptoMaterial : virtual public CryptoMaterial -{ -public: - //! generate a random key or crypto parameters - /*! \throws KeyingErr if algorithm parameters are invalid, or if a key can't be generated - (e.g., if this is a public key object) */ - virtual void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs ¶ms = g_nullNameValuePairs) - {throw NotImplemented("GeneratableCryptoMaterial: this object does not support key/parameter generation");} - - //! calls the above function with a NameValuePairs object that just specifies "KeySize" - void GenerateRandomWithKeySize(RandomNumberGenerator &rng, unsigned int keySize); -}; - -//! interface for public keys - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PublicKey : virtual public CryptoMaterial -{ -}; - -//! interface for private keys - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PrivateKey : public GeneratableCryptoMaterial -{ -}; - -//! interface for crypto prameters - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CryptoParameters : public GeneratableCryptoMaterial -{ -}; - -//! interface for asymmetric algorithms - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AsymmetricAlgorithm : public Algorithm -{ -public: - //! returns a reference to the crypto material used by this object - virtual CryptoMaterial & AccessMaterial() =0; - //! returns a const reference to the crypto material used by this object - virtual const CryptoMaterial & GetMaterial() const =0; - - //! for backwards compatibility, calls AccessMaterial().Load(bt) - void BERDecode(BufferedTransformation &bt) - {AccessMaterial().Load(bt);} - //! for backwards compatibility, calls GetMaterial().Save(bt) - void DEREncode(BufferedTransformation &bt) const - {GetMaterial().Save(bt);} -}; - -//! interface for asymmetric algorithms using public keys - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PublicKeyAlgorithm : public AsymmetricAlgorithm -{ -public: - // VC60 workaround: no co-variant return type - CryptoMaterial & AccessMaterial() {return AccessPublicKey();} - const CryptoMaterial & GetMaterial() const {return GetPublicKey();} - - virtual PublicKey & AccessPublicKey() =0; - virtual const PublicKey & GetPublicKey() const {return const_cast(this)->AccessPublicKey();} -}; - -//! interface for asymmetric algorithms using private keys - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PrivateKeyAlgorithm : public AsymmetricAlgorithm -{ -public: - CryptoMaterial & AccessMaterial() {return AccessPrivateKey();} - const CryptoMaterial & GetMaterial() const {return GetPrivateKey();} - - virtual PrivateKey & AccessPrivateKey() =0; - virtual const PrivateKey & GetPrivateKey() const {return const_cast(this)->AccessPrivateKey();} -}; - -//! interface for key agreement algorithms - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE KeyAgreementAlgorithm : public AsymmetricAlgorithm -{ -public: - CryptoMaterial & AccessMaterial() {return AccessCryptoParameters();} - const CryptoMaterial & GetMaterial() const {return GetCryptoParameters();} - - virtual CryptoParameters & AccessCryptoParameters() =0; - virtual const CryptoParameters & GetCryptoParameters() const {return const_cast(this)->AccessCryptoParameters();} -}; - -//! interface for public-key encryptors and decryptors - -/*! This class provides an interface common to encryptors and decryptors - for querying their plaintext and ciphertext lengths. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_CryptoSystem -{ -public: - virtual ~PK_CryptoSystem() {} - - //! maximum length of plaintext for a given ciphertext length - /*! \note This function returns 0 if ciphertextLength is not valid (too long or too short). */ - virtual size_t MaxPlaintextLength(size_t ciphertextLength) const =0; - - //! calculate length of ciphertext given length of plaintext - /*! \note This function returns 0 if plaintextLength is not valid (too long). */ - virtual size_t CiphertextLength(size_t plaintextLength) const =0; - - //! this object supports the use of the parameter with the given name - /*! some possible parameter names: EncodingParameters, KeyDerivationParameters */ - virtual bool ParameterSupported(const char *name) const =0; - - //! return fixed ciphertext length, if one exists, otherwise return 0 - /*! \note "Fixed" here means length of ciphertext does not depend on length of plaintext. - It usually does depend on the key length. */ - virtual size_t FixedCiphertextLength() const {return 0;} - - //! return maximum plaintext length given the fixed ciphertext length, if one exists, otherwise return 0 - virtual size_t FixedMaxPlaintextLength() const {return 0;} - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - size_t MaxPlainTextLength(size_t cipherTextLength) const {return MaxPlaintextLength(cipherTextLength);} - size_t CipherTextLength(size_t plainTextLength) const {return CiphertextLength(plainTextLength);} -#endif -}; - -//! interface for public-key encryptors -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Encryptor : public PK_CryptoSystem, public PublicKeyAlgorithm -{ -public: - //! exception thrown when trying to encrypt plaintext of invalid length - class CRYPTOPP_DLL InvalidPlaintextLength : public Exception - { - public: - InvalidPlaintextLength() : Exception(OTHER_ERROR, "PK_Encryptor: invalid plaintext length") {} - }; - - //! encrypt a byte string - /*! \pre CiphertextLength(plaintextLength) != 0 (i.e., plaintext isn't too long) - \pre size of ciphertext == CiphertextLength(plaintextLength) - */ - virtual void Encrypt(RandomNumberGenerator &rng, - const byte *plaintext, size_t plaintextLength, - byte *ciphertext, const NameValuePairs ¶meters = g_nullNameValuePairs) const =0; - - //! create a new encryption filter - /*! \note The caller is responsible for deleting the returned pointer. - \note Encoding parameters should be passed in the "EP" channel. - */ - virtual BufferedTransformation * CreateEncryptionFilter(RandomNumberGenerator &rng, - BufferedTransformation *attachment=NULL, const NameValuePairs ¶meters = g_nullNameValuePairs) const; -}; - -//! interface for public-key decryptors - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Decryptor : public PK_CryptoSystem, public PrivateKeyAlgorithm -{ -public: - //! decrypt a byte string, and return the length of plaintext - /*! \pre size of plaintext == MaxPlaintextLength(ciphertextLength) bytes. - \return the actual length of the plaintext, indication that decryption failed. - */ - virtual DecodingResult Decrypt(RandomNumberGenerator &rng, - const byte *ciphertext, size_t ciphertextLength, - byte *plaintext, const NameValuePairs ¶meters = g_nullNameValuePairs) const =0; - - //! create a new decryption filter - /*! \note caller is responsible for deleting the returned pointer - */ - virtual BufferedTransformation * CreateDecryptionFilter(RandomNumberGenerator &rng, - BufferedTransformation *attachment=NULL, const NameValuePairs ¶meters = g_nullNameValuePairs) const; - - //! decrypt a fixed size ciphertext - DecodingResult FixedLengthDecrypt(RandomNumberGenerator &rng, const byte *ciphertext, byte *plaintext, const NameValuePairs ¶meters = g_nullNameValuePairs) const - {return Decrypt(rng, ciphertext, FixedCiphertextLength(), plaintext, parameters);} -}; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY -typedef PK_CryptoSystem PK_FixedLengthCryptoSystem; -typedef PK_Encryptor PK_FixedLengthEncryptor; -typedef PK_Decryptor PK_FixedLengthDecryptor; -#endif - -//! interface for public-key signers and verifiers - -/*! This class provides an interface common to signers and verifiers - for querying scheme properties. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_SignatureScheme -{ -public: - //! invalid key exception, may be thrown by any function in this class if the private or public key has a length that can't be used - class CRYPTOPP_DLL InvalidKeyLength : public Exception - { - public: - InvalidKeyLength(const std::string &message) : Exception(OTHER_ERROR, message) {} - }; - - //! key too short exception, may be thrown by any function in this class if the private or public key is too short to sign or verify anything - class CRYPTOPP_DLL KeyTooShort : public InvalidKeyLength - { - public: - KeyTooShort() : InvalidKeyLength("PK_Signer: key too short for this signature scheme") {} - }; - - virtual ~PK_SignatureScheme() {} - - //! signature length if it only depends on the key, otherwise 0 - virtual size_t SignatureLength() const =0; - - //! maximum signature length produced for a given length of recoverable message part - virtual size_t MaxSignatureLength(size_t recoverablePartLength = 0) const {return SignatureLength();} - - //! length of longest message that can be recovered, or 0 if this signature scheme does not support message recovery - virtual size_t MaxRecoverableLength() const =0; - - //! length of longest message that can be recovered from a signature of given length, or 0 if this signature scheme does not support message recovery - virtual size_t MaxRecoverableLengthFromSignatureLength(size_t signatureLength) const =0; - - //! requires a random number generator to sign - /*! if this returns false, NullRNG() can be passed to functions that take RandomNumberGenerator & */ - virtual bool IsProbabilistic() const =0; - - //! whether or not a non-recoverable message part can be signed - virtual bool AllowNonrecoverablePart() const =0; - - //! if this function returns true, during verification you must input the signature before the message, otherwise you can input it at anytime */ - virtual bool SignatureUpfront() const {return false;} - - //! whether you must input the recoverable part before the non-recoverable part during signing - virtual bool RecoverablePartFirst() const =0; -}; - -//! interface for accumulating messages to be signed or verified -/*! Only Update() should be called - on this class. No other functions inherited from HashTransformation should be called. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_MessageAccumulator : public HashTransformation -{ -public: - //! should not be called on PK_MessageAccumulator - unsigned int DigestSize() const - {throw NotImplemented("PK_MessageAccumulator: DigestSize() should not be called");} - //! should not be called on PK_MessageAccumulator - void TruncatedFinal(byte *digest, size_t digestSize) - {throw NotImplemented("PK_MessageAccumulator: TruncatedFinal() should not be called");} -}; - -//! interface for public-key signers - -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Signer : public PK_SignatureScheme, public PrivateKeyAlgorithm -{ -public: - //! create a new HashTransformation to accumulate the message to be signed - virtual PK_MessageAccumulator * NewSignatureAccumulator(RandomNumberGenerator &rng) const =0; - - virtual void InputRecoverableMessage(PK_MessageAccumulator &messageAccumulator, const byte *recoverableMessage, size_t recoverableMessageLength) const =0; - - //! sign and delete messageAccumulator (even in case of exception thrown) - /*! \pre size of signature == MaxSignatureLength() - \return actual signature length - */ - virtual size_t Sign(RandomNumberGenerator &rng, PK_MessageAccumulator *messageAccumulator, byte *signature) const; - - //! sign and restart messageAccumulator - /*! \pre size of signature == MaxSignatureLength() - \return actual signature length - */ - virtual size_t SignAndRestart(RandomNumberGenerator &rng, PK_MessageAccumulator &messageAccumulator, byte *signature, bool restart=true) const =0; - - //! sign a message - /*! \pre size of signature == MaxSignatureLength() - \return actual signature length - */ - virtual size_t SignMessage(RandomNumberGenerator &rng, const byte *message, size_t messageLen, byte *signature) const; - - //! sign a recoverable message - /*! \pre size of signature == MaxSignatureLength(recoverableMessageLength) - \return actual signature length - */ - virtual size_t SignMessageWithRecovery(RandomNumberGenerator &rng, const byte *recoverableMessage, size_t recoverableMessageLength, - const byte *nonrecoverableMessage, size_t nonrecoverableMessageLength, byte *signature) const; -}; - -//! interface for public-key signature verifiers -/*! The Recover* functions throw NotImplemented if the signature scheme does not support - message recovery. - The Verify* functions throw InvalidDataFormat if the scheme does support message - recovery and the signature contains a non-empty recoverable message part. The - Recovery* functions should be used in that case. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Verifier : public PK_SignatureScheme, public PublicKeyAlgorithm -{ -public: - //! create a new HashTransformation to accumulate the message to be verified - virtual PK_MessageAccumulator * NewVerificationAccumulator() const =0; - - //! input signature into a message accumulator - virtual void InputSignature(PK_MessageAccumulator &messageAccumulator, const byte *signature, size_t signatureLength) const =0; - - //! check whether messageAccumulator contains a valid signature and message, and delete messageAccumulator (even in case of exception thrown) - virtual bool Verify(PK_MessageAccumulator *messageAccumulator) const; - - //! check whether messageAccumulator contains a valid signature and message, and restart messageAccumulator - virtual bool VerifyAndRestart(PK_MessageAccumulator &messageAccumulator) const =0; - - //! check whether input signature is a valid signature for input message - virtual bool VerifyMessage(const byte *message, size_t messageLen, - const byte *signature, size_t signatureLength) const; - - //! recover a message from its signature - /*! \pre size of recoveredMessage == MaxRecoverableLengthFromSignatureLength(signatureLength) - */ - virtual DecodingResult Recover(byte *recoveredMessage, PK_MessageAccumulator *messageAccumulator) const; - - //! recover a message from its signature - /*! \pre size of recoveredMessage == MaxRecoverableLengthFromSignatureLength(signatureLength) - */ - virtual DecodingResult RecoverAndRestart(byte *recoveredMessage, PK_MessageAccumulator &messageAccumulator) const =0; - - //! recover a message from its signature - /*! \pre size of recoveredMessage == MaxRecoverableLengthFromSignatureLength(signatureLength) - */ - virtual DecodingResult RecoverMessage(byte *recoveredMessage, - const byte *nonrecoverableMessage, size_t nonrecoverableMessageLength, - const byte *signature, size_t signatureLength) const; -}; - -//! interface for domains of simple key agreement protocols - -/*! A key agreement domain is a set of parameters that must be shared - by two parties in a key agreement protocol, along with the algorithms - for generating key pairs and deriving agreed values. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE SimpleKeyAgreementDomain : public KeyAgreementAlgorithm -{ -public: - //! return length of agreed value produced - virtual unsigned int AgreedValueLength() const =0; - //! return length of private keys in this domain - virtual unsigned int PrivateKeyLength() const =0; - //! return length of public keys in this domain - virtual unsigned int PublicKeyLength() const =0; - //! generate private key - /*! \pre size of privateKey == PrivateKeyLength() */ - virtual void GeneratePrivateKey(RandomNumberGenerator &rng, byte *privateKey) const =0; - //! generate public key - /*! \pre size of publicKey == PublicKeyLength() */ - virtual void GeneratePublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const =0; - //! generate private/public key pair - /*! \note equivalent to calling GeneratePrivateKey() and then GeneratePublicKey() */ - virtual void GenerateKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const; - //! derive agreed value from your private key and couterparty's public key, return false in case of failure - /*! \note If you have previously validated the public key, use validateOtherPublicKey=false to save time. - \pre size of agreedValue == AgreedValueLength() - \pre length of privateKey == PrivateKeyLength() - \pre length of otherPublicKey == PublicKeyLength() - */ - virtual bool Agree(byte *agreedValue, const byte *privateKey, const byte *otherPublicKey, bool validateOtherPublicKey=true) const =0; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - bool ValidateDomainParameters(RandomNumberGenerator &rng) const - {return GetCryptoParameters().Validate(rng, 2);} -#endif -}; - -//! interface for domains of authenticated key agreement protocols - -/*! In an authenticated key agreement protocol, each party has two - key pairs. The long-lived key pair is called the static key pair, - and the short-lived key pair is called the ephemeral key pair. -*/ -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AuthenticatedKeyAgreementDomain : public KeyAgreementAlgorithm -{ -public: - //! return length of agreed value produced - virtual unsigned int AgreedValueLength() const =0; - - //! return length of static private keys in this domain - virtual unsigned int StaticPrivateKeyLength() const =0; - //! return length of static public keys in this domain - virtual unsigned int StaticPublicKeyLength() const =0; - //! generate static private key - /*! \pre size of privateKey == PrivateStaticKeyLength() */ - virtual void GenerateStaticPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const =0; - //! generate static public key - /*! \pre size of publicKey == PublicStaticKeyLength() */ - virtual void GenerateStaticPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const =0; - //! generate private/public key pair - /*! \note equivalent to calling GenerateStaticPrivateKey() and then GenerateStaticPublicKey() */ - virtual void GenerateStaticKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const; - - //! return length of ephemeral private keys in this domain - virtual unsigned int EphemeralPrivateKeyLength() const =0; - //! return length of ephemeral public keys in this domain - virtual unsigned int EphemeralPublicKeyLength() const =0; - //! generate ephemeral private key - /*! \pre size of privateKey == PrivateEphemeralKeyLength() */ - virtual void GenerateEphemeralPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const =0; - //! generate ephemeral public key - /*! \pre size of publicKey == PublicEphemeralKeyLength() */ - virtual void GenerateEphemeralPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const =0; - //! generate private/public key pair - /*! \note equivalent to calling GenerateEphemeralPrivateKey() and then GenerateEphemeralPublicKey() */ - virtual void GenerateEphemeralKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const; - - //! derive agreed value from your private keys and couterparty's public keys, return false in case of failure - /*! \note The ephemeral public key will always be validated. - If you have previously validated the static public key, use validateStaticOtherPublicKey=false to save time. - \pre size of agreedValue == AgreedValueLength() - \pre length of staticPrivateKey == StaticPrivateKeyLength() - \pre length of ephemeralPrivateKey == EphemeralPrivateKeyLength() - \pre length of staticOtherPublicKey == StaticPublicKeyLength() - \pre length of ephemeralOtherPublicKey == EphemeralPublicKeyLength() - */ - virtual bool Agree(byte *agreedValue, - const byte *staticPrivateKey, const byte *ephemeralPrivateKey, - const byte *staticOtherPublicKey, const byte *ephemeralOtherPublicKey, - bool validateStaticOtherPublicKey=true) const =0; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY - bool ValidateDomainParameters(RandomNumberGenerator &rng) const - {return GetCryptoParameters().Validate(rng, 2);} -#endif -}; - -// interface for password authenticated key agreement protocols, not implemented yet -#if 0 -//! interface for protocol sessions -/*! The methods should be called in the following order: - - InitializeSession(rng, parameters); // or call initialize method in derived class - while (true) - { - if (OutgoingMessageAvailable()) - { - length = GetOutgoingMessageLength(); - GetOutgoingMessage(message); - ; // send outgoing message - } - - if (LastMessageProcessed()) - break; - - ; // receive incoming message - ProcessIncomingMessage(message); - } - ; // call methods in derived class to obtain result of protocol session -*/ -class ProtocolSession -{ -public: - //! exception thrown when an invalid protocol message is processed - class ProtocolError : public Exception - { - public: - ProtocolError(ErrorType errorType, const std::string &s) : Exception(errorType, s) {} - }; - - //! exception thrown when a function is called unexpectedly - /*! for example calling ProcessIncomingMessage() when ProcessedLastMessage() == true */ - class UnexpectedMethodCall : public Exception - { - public: - UnexpectedMethodCall(const std::string &s) : Exception(OTHER_ERROR, s) {} - }; - - ProtocolSession() : m_rng(NULL), m_throwOnProtocolError(true), m_validState(false) {} - virtual ~ProtocolSession() {} - - virtual void InitializeSession(RandomNumberGenerator &rng, const NameValuePairs ¶meters) =0; - - bool GetThrowOnProtocolError() const {return m_throwOnProtocolError;} - void SetThrowOnProtocolError(bool throwOnProtocolError) {m_throwOnProtocolError = throwOnProtocolError;} - - bool HasValidState() const {return m_validState;} - - virtual bool OutgoingMessageAvailable() const =0; - virtual unsigned int GetOutgoingMessageLength() const =0; - virtual void GetOutgoingMessage(byte *message) =0; - - virtual bool LastMessageProcessed() const =0; - virtual void ProcessIncomingMessage(const byte *message, unsigned int messageLength) =0; - -protected: - void HandleProtocolError(Exception::ErrorType errorType, const std::string &s) const; - void CheckAndHandleInvalidState() const; - void SetValidState(bool valid) {m_validState = valid;} - - RandomNumberGenerator *m_rng; - -private: - bool m_throwOnProtocolError, m_validState; -}; - -class KeyAgreementSession : public ProtocolSession -{ -public: - virtual unsigned int GetAgreedValueLength() const =0; - virtual void GetAgreedValue(byte *agreedValue) const =0; -}; - -class PasswordAuthenticatedKeyAgreementSession : public KeyAgreementSession -{ -public: - void InitializePasswordAuthenticatedKeyAgreementSession(RandomNumberGenerator &rng, - const byte *myId, unsigned int myIdLength, - const byte *counterPartyId, unsigned int counterPartyIdLength, - const byte *passwordOrVerifier, unsigned int passwordOrVerifierLength); -}; - -class PasswordAuthenticatedKeyAgreementDomain : public KeyAgreementAlgorithm -{ -public: - //! return whether the domain parameters stored in this object are valid - virtual bool ValidateDomainParameters(RandomNumberGenerator &rng) const - {return GetCryptoParameters().Validate(rng, 2);} - - virtual unsigned int GetPasswordVerifierLength(const byte *password, unsigned int passwordLength) const =0; - virtual void GeneratePasswordVerifier(RandomNumberGenerator &rng, const byte *userId, unsigned int userIdLength, const byte *password, unsigned int passwordLength, byte *verifier) const =0; - - enum RoleFlags {CLIENT=1, SERVER=2, INITIATOR=4, RESPONDER=8}; - - virtual bool IsValidRole(unsigned int role) =0; - virtual PasswordAuthenticatedKeyAgreementSession * CreateProtocolSession(unsigned int role) const =0; -}; -#endif - -//! BER Decode Exception Class, may be thrown during an ASN1 BER decode operation -class CRYPTOPP_DLL BERDecodeErr : public InvalidArgument -{ -public: - BERDecodeErr() : InvalidArgument("BER decode error") {} - BERDecodeErr(const std::string &s) : InvalidArgument(s) {} -}; - -//! interface for encoding and decoding ASN1 objects -class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE ASN1Object -{ -public: - virtual ~ASN1Object() {} - //! decode this object from a BufferedTransformation, using BER (Basic Encoding Rules) - virtual void BERDecode(BufferedTransformation &bt) =0; - //! encode this object into a BufferedTransformation, using DER (Distinguished Encoding Rules) - virtual void DEREncode(BufferedTransformation &bt) const =0; - //! encode this object into a BufferedTransformation, using BER - /*! this may be useful if DEREncode() would be too inefficient */ - virtual void BEREncode(BufferedTransformation &bt) const {DEREncode(bt);} -}; - -#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY -typedef PK_SignatureScheme PK_SignatureSystem; -typedef SimpleKeyAgreementDomain PK_SimpleKeyAgreementDomain; -typedef AuthenticatedKeyAgreementDomain PK_AuthenticatedKeyAgreementDomain; -#endif - -NAMESPACE_END - -#endif diff --git a/cryptopp/include/cryptopp/iterhash.h b/cryptopp/include/cryptopp/iterhash.h deleted file mode 100644 index 0e2a147728..0000000000 --- a/cryptopp/include/cryptopp/iterhash.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef CRYPTOPP_ITERHASH_H -#define CRYPTOPP_ITERHASH_H - -#include "cryptopp/secblock.h" - -NAMESPACE_BEGIN(CryptoPP) - -// *** trimmed down dependency from iterhash.h *** -template -class CRYPTOPP_NO_VTABLE IteratedHashWithStaticTransform -{ -public: - CRYPTOPP_CONSTANT(DIGESTSIZE = T_DigestSize ? T_DigestSize : T_StateSize) - unsigned int DigestSize() const {return DIGESTSIZE;}; - typedef T_HashWordType HashWordType; - CRYPTOPP_CONSTANT(BLOCKSIZE = T_BlockSize) - -protected: - IteratedHashWithStaticTransform() {this->Init();} - void HashEndianCorrectedBlock(const T_HashWordType *data) {T_Transform::Transform(this->m_state, data);} - void Init() {T_Transform::InitState(this->m_state);} - - T_HashWordType* StateBuf() {return this->m_state;} - FixedSizeAlignedSecBlock m_state; -}; - -NAMESPACE_END - -#endif diff --git a/cryptopp/include/cryptopp/misc.h b/cryptopp/include/cryptopp/misc.h deleted file mode 100644 index 5258e2abca..0000000000 --- a/cryptopp/include/cryptopp/misc.h +++ /dev/null @@ -1,1134 +0,0 @@ -#ifndef CRYPTOPP_MISC_H -#define CRYPTOPP_MISC_H - -#include "cryptopp/cryptlib.h" -#include "cryptopp/smartptr.h" -#include // for memcpy and memmove - -#ifdef _MSC_VER - #include - #if _MSC_VER >= 1400 - // VC2005 workaround: disable declarations that conflict with winnt.h - #define _interlockedbittestandset CRYPTOPP_DISABLED_INTRINSIC_1 - #define _interlockedbittestandreset CRYPTOPP_DISABLED_INTRINSIC_2 - #define _interlockedbittestandset64 CRYPTOPP_DISABLED_INTRINSIC_3 - #define _interlockedbittestandreset64 CRYPTOPP_DISABLED_INTRINSIC_4 - #include - #undef _interlockedbittestandset - #undef _interlockedbittestandreset - #undef _interlockedbittestandset64 - #undef _interlockedbittestandreset64 - #define CRYPTOPP_FAST_ROTATE(x) 1 - #elif _MSC_VER >= 1300 - #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32 | (x) == 64) - #else - #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32) - #endif -#elif (defined(__MWERKS__) && TARGET_CPU_PPC) || \ - (defined(__GNUC__) && (defined(_ARCH_PWR2) || defined(_ARCH_PWR) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || defined(_ARCH_COM))) - #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32) -#elif defined(__GNUC__) && (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X86) // depend on GCC's peephole optimization to generate rotate instructions - #define CRYPTOPP_FAST_ROTATE(x) 1 -#else - #define CRYPTOPP_FAST_ROTATE(x) 0 -#endif - -#ifdef __BORLANDC__ -#include -#endif - -#if defined(__GNUC__) && defined(__linux__) -#define CRYPTOPP_BYTESWAP_AVAILABLE -#include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -// ************** compile-time assertion *************** - -template -struct CompileAssert -{ - static char dummy[2*b-1]; -}; - -#define CRYPTOPP_COMPILE_ASSERT(assertion) CRYPTOPP_COMPILE_ASSERT_INSTANCE(assertion, __LINE__) -#if defined(CRYPTOPP_EXPORTS) || defined(CRYPTOPP_IMPORTS) -#define CRYPTOPP_COMPILE_ASSERT_INSTANCE(assertion, instance) -#else -#define CRYPTOPP_COMPILE_ASSERT_INSTANCE(assertion, instance) static CompileAssert<(assertion)> CRYPTOPP_ASSERT_JOIN(cryptopp_assert_, instance) -#endif -#define CRYPTOPP_ASSERT_JOIN(X, Y) CRYPTOPP_DO_ASSERT_JOIN(X, Y) -#define CRYPTOPP_DO_ASSERT_JOIN(X, Y) X##Y - -// ************** misc classes *************** - -class CRYPTOPP_DLL Empty -{ -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE TwoBases : public BASE1, public BASE2 -{ -}; - -//! _ -template -class CRYPTOPP_NO_VTABLE ThreeBases : public BASE1, public BASE2, public BASE3 -{ -}; - -template -class ObjectHolder -{ -protected: - T m_object; -}; - -class NotCopyable -{ -public: - NotCopyable() {} -private: - NotCopyable(const NotCopyable &); - void operator=(const NotCopyable &); -}; - -template -struct NewObject -{ - T* operator()() const {return new T;} -}; - -/*! This function safely initializes a static object in a multithreaded environment without using locks. - It may leak memory when two threads try to initialize the static object at the same time - but this should be acceptable since each static object is only initialized once per session. -*/ -template , int instance=0> -class Singleton -{ -public: - Singleton(F objectFactory = F()) : m_objectFactory(objectFactory) {} - - // prevent this function from being inlined - CRYPTOPP_NOINLINE const T & Ref(CRYPTOPP_NOINLINE_DOTDOTDOT) const; - -private: - F m_objectFactory; -}; - -template -const T & Singleton::Ref(CRYPTOPP_NOINLINE_DOTDOTDOT) const -{ - static simple_ptr s_pObject; - static char s_objectState = 0; - -retry: - switch (s_objectState) - { - case 0: - s_objectState = 1; - try - { - s_pObject.m_p = m_objectFactory(); - } - catch(...) - { - s_objectState = 0; - throw; - } - s_objectState = 2; - break; - case 1: - goto retry; - default: - break; - } - return *s_pObject.m_p; -} - -// ************** misc functions *************** - -#if (!__STDC_WANT_SECURE_LIB__) -inline void memcpy_s(void *dest, size_t sizeInBytes, const void *src, size_t count) -{ - if (count > sizeInBytes) - throw InvalidArgument("memcpy_s: buffer overflow"); - memcpy(dest, src, count); -} - -inline void memmove_s(void *dest, size_t sizeInBytes, const void *src, size_t count) -{ - if (count > sizeInBytes) - throw InvalidArgument("memmove_s: buffer overflow"); - memmove(dest, src, count); -} -#endif - -inline void * memset_z(void *ptr, int value, size_t num) -{ -// avoid extranous warning on GCC 4.3.2 Ubuntu 8.10 -#if CRYPTOPP_GCC_VERSION >= 30001 - if (__builtin_constant_p(num) && num==0) - return ptr; -#endif - return memset(ptr, value, num); -} - -// can't use std::min or std::max in MSVC60 or Cygwin 1.1.0 -template inline const T& STDMIN(const T& a, const T& b) -{ - return b < a ? b : a; -} - -template inline const T1 UnsignedMin(const T1& a, const T2& b) -{ - CRYPTOPP_COMPILE_ASSERT((sizeof(T1)<=sizeof(T2) && T2(-1)>0) || (sizeof(T1)>sizeof(T2) && T1(-1)>0)); - assert(a==0 || a>0); // GCC workaround: get rid of the warning "comparison is always true due to limited range of data type" - assert(b>=0); - - if (sizeof(T1)<=sizeof(T2)) - return b < (T2)a ? (T1)b : a; - else - return (T1)b < a ? (T1)b : a; -} - -template inline const T& STDMAX(const T& a, const T& b) -{ - return a < b ? b : a; -} - -#define RETURN_IF_NONZERO(x) size_t returnedValue = x; if (returnedValue) return returnedValue - -// this version of the macro is fastest on Pentium 3 and Pentium 4 with MSVC 6 SP5 w/ Processor Pack -#define GETBYTE(x, y) (unsigned int)byte((x)>>(8*(y))) -// these may be faster on other CPUs/compilers -// #define GETBYTE(x, y) (unsigned int)(((x)>>(8*(y)))&255) -// #define GETBYTE(x, y) (((byte *)&(x))[y]) - -#define CRYPTOPP_GET_BYTE_AS_BYTE(x, y) byte((x)>>(8*(y))) - -template -unsigned int Parity(T value) -{ - for (unsigned int i=8*sizeof(value)/2; i>0; i/=2) - value ^= value >> i; - return (unsigned int)value&1; -} - -template -unsigned int BytePrecision(const T &value) -{ - if (!value) - return 0; - - unsigned int l=0, h=8*sizeof(value); - - while (h-l > 8) - { - unsigned int t = (l+h)/2; - if (value >> t) - l = t; - else - h = t; - } - - return h/8; -} - -template -unsigned int BitPrecision(const T &value) -{ - if (!value) - return 0; - - unsigned int l=0, h=8*sizeof(value); - - while (h-l > 1) - { - unsigned int t = (l+h)/2; - if (value >> t) - l = t; - else - h = t; - } - - return h; -} - -template -inline T Crop(T value, size_t size) -{ - if (size < 8*sizeof(value)) - return T(value & ((T(1) << size) - 1)); - else - return value; -} - -template -inline bool SafeConvert(T1 from, T2 &to) -{ - to = (T2)from; - if (from != to || (from > 0) != (to > 0)) - return false; - return true; -} - -inline size_t BitsToBytes(size_t bitCount) -{ - return ((bitCount+7)/(8)); -} - -inline size_t BytesToWords(size_t byteCount) -{ - return ((byteCount+WORD_SIZE-1)/WORD_SIZE); -} - -inline size_t BitsToWords(size_t bitCount) -{ - return ((bitCount+WORD_BITS-1)/(WORD_BITS)); -} - -inline size_t BitsToDwords(size_t bitCount) -{ - return ((bitCount+2*WORD_BITS-1)/(2*WORD_BITS)); -} - -CRYPTOPP_DLL void CRYPTOPP_API xorbuf(byte *buf, const byte *mask, size_t count); -CRYPTOPP_DLL void CRYPTOPP_API xorbuf(byte *output, const byte *input, const byte *mask, size_t count); - -CRYPTOPP_DLL bool CRYPTOPP_API VerifyBufsEqual(const byte *buf1, const byte *buf2, size_t count); - -template -inline bool IsPowerOf2(const T &n) -{ - return n > 0 && (n & (n-1)) == 0; -} - -template -inline T2 ModPowerOf2(const T1 &a, const T2 &b) -{ - assert(IsPowerOf2(b)); - return T2(a) & (b-1); -} - -template -inline T1 RoundDownToMultipleOf(const T1 &n, const T2 &m) -{ - if (IsPowerOf2(m)) - return n - ModPowerOf2(n, m); - else - return n - n%m; -} - -template -inline T1 RoundUpToMultipleOf(const T1 &n, const T2 &m) -{ - if (n+m-1 < n) - throw InvalidArgument("RoundUpToMultipleOf: integer overflow"); - return RoundDownToMultipleOf(n+m-1, m); -} - -template -inline unsigned int GetAlignmentOf(T *dummy=NULL) // VC60 workaround -{ -#ifdef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - if (sizeof(T) < 16) - return 1; -#endif - -#if (_MSC_VER >= 1300) - return __alignof(T); -#elif defined(__GNUC__) - return __alignof__(T); -#elif CRYPTOPP_BOOL_SLOW_WORD64 - return UnsignedMin(4U, sizeof(T)); -#else - return sizeof(T); -#endif -} - -inline bool IsAlignedOn(const void *p, unsigned int alignment) -{ - return alignment==1 || (IsPowerOf2(alignment) ? ModPowerOf2((size_t)p, alignment) == 0 : (size_t)p % alignment == 0); -} - -template -inline bool IsAligned(const void *p, T *dummy=NULL) // VC60 workaround -{ - return IsAlignedOn(p, GetAlignmentOf()); -} - -#ifdef IS_LITTLE_ENDIAN - typedef LittleEndian NativeByteOrder; -#else - typedef BigEndian NativeByteOrder; -#endif - -inline ByteOrder GetNativeByteOrder() -{ - return NativeByteOrder::ToEnum(); -} - -inline bool NativeByteOrderIs(ByteOrder order) -{ - return order == GetNativeByteOrder(); -} - -template -std::string IntToString(T a, unsigned int base = 10) -{ - if (a == 0) - return "0"; - bool negate = false; - if (a < 0) - { - negate = true; - a = 0-a; // VC .NET does not like -a - } - std::string result; - while (a > 0) - { - T digit = a % base; - result = char((digit < 10 ? '0' : ('a' - 10)) + digit) + result; - a /= base; - } - if (negate) - result = "-" + result; - return result; -} - -template -inline T1 SaturatingSubtract(const T1 &a, const T2 &b) -{ - return T1((a > b) ? (a - b) : 0); -} - -template -inline CipherDir GetCipherDir(const T &obj) -{ - return obj.IsForwardTransformation() ? ENCRYPTION : DECRYPTION; -} - -CRYPTOPP_DLL void CRYPTOPP_API CallNewHandler(); - -inline void IncrementCounterByOne(byte *inout, unsigned int s) -{ - for (int i=s-1, carry=1; i>=0 && carry; i--) - carry = !++inout[i]; -} - -inline void IncrementCounterByOne(byte *output, const byte *input, unsigned int s) -{ - int i, carry; - for (i=s-1, carry=1; i>=0 && carry; i--) - carry = ((output[i] = input[i]+1) == 0); - memcpy_s(output, s, input, i+1); -} - -// ************** rotate functions *************** - -template inline T rotlFixed(T x, unsigned int y) -{ - assert(y < sizeof(T)*8); - return T((x<>(sizeof(T)*8-y))); -} - -template inline T rotrFixed(T x, unsigned int y) -{ - assert(y < sizeof(T)*8); - return T((x>>y) | (x<<(sizeof(T)*8-y))); -} - -template inline T rotlVariable(T x, unsigned int y) -{ - assert(y < sizeof(T)*8); - return T((x<>(sizeof(T)*8-y))); -} - -template inline T rotrVariable(T x, unsigned int y) -{ - assert(y < sizeof(T)*8); - return T((x>>y) | (x<<(sizeof(T)*8-y))); -} - -template inline T rotlMod(T x, unsigned int y) -{ - y %= sizeof(T)*8; - return T((x<>(sizeof(T)*8-y))); -} - -template inline T rotrMod(T x, unsigned int y) -{ - y %= sizeof(T)*8; - return T((x>>y) | (x<<(sizeof(T)*8-y))); -} - -#ifdef _MSC_VER - -template<> inline word32 rotlFixed(word32 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _lrotl(x, y) : x; -} - -template<> inline word32 rotrFixed(word32 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _lrotr(x, y) : x; -} - -template<> inline word32 rotlVariable(word32 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _lrotl(x, y); -} - -template<> inline word32 rotrVariable(word32 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _lrotr(x, y); -} - -template<> inline word32 rotlMod(word32 x, unsigned int y) -{ - return _lrotl(x, y); -} - -template<> inline word32 rotrMod(word32 x, unsigned int y) -{ - return _lrotr(x, y); -} - -#endif // #ifdef _MSC_VER - -#if _MSC_VER >= 1300 && !defined(__INTEL_COMPILER) -// Intel C++ Compiler 10.0 calls a function instead of using the rotate instruction when using these instructions - -template<> inline word64 rotlFixed(word64 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _rotl64(x, y) : x; -} - -template<> inline word64 rotrFixed(word64 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _rotr64(x, y) : x; -} - -template<> inline word64 rotlVariable(word64 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _rotl64(x, y); -} - -template<> inline word64 rotrVariable(word64 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _rotr64(x, y); -} - -template<> inline word64 rotlMod(word64 x, unsigned int y) -{ - return _rotl64(x, y); -} - -template<> inline word64 rotrMod(word64 x, unsigned int y) -{ - return _rotr64(x, y); -} - -#endif // #if _MSC_VER >= 1310 - -#if _MSC_VER >= 1400 && !defined(__INTEL_COMPILER) -// Intel C++ Compiler 10.0 gives undefined externals with these - -template<> inline word16 rotlFixed(word16 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _rotl16(x, y) : x; -} - -template<> inline word16 rotrFixed(word16 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _rotr16(x, y) : x; -} - -template<> inline word16 rotlVariable(word16 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _rotl16(x, y); -} - -template<> inline word16 rotrVariable(word16 x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _rotr16(x, y); -} - -template<> inline word16 rotlMod(word16 x, unsigned int y) -{ - return _rotl16(x, y); -} - -template<> inline word16 rotrMod(word16 x, unsigned int y) -{ - return _rotr16(x, y); -} - -template<> inline byte rotlFixed(byte x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _rotl8(x, y) : x; -} - -template<> inline byte rotrFixed(byte x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return y ? _rotr8(x, y) : x; -} - -template<> inline byte rotlVariable(byte x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _rotl8(x, y); -} - -template<> inline byte rotrVariable(byte x, unsigned int y) -{ - assert(y < 8*sizeof(x)); - return _rotr8(x, y); -} - -template<> inline byte rotlMod(byte x, unsigned int y) -{ - return _rotl8(x, y); -} - -template<> inline byte rotrMod(byte x, unsigned int y) -{ - return _rotr8(x, y); -} - -#endif // #if _MSC_VER >= 1400 - -#if (defined(__MWERKS__) && TARGET_CPU_PPC) - -template<> inline word32 rotlFixed(word32 x, unsigned int y) -{ - assert(y < 32); - return y ? __rlwinm(x,y,0,31) : x; -} - -template<> inline word32 rotrFixed(word32 x, unsigned int y) -{ - assert(y < 32); - return y ? __rlwinm(x,32-y,0,31) : x; -} - -template<> inline word32 rotlVariable(word32 x, unsigned int y) -{ - assert(y < 32); - return (__rlwnm(x,y,0,31)); -} - -template<> inline word32 rotrVariable(word32 x, unsigned int y) -{ - assert(y < 32); - return (__rlwnm(x,32-y,0,31)); -} - -template<> inline word32 rotlMod(word32 x, unsigned int y) -{ - return (__rlwnm(x,y,0,31)); -} - -template<> inline word32 rotrMod(word32 x, unsigned int y) -{ - return (__rlwnm(x,32-y,0,31)); -} - -#endif // #if (defined(__MWERKS__) && TARGET_CPU_PPC) - -// ************** endian reversal *************** - -template -inline unsigned int GetByte(ByteOrder order, T value, unsigned int index) -{ - if (order == LITTLE_ENDIAN_ORDER) - return GETBYTE(value, index); - else - return GETBYTE(value, sizeof(T)-index-1); -} - -inline byte ByteReverse(byte value) -{ - return value; -} - -inline word16 ByteReverse(word16 value) -{ -#ifdef CRYPTOPP_BYTESWAP_AVAILABLE - return bswap_16(value); -#elif defined(_MSC_VER) && _MSC_VER >= 1300 - return _byteswap_ushort(value); -#else - return rotlFixed(value, 8U); -#endif -} - -inline word32 ByteReverse(word32 value) -{ -#if defined(__GNUC__) && defined(CRYPTOPP_X86_ASM_AVAILABLE) - __asm__ ("bswap %0" : "=r" (value) : "0" (value)); - return value; -#elif defined(CRYPTOPP_BYTESWAP_AVAILABLE) - return bswap_32(value); -#elif defined(__MWERKS__) && TARGET_CPU_PPC - return (word32)__lwbrx(&value,0); -#elif _MSC_VER >= 1400 || (_MSC_VER >= 1300 && !defined(_DLL)) - return _byteswap_ulong(value); -#elif CRYPTOPP_FAST_ROTATE(32) - // 5 instructions with rotate instruction, 9 without - return (rotrFixed(value, 8U) & 0xff00ff00) | (rotlFixed(value, 8U) & 0x00ff00ff); -#else - // 6 instructions with rotate instruction, 8 without - value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); - return rotlFixed(value, 16U); -#endif -} - -inline word64 ByteReverse(word64 value) -{ -#if defined(__GNUC__) && defined(CRYPTOPP_X86_ASM_AVAILABLE) && defined(__x86_64__) - __asm__ ("bswap %0" : "=r" (value) : "0" (value)); - return value; -#elif defined(CRYPTOPP_BYTESWAP_AVAILABLE) - return bswap_64(value); -#elif defined(_MSC_VER) && _MSC_VER >= 1300 - return _byteswap_uint64(value); -#elif CRYPTOPP_BOOL_SLOW_WORD64 - return (word64(ByteReverse(word32(value))) << 32) | ByteReverse(word32(value>>32)); -#else - value = ((value & W64LIT(0xFF00FF00FF00FF00)) >> 8) | ((value & W64LIT(0x00FF00FF00FF00FF)) << 8); - value = ((value & W64LIT(0xFFFF0000FFFF0000)) >> 16) | ((value & W64LIT(0x0000FFFF0000FFFF)) << 16); - return rotlFixed(value, 32U); -#endif -} - -inline byte BitReverse(byte value) -{ - value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); - value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); - return rotlFixed(value, 4U); -} - -inline word16 BitReverse(word16 value) -{ - value = ((value & 0xAAAA) >> 1) | ((value & 0x5555) << 1); - value = ((value & 0xCCCC) >> 2) | ((value & 0x3333) << 2); - value = ((value & 0xF0F0) >> 4) | ((value & 0x0F0F) << 4); - return ByteReverse(value); -} - -inline word32 BitReverse(word32 value) -{ - value = ((value & 0xAAAAAAAA) >> 1) | ((value & 0x55555555) << 1); - value = ((value & 0xCCCCCCCC) >> 2) | ((value & 0x33333333) << 2); - value = ((value & 0xF0F0F0F0) >> 4) | ((value & 0x0F0F0F0F) << 4); - return ByteReverse(value); -} - -inline word64 BitReverse(word64 value) -{ -#if CRYPTOPP_BOOL_SLOW_WORD64 - return (word64(BitReverse(word32(value))) << 32) | BitReverse(word32(value>>32)); -#else - value = ((value & W64LIT(0xAAAAAAAAAAAAAAAA)) >> 1) | ((value & W64LIT(0x5555555555555555)) << 1); - value = ((value & W64LIT(0xCCCCCCCCCCCCCCCC)) >> 2) | ((value & W64LIT(0x3333333333333333)) << 2); - value = ((value & W64LIT(0xF0F0F0F0F0F0F0F0)) >> 4) | ((value & W64LIT(0x0F0F0F0F0F0F0F0F)) << 4); - return ByteReverse(value); -#endif -} - -template -inline T BitReverse(T value) -{ - if (sizeof(T) == 1) - return (T)BitReverse((byte)value); - else if (sizeof(T) == 2) - return (T)BitReverse((word16)value); - else if (sizeof(T) == 4) - return (T)BitReverse((word32)value); - else - { - assert(sizeof(T) == 8); - return (T)BitReverse((word64)value); - } -} - -template -inline T ConditionalByteReverse(ByteOrder order, T value) -{ - return NativeByteOrderIs(order) ? value : ByteReverse(value); -} - -template -void ByteReverse(T *out, const T *in, size_t byteCount) -{ - assert(byteCount % sizeof(T) == 0); - size_t count = byteCount/sizeof(T); - for (size_t i=0; i -inline void ConditionalByteReverse(ByteOrder order, T *out, const T *in, size_t byteCount) -{ - if (!NativeByteOrderIs(order)) - ByteReverse(out, in, byteCount); - else if (in != out) - memcpy_s(out, byteCount, in, byteCount); -} - -template -inline void GetUserKey(ByteOrder order, T *out, size_t outlen, const byte *in, size_t inlen) -{ - const size_t U = sizeof(T); - assert(inlen <= outlen*U); - memcpy_s(out, outlen*U, in, inlen); - memset_z((byte *)out+inlen, 0, outlen*U-inlen); - ConditionalByteReverse(order, out, out, RoundUpToMultipleOf(inlen, U)); -} - -#ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS -inline byte UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const byte *) -{ - return block[0]; -} - -inline word16 UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const word16 *) -{ - return (order == BIG_ENDIAN_ORDER) - ? block[1] | (block[0] << 8) - : block[0] | (block[1] << 8); -} - -inline word32 UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const word32 *) -{ - return (order == BIG_ENDIAN_ORDER) - ? word32(block[3]) | (word32(block[2]) << 8) | (word32(block[1]) << 16) | (word32(block[0]) << 24) - : word32(block[0]) | (word32(block[1]) << 8) | (word32(block[2]) << 16) | (word32(block[3]) << 24); -} - -inline word64 UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const word64 *) -{ - return (order == BIG_ENDIAN_ORDER) - ? - (word64(block[7]) | - (word64(block[6]) << 8) | - (word64(block[5]) << 16) | - (word64(block[4]) << 24) | - (word64(block[3]) << 32) | - (word64(block[2]) << 40) | - (word64(block[1]) << 48) | - (word64(block[0]) << 56)) - : - (word64(block[0]) | - (word64(block[1]) << 8) | - (word64(block[2]) << 16) | - (word64(block[3]) << 24) | - (word64(block[4]) << 32) | - (word64(block[5]) << 40) | - (word64(block[6]) << 48) | - (word64(block[7]) << 56)); -} - -inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, byte value, const byte *xorBlock) -{ - block[0] = xorBlock ? (value ^ xorBlock[0]) : value; -} - -inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, word16 value, const byte *xorBlock) -{ - if (order == BIG_ENDIAN_ORDER) - { - if (xorBlock) - { - block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - } - else - { - block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - } - } - else - { - if (xorBlock) - { - block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - } - else - { - block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - } - } -} - -inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, word32 value, const byte *xorBlock) -{ - if (order == BIG_ENDIAN_ORDER) - { - if (xorBlock) - { - block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - } - else - { - block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - } - } - else - { - if (xorBlock) - { - block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - } - else - { - block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - } - } -} - -inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, word64 value, const byte *xorBlock) -{ - if (order == BIG_ENDIAN_ORDER) - { - if (xorBlock) - { - block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); - block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); - block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); - block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); - block[4] = xorBlock[4] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - block[5] = xorBlock[5] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[6] = xorBlock[6] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[7] = xorBlock[7] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - } - else - { - block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); - block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); - block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); - block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); - block[4] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - block[5] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[6] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[7] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - } - } - else - { - if (xorBlock) - { - block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - block[4] = xorBlock[4] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); - block[5] = xorBlock[5] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); - block[6] = xorBlock[6] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); - block[7] = xorBlock[7] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); - } - else - { - block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); - block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); - block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); - block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); - block[4] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); - block[5] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); - block[6] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); - block[7] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); - } - } -} -#endif // #ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - -template -inline T GetWord(bool assumeAligned, ByteOrder order, const byte *block) -{ -#ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - if (!assumeAligned) - return UnalignedGetWordNonTemplate(order, block, (T*)NULL); - assert(IsAligned(block)); -#endif - return ConditionalByteReverse(order, *reinterpret_cast(block)); -} - -template -inline void GetWord(bool assumeAligned, ByteOrder order, T &result, const byte *block) -{ - result = GetWord(assumeAligned, order, block); -} - -template -inline void PutWord(bool assumeAligned, ByteOrder order, byte *block, T value, const byte *xorBlock = NULL) -{ -#ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS - if (!assumeAligned) - return UnalignedPutWordNonTemplate(order, block, value, xorBlock); - assert(IsAligned(block)); - assert(IsAligned(xorBlock)); -#endif - *reinterpret_cast(block) = ConditionalByteReverse(order, value) ^ (xorBlock ? *reinterpret_cast(xorBlock) : 0); -} - -template -class GetBlock -{ -public: - GetBlock(const void *block) - : m_block((const byte *)block) {} - - template - inline GetBlock & operator()(U &x) - { - CRYPTOPP_COMPILE_ASSERT(sizeof(U) >= sizeof(T)); - x = GetWord(A, B::ToEnum(), m_block); - m_block += sizeof(T); - return *this; - } - -private: - const byte *m_block; -}; - -template -class PutBlock -{ -public: - PutBlock(const void *xorBlock, void *block) - : m_xorBlock((const byte *)xorBlock), m_block((byte *)block) {} - - template - inline PutBlock & operator()(U x) - { - PutWord(A, B::ToEnum(), m_block, (T)x, m_xorBlock); - m_block += sizeof(T); - if (m_xorBlock) - m_xorBlock += sizeof(T); - return *this; - } - -private: - const byte *m_xorBlock; - byte *m_block; -}; - -template -struct BlockGetAndPut -{ - // function needed because of C++ grammatical ambiguity between expression-statements and declarations - static inline GetBlock Get(const void *block) {return GetBlock(block);} - typedef PutBlock Put; -}; - -template -std::string WordToString(T value, ByteOrder order = BIG_ENDIAN_ORDER) -{ - if (!NativeByteOrderIs(order)) - value = ByteReverse(value); - - return std::string((char *)&value, sizeof(value)); -} - -template -T StringToWord(const std::string &str, ByteOrder order = BIG_ENDIAN_ORDER) -{ - T value = 0; - memcpy_s(&value, sizeof(value), str.data(), UnsignedMin(str.size(), sizeof(value))); - return NativeByteOrderIs(order) ? value : ByteReverse(value); -} - -// ************** help remove warning on g++ *************** - -template struct SafeShifter; - -template<> struct SafeShifter -{ - template - static inline T RightShift(T value, unsigned int bits) - { - return 0; - } - - template - static inline T LeftShift(T value, unsigned int bits) - { - return 0; - } -}; - -template<> struct SafeShifter -{ - template - static inline T RightShift(T value, unsigned int bits) - { - return value >> bits; - } - - template - static inline T LeftShift(T value, unsigned int bits) - { - return value << bits; - } -}; - -template -inline T SafeRightShift(T value) -{ - return SafeShifter<(bits>=(8*sizeof(T)))>::RightShift(value, bits); -} - -template -inline T SafeLeftShift(T value) -{ - return SafeShifter<(bits>=(8*sizeof(T)))>::LeftShift(value, bits); -} - -// ************** use one buffer for multiple data members *************** - -#define CRYPTOPP_BLOCK_1(n, t, s) t* m_##n() {return (t *)(m_aggregate+0);} size_t SS1() {return sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_2(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS1());} size_t SS2() {return SS1()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_3(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS2());} size_t SS3() {return SS2()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_4(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS3());} size_t SS4() {return SS3()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_5(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS4());} size_t SS5() {return SS4()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_6(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS5());} size_t SS6() {return SS5()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_7(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS6());} size_t SS7() {return SS6()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCK_8(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS7());} size_t SS8() {return SS7()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} -#define CRYPTOPP_BLOCKS_END(i) size_t SST() {return SS##i();} void AllocateBlocks() {m_aggregate.New(SST());} AlignedSecByteBlock m_aggregate; - -NAMESPACE_END - -#endif diff --git a/cryptopp/include/cryptopp/pch.h b/cryptopp/include/cryptopp/pch.h deleted file mode 100644 index 418c39076d..0000000000 --- a/cryptopp/include/cryptopp/pch.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef CRYPTOPP_PCH_H -#define CRYPTOPP_PCH_H - -#ifdef CRYPTOPP_GENERATE_X64_MASM - - #include "cpu.h" - -#else - - #include "config.h" - - #ifdef USE_PRECOMPILED_HEADERS - #include "simple.h" - #include "secblock.h" - #include "misc.h" - #include "smartptr.h" - #endif - -#endif - -#endif diff --git a/cryptopp/include/cryptopp/secblock.h b/cryptopp/include/cryptopp/secblock.h deleted file mode 100644 index 6433f97349..0000000000 --- a/cryptopp/include/cryptopp/secblock.h +++ /dev/null @@ -1,501 +0,0 @@ -// secblock.h - written and placed in the public domain by Wei Dai - -#ifndef CRYPTOPP_SECBLOCK_H -#define CRYPTOPP_SECBLOCK_H - -#include "cryptopp/config.h" -#include "cryptopp/misc.h" -#include - -#if defined(CRYPTOPP_MEMALIGN_AVAILABLE) || defined(CRYPTOPP_MM_MALLOC_AVAILABLE) || defined(QNX) - #include -#else - #include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -// ************** secure memory allocation *************** - -template -class AllocatorBase -{ -public: - typedef T value_type; - typedef size_t size_type; -#ifdef CRYPTOPP_MSVCRT6 - typedef ptrdiff_t difference_type; -#else - typedef std::ptrdiff_t difference_type; -#endif - typedef T * pointer; - typedef const T * const_pointer; - typedef T & reference; - typedef const T & const_reference; - - pointer address(reference r) const {return (&r);} - const_pointer address(const_reference r) const {return (&r); } - void construct(pointer p, const T& val) {new (p) T(val);} - void destroy(pointer p) {p->~T();} - size_type max_size() const {return ~size_type(0)/sizeof(T);} // switch to std::numeric_limits::max later - -protected: - static void CheckSize(size_t n) - { - if (n > ~size_t(0) / sizeof(T)) - throw InvalidArgument("AllocatorBase: requested size would cause integer overflow"); - } -}; - -#define CRYPTOPP_INHERIT_ALLOCATOR_TYPES \ -typedef typename AllocatorBase::value_type value_type;\ -typedef typename AllocatorBase::size_type size_type;\ -typedef typename AllocatorBase::difference_type difference_type;\ -typedef typename AllocatorBase::pointer pointer;\ -typedef typename AllocatorBase::const_pointer const_pointer;\ -typedef typename AllocatorBase::reference reference;\ -typedef typename AllocatorBase::const_reference const_reference; - -#if defined(_MSC_VER) && (_MSC_VER < 1300) -// this pragma causes an internal compiler error if placed immediately before std::swap(a, b) -#pragma warning(push) -#pragma warning(disable: 4700) // VC60 workaround: don't know how to get rid of this warning -#endif - -template -typename A::pointer StandardReallocate(A& a, T *p, typename A::size_type oldSize, typename A::size_type newSize, bool preserve) -{ - if (oldSize == newSize) - return p; - - if (preserve) - { - typename A::pointer newPointer = a.allocate(newSize, NULL); - memcpy_s(newPointer, sizeof(T)*newSize, p, sizeof(T)*STDMIN(oldSize, newSize)); - a.deallocate(p, oldSize); - return newPointer; - } - else - { - a.deallocate(p, oldSize); - return a.allocate(newSize, NULL); - } -} - -#if defined(_MSC_VER) && (_MSC_VER < 1300) -#pragma warning(pop) -#endif - -template -class AllocatorWithCleanup : public AllocatorBase -{ -public: - CRYPTOPP_INHERIT_ALLOCATOR_TYPES - - pointer allocate(size_type n, const void * = NULL) - { - CheckSize(n); - if (n == 0) - return NULL; - - if (CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16 && n*sizeof(T) >= 16) - { - byte *p; - #ifdef CRYPTOPP_MM_MALLOC_AVAILABLE - while (!(p = (byte *)_mm_malloc(sizeof(T)*n, 16))) - #elif defined(CRYPTOPP_MEMALIGN_AVAILABLE) - while (!(p = (byte *)memalign(16, sizeof(T)*n))) - #elif defined(CRYPTOPP_MALLOC_ALIGNMENT_IS_16) - while (!(p = (byte *)malloc(sizeof(T)*n))) - #else - while (!(p = (byte *)malloc(sizeof(T)*n + 16))) - #endif - CallNewHandler(); - - #ifdef CRYPTOPP_NO_ALIGNED_ALLOC - size_t adjustment = 16-((size_t)p%16); - p += adjustment; - p[-1] = (byte)adjustment; - #endif - - assert(IsAlignedOn(p, 16)); - return (pointer)p; - } - - pointer p; - while (!(p = (pointer)malloc(sizeof(T)*n))) - CallNewHandler(); - return p; - } - - void deallocate(void *p, size_type n) - { - memset_z(p, 0, n*sizeof(T)); - - if (CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16 && n*sizeof(T) >= 16) - { - #ifdef CRYPTOPP_MM_MALLOC_AVAILABLE - _mm_free(p); - #elif defined(CRYPTOPP_NO_ALIGNED_ALLOC) - p = (byte *)p - ((byte *)p)[-1]; - free(p); - #else - free(p); - #endif - return; - } - - free(p); - } - - pointer reallocate(T *p, size_type oldSize, size_type newSize, bool preserve) - { - return StandardReallocate(*this, p, oldSize, newSize, preserve); - } - - // VS.NET STL enforces the policy of "All STL-compliant allocators have to provide a - // template class member called rebind". - template struct rebind { typedef AllocatorWithCleanup other; }; -#if _MSC_VER >= 1500 - AllocatorWithCleanup() {} - template AllocatorWithCleanup(const AllocatorWithCleanup &) {} -#endif -}; - -CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; -CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; -CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; -CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; -#if CRYPTOPP_BOOL_X86 -CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; // for Integer -#endif - -template -class NullAllocator : public AllocatorBase -{ -public: - CRYPTOPP_INHERIT_ALLOCATOR_TYPES - - pointer allocate(size_type n, const void * = NULL) - { - assert(false); - return NULL; - } - - void deallocate(void *p, size_type n) - { - //// Bitcoin: don't know why this trips, probably a false alarm, depends on the compiler used. - //assert(false); - } - - size_type max_size() const {return 0;} -}; - -// This allocator can't be used with standard collections because -// they require that all objects of the same allocator type are equivalent. -// So this is for use with SecBlock only. -template , bool T_Align16 = false> -class FixedSizeAllocatorWithCleanup : public AllocatorBase -{ -public: - CRYPTOPP_INHERIT_ALLOCATOR_TYPES - - FixedSizeAllocatorWithCleanup() : m_allocated(false) {} - - pointer allocate(size_type n) - { - assert(IsAlignedOn(m_array, 8)); - - if (n <= S && !m_allocated) - { - m_allocated = true; - return GetAlignedArray(); - } - else - return m_fallbackAllocator.allocate(n); - } - - pointer allocate(size_type n, const void *hint) - { - if (n <= S && !m_allocated) - { - m_allocated = true; - return GetAlignedArray(); - } - else - return m_fallbackAllocator.allocate(n, hint); - } - - void deallocate(void *p, size_type n) - { - if (p == GetAlignedArray()) - { - assert(n <= S); - assert(m_allocated); - m_allocated = false; - memset(p, 0, n*sizeof(T)); - } - else - m_fallbackAllocator.deallocate(p, n); - } - - pointer reallocate(pointer p, size_type oldSize, size_type newSize, bool preserve) - { - if (p == GetAlignedArray() && newSize <= S) - { - assert(oldSize <= S); - if (oldSize > newSize) - memset(p + newSize, 0, (oldSize-newSize)*sizeof(T)); - return p; - } - - pointer newPointer = allocate(newSize, NULL); - if (preserve) - memcpy(newPointer, p, sizeof(T)*STDMIN(oldSize, newSize)); - deallocate(p, oldSize); - return newPointer; - } - - size_type max_size() const {return STDMAX(m_fallbackAllocator.max_size(), S);} - -private: -#ifdef __BORLANDC__ - T* GetAlignedArray() {return m_array;} - T m_array[S]; -#else - T* GetAlignedArray() {return (CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16) ? (T*)(((byte *)m_array) + (0-(size_t)m_array)%16) : m_array;} - CRYPTOPP_ALIGN_DATA(8) T m_array[(CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16) ? S+8/sizeof(T) : S]; -#endif - A m_fallbackAllocator; - bool m_allocated; -}; - -//! a block of memory allocated using A -template > -class SecBlock -{ -public: - typedef typename A::value_type value_type; - typedef typename A::pointer iterator; - typedef typename A::const_pointer const_iterator; - typedef typename A::size_type size_type; - - explicit SecBlock(size_type size=0) - : m_size(size) {m_ptr = m_alloc.allocate(size, NULL);} - SecBlock(const SecBlock &t) - : m_size(t.m_size) {m_ptr = m_alloc.allocate(m_size, NULL); memcpy_s(m_ptr, m_size*sizeof(T), t.m_ptr, m_size*sizeof(T));} - SecBlock(const T *t, size_type len) - : m_size(len) - { - m_ptr = m_alloc.allocate(len, NULL); - if (t == NULL) - memset_z(m_ptr, 0, len*sizeof(T)); - else - memcpy(m_ptr, t, len*sizeof(T)); - } - - ~SecBlock() - {m_alloc.deallocate(m_ptr, m_size);} - -#ifdef __BORLANDC__ - operator T *() const - {return (T*)m_ptr;} -#else - operator const void *() const - {return m_ptr;} - operator void *() - {return m_ptr;} - - operator const T *() const - {return m_ptr;} - operator T *() - {return m_ptr;} -#endif - -// T *operator +(size_type offset) -// {return m_ptr+offset;} - -// const T *operator +(size_type offset) const -// {return m_ptr+offset;} - -// T& operator[](size_type index) -// {assert(index >= 0 && index < m_size); return m_ptr[index];} - -// const T& operator[](size_type index) const -// {assert(index >= 0 && index < m_size); return m_ptr[index];} - - iterator begin() - {return m_ptr;} - const_iterator begin() const - {return m_ptr;} - iterator end() - {return m_ptr+m_size;} - const_iterator end() const - {return m_ptr+m_size;} - - typename A::pointer data() {return m_ptr;} - typename A::const_pointer data() const {return m_ptr;} - - size_type size() const {return m_size;} - bool empty() const {return m_size == 0;} - - byte * BytePtr() {return (byte *)m_ptr;} - const byte * BytePtr() const {return (const byte *)m_ptr;} - size_type SizeInBytes() const {return m_size*sizeof(T);} - - //! set contents and size - void Assign(const T *t, size_type len) - { - New(len); - memcpy_s(m_ptr, m_size*sizeof(T), t, len*sizeof(T)); - } - - //! copy contents and size from another SecBlock - void Assign(const SecBlock &t) - { - New(t.m_size); - memcpy_s(m_ptr, m_size*sizeof(T), t.m_ptr, m_size*sizeof(T)); - } - - SecBlock& operator=(const SecBlock &t) - { - Assign(t); - return *this; - } - - // append to this object - SecBlock& operator+=(const SecBlock &t) - { - size_type oldSize = m_size; - Grow(m_size+t.m_size); - memcpy_s(m_ptr+oldSize, m_size*sizeof(T), t.m_ptr, t.m_size*sizeof(T)); - return *this; - } - - // append operator - SecBlock operator+(const SecBlock &t) - { - SecBlock result(m_size+t.m_size); - memcpy_s(result.m_ptr, result.m_size*sizeof(T), m_ptr, m_size*sizeof(T)); - memcpy_s(result.m_ptr+m_size, t.m_size*sizeof(T), t.m_ptr, t.m_size*sizeof(T)); - return result; - } - - bool operator==(const SecBlock &t) const - { - return m_size == t.m_size && VerifyBufsEqual(m_ptr, t.m_ptr, m_size*sizeof(T)); - } - - bool operator!=(const SecBlock &t) const - { - return !operator==(t); - } - - //! change size, without preserving contents - void New(size_type newSize) - { - m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, false); - m_size = newSize; - } - - //! change size and set contents to 0 - void CleanNew(size_type newSize) - { - New(newSize); - memset_z(m_ptr, 0, m_size*sizeof(T)); - } - - //! change size only if newSize > current size. contents are preserved - void Grow(size_type newSize) - { - if (newSize > m_size) - { - m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); - m_size = newSize; - } - } - - //! change size only if newSize > current size. contents are preserved and additional area is set to 0 - void CleanGrow(size_type newSize) - { - if (newSize > m_size) - { - m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); - memset(m_ptr+m_size, 0, (newSize-m_size)*sizeof(T)); - m_size = newSize; - } - } - - //! change size and preserve contents - void resize(size_type newSize) - { - m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); - m_size = newSize; - } - - //! swap contents and size with another SecBlock - void swap(SecBlock &b) - { - std::swap(m_alloc, b.m_alloc); - std::swap(m_size, b.m_size); - std::swap(m_ptr, b.m_ptr); - } - -//private: - A m_alloc; - size_type m_size; - T *m_ptr; -}; - -typedef SecBlock SecByteBlock; -typedef SecBlock > AlignedSecByteBlock; -typedef SecBlock SecWordBlock; - -//! a SecBlock with fixed size, allocated statically -template > -class FixedSizeSecBlock : public SecBlock -{ -public: - explicit FixedSizeSecBlock() : SecBlock(S) {} -}; - -template -class FixedSizeAlignedSecBlock : public FixedSizeSecBlock, T_Align16> > -{ -}; - -//! a SecBlock that preallocates size S statically, and uses the heap when this size is exceeded -template > > -class SecBlockWithHint : public SecBlock -{ -public: - explicit SecBlockWithHint(size_t size) : SecBlock(size) {} -}; - -template -inline bool operator==(const CryptoPP::AllocatorWithCleanup&, const CryptoPP::AllocatorWithCleanup&) {return (true);} -template -inline bool operator!=(const CryptoPP::AllocatorWithCleanup&, const CryptoPP::AllocatorWithCleanup&) {return (false);} - -NAMESPACE_END - -NAMESPACE_BEGIN(std) -template -inline void swap(CryptoPP::SecBlock &a, CryptoPP::SecBlock &b) -{ - a.swap(b); -} - -#if defined(_STLP_DONT_SUPPORT_REBIND_MEMBER_TEMPLATE) || (defined(_STLPORT_VERSION) && !defined(_STLP_MEMBER_TEMPLATE_CLASSES)) -// working for STLport 5.1.3 and MSVC 6 SP5 -template -inline CryptoPP::AllocatorWithCleanup<_Tp2>& -__stl_alloc_rebind(CryptoPP::AllocatorWithCleanup<_Tp1>& __a, const _Tp2*) -{ - return (CryptoPP::AllocatorWithCleanup<_Tp2>&)(__a); -} -#endif - -NAMESPACE_END - -#endif diff --git a/cryptopp/include/cryptopp/sha.h b/cryptopp/include/cryptopp/sha.h deleted file mode 100644 index 8d0dbfcac7..0000000000 --- a/cryptopp/include/cryptopp/sha.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef CRYPTOPP_SHA_H -#define CRYPTOPP_SHA_H - -#include "cryptopp/iterhash.h" - -NAMESPACE_BEGIN(CryptoPP) - -/// SHA-1 -class CRYPTOPP_DLL SHA1 : public IteratedHashWithStaticTransform -{ -public: - static void CRYPTOPP_API InitState(HashWordType *state); - static void CRYPTOPP_API Transform(word32 *digest, const word32 *data); - static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-1";} -}; - -typedef SHA1 SHA; // for backwards compatibility - -//! implements the SHA-256 standard -class CRYPTOPP_DLL SHA256 : public IteratedHashWithStaticTransform -{ -public: -#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) - size_t HashMultipleBlocks(const word32 *input, size_t length); -#endif - static void CRYPTOPP_API InitState(HashWordType *state); - static void CRYPTOPP_API Transform(word32 *digest, const word32 *data); - static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-256";} -}; - -//! implements the SHA-224 standard -class CRYPTOPP_DLL SHA224 : public IteratedHashWithStaticTransform -{ -public: -#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) - size_t HashMultipleBlocks(const word32 *input, size_t length); -#endif - static void CRYPTOPP_API InitState(HashWordType *state); - static void CRYPTOPP_API Transform(word32 *digest, const word32 *data) {SHA256::Transform(digest, data);} - static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-224";} -}; - -//! implements the SHA-512 standard -class CRYPTOPP_DLL SHA512 : public IteratedHashWithStaticTransform -{ -public: - static void CRYPTOPP_API InitState(HashWordType *state); - static void CRYPTOPP_API Transform(word64 *digest, const word64 *data); - static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-512";} -}; - -//! implements the SHA-384 standard -class CRYPTOPP_DLL SHA384 : public IteratedHashWithStaticTransform -{ -public: - static void CRYPTOPP_API InitState(HashWordType *state); - static void CRYPTOPP_API Transform(word64 *digest, const word64 *data) {SHA512::Transform(digest, data);} - static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-384";} -}; - -NAMESPACE_END - -#endif diff --git a/cryptopp/include/cryptopp/simple.h b/cryptopp/include/cryptopp/simple.h deleted file mode 100644 index 8b13789179..0000000000 --- a/cryptopp/include/cryptopp/simple.h +++ /dev/null @@ -1 +0,0 @@ - diff --git a/cryptopp/include/cryptopp/smartptr.h b/cryptopp/include/cryptopp/smartptr.h deleted file mode 100644 index fa0daec3df..0000000000 --- a/cryptopp/include/cryptopp/smartptr.h +++ /dev/null @@ -1,223 +0,0 @@ -#ifndef CRYPTOPP_SMARTPTR_H -#define CRYPTOPP_SMARTPTR_H - -#include "cryptopp/config.h" -#include - -NAMESPACE_BEGIN(CryptoPP) - -template class simple_ptr -{ -public: - simple_ptr() : m_p(NULL) {} - ~simple_ptr() {delete m_p;} - T *m_p; -}; - -template class member_ptr -{ -public: - explicit member_ptr(T *p = NULL) : m_p(p) {} - - ~member_ptr(); - - const T& operator*() const { return *m_p; } - T& operator*() { return *m_p; } - - const T* operator->() const { return m_p; } - T* operator->() { return m_p; } - - const T* get() const { return m_p; } - T* get() { return m_p; } - - T* release() - { - T *old_p = m_p; - m_p = 0; - return old_p; - } - - void reset(T *p = 0); - -protected: - member_ptr(const member_ptr& rhs); // copy not allowed - void operator=(const member_ptr& rhs); // assignment not allowed - - T *m_p; -}; - -template member_ptr::~member_ptr() {delete m_p;} -template void member_ptr::reset(T *p) {delete m_p; m_p = p;} - -// ******************************************************** - -template class value_ptr : public member_ptr -{ -public: - value_ptr(const T &obj) : member_ptr(new T(obj)) {} - value_ptr(T *p = NULL) : member_ptr(p) {} - value_ptr(const value_ptr& rhs) - : member_ptr(rhs.m_p ? new T(*rhs.m_p) : NULL) {} - - value_ptr& operator=(const value_ptr& rhs); - bool operator==(const value_ptr& rhs) - { - return (!this->m_p && !rhs.m_p) || (this->m_p && rhs.m_p && *this->m_p == *rhs.m_p); - } -}; - -template value_ptr& value_ptr::operator=(const value_ptr& rhs) -{ - T *old_p = this->m_p; - this->m_p = rhs.m_p ? new T(*rhs.m_p) : NULL; - delete old_p; - return *this; -} - -// ******************************************************** - -template class clonable_ptr : public member_ptr -{ -public: - clonable_ptr(const T &obj) : member_ptr(obj.Clone()) {} - clonable_ptr(T *p = NULL) : member_ptr(p) {} - clonable_ptr(const clonable_ptr& rhs) - : member_ptr(rhs.m_p ? rhs.m_p->Clone() : NULL) {} - - clonable_ptr& operator=(const clonable_ptr& rhs); -}; - -template clonable_ptr& clonable_ptr::operator=(const clonable_ptr& rhs) -{ - T *old_p = this->m_p; - this->m_p = rhs.m_p ? rhs.m_p->Clone() : NULL; - delete old_p; - return *this; -} - -// ******************************************************** - -template class counted_ptr -{ -public: - explicit counted_ptr(T *p = 0); - counted_ptr(const T &r) : m_p(0) {attach(r);} - counted_ptr(const counted_ptr& rhs); - - ~counted_ptr(); - - const T& operator*() const { return *m_p; } - T& operator*() { return *m_p; } - - const T* operator->() const { return m_p; } - T* operator->() { return get(); } - - const T* get() const { return m_p; } - T* get(); - - void attach(const T &p); - - counted_ptr & operator=(const counted_ptr& rhs); - -private: - T *m_p; -}; - -template counted_ptr::counted_ptr(T *p) - : m_p(p) -{ - if (m_p) - m_p->m_referenceCount = 1; -} - -template counted_ptr::counted_ptr(const counted_ptr& rhs) - : m_p(rhs.m_p) -{ - if (m_p) - m_p->m_referenceCount++; -} - -template counted_ptr::~counted_ptr() -{ - if (m_p && --m_p->m_referenceCount == 0) - delete m_p; -} - -template void counted_ptr::attach(const T &r) -{ - if (m_p && --m_p->m_referenceCount == 0) - delete m_p; - if (r.m_referenceCount == 0) - { - m_p = r.clone(); - m_p->m_referenceCount = 1; - } - else - { - m_p = const_cast(&r); - m_p->m_referenceCount++; - } -} - -template T* counted_ptr::get() -{ - if (m_p && m_p->m_referenceCount > 1) - { - T *temp = m_p->clone(); - m_p->m_referenceCount--; - m_p = temp; - m_p->m_referenceCount = 1; - } - return m_p; -} - -template counted_ptr & counted_ptr::operator=(const counted_ptr& rhs) -{ - if (m_p != rhs.m_p) - { - if (m_p && --m_p->m_referenceCount == 0) - delete m_p; - m_p = rhs.m_p; - if (m_p) - m_p->m_referenceCount++; - } - return *this; -} - -// ******************************************************** - -template class vector_member_ptrs -{ -public: - vector_member_ptrs(size_t size=0) - : m_size(size), m_ptr(new member_ptr[size]) {} - ~vector_member_ptrs() - {delete [] this->m_ptr;} - - member_ptr& operator[](size_t index) - {assert(indexm_size); return this->m_ptr[index];} - const member_ptr& operator[](size_t index) const - {assert(indexm_size); return this->m_ptr[index];} - - size_t size() const {return this->m_size;} - void resize(size_t newSize) - { - member_ptr *newPtr = new member_ptr[newSize]; - for (size_t i=0; im_size && im_ptr[i].release()); - delete [] this->m_ptr; - this->m_size = newSize; - this->m_ptr = newPtr; - } - -private: - vector_member_ptrs(const vector_member_ptrs &c); // copy not allowed - void operator=(const vector_member_ptrs &x); // assignment not allowed - - size_t m_size; - member_ptr *m_ptr; -}; - -NAMESPACE_END - -#endif diff --git a/cryptopp/include/cryptopp/stdcpp.h b/cryptopp/include/cryptopp/stdcpp.h deleted file mode 100644 index 9a468ab61e..0000000000 --- a/cryptopp/include/cryptopp/stdcpp.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef CRYPTOPP_STDCPP_H -#define CRYPTOPP_STDCPP_H - -#include -#include -#include -#include -#include -#include -#include - - -#ifdef _MSC_VER -#include // CodeWarrior doesn't have memory.h -#include -#include -#include - -// re-disable this -#pragma warning(disable: 4231) -#endif - -#if defined(_MSC_VER) && defined(_CRTAPI1) -#define CRYPTOPP_MSVCRT6 -#endif - -#endif diff --git a/cryptopp/src/cpu.cpp b/cryptopp/src/cpu.cpp deleted file mode 100644 index d36d3253ae..0000000000 --- a/cryptopp/src/cpu.cpp +++ /dev/null @@ -1,199 +0,0 @@ -// cpu.cpp - written and placed in the public domain by Wei Dai - -#include "cryptopp/pch.h" - -#ifndef CRYPTOPP_IMPORTS - -#include "cryptopp/cpu.h" -#include "cryptopp/misc.h" -#include - -#ifdef __GNUC__ -#include -#include -#endif - -#ifdef CRYPTOPP_MSVC6PP_OR_LATER -#include -#endif - -NAMESPACE_BEGIN(CryptoPP) - -#ifdef CRYPTOPP_X86_ASM_AVAILABLE - -#ifndef _MSC_VER -typedef void (*SigHandler)(int); - -static jmp_buf s_jmpNoCPUID; -static void SigIllHandlerCPUID(int) -{ - longjmp(s_jmpNoCPUID, 1); -} -#endif - -bool CpuId(word32 input, word32 *output) -{ -#ifdef _MSC_VER - __try - { - __asm - { - mov eax, input - cpuid - mov edi, output - mov [edi], eax - mov [edi+4], ebx - mov [edi+8], ecx - mov [edi+12], edx - } - } - __except (1) - { - return false; - } - return true; -#else - SigHandler oldHandler = signal(SIGILL, SigIllHandlerCPUID); - if (oldHandler == SIG_ERR) - return false; - - bool result = true; - if (setjmp(s_jmpNoCPUID)) - result = false; - else - { - __asm__ - ( - // save ebx in case -fPIC is being used -#if CRYPTOPP_BOOL_X86 - "push %%ebx; cpuid; mov %%ebx, %%edi; pop %%ebx" -#else - "pushq %%rbx; cpuid; mov %%ebx, %%edi; popq %%rbx" -#endif - : "=a" (output[0]), "=D" (output[1]), "=c" (output[2]), "=d" (output[3]) - : "a" (input) - ); - } - - signal(SIGILL, oldHandler); - return result; -#endif -} - -#ifndef _MSC_VER -static jmp_buf s_jmpNoSSE2; -static void SigIllHandlerSSE2(int) -{ - longjmp(s_jmpNoSSE2, 1); -} -#endif - -#elif _MSC_VER >= 1400 && CRYPTOPP_BOOL_X64 - -bool CpuId(word32 input, word32 *output) -{ - __cpuid((int *)output, input); - return true; -} - -#endif - -#ifdef CRYPTOPP_CPUID_AVAILABLE - -static bool TrySSE2() -{ -#if CRYPTOPP_BOOL_X64 - return true; -#elif defined(_MSC_VER) - __try - { -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - AS2(por xmm0, xmm0) // executing SSE2 instruction -#elif CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE - __mm128i x = _mm_setzero_si128(); - return _mm_cvtsi128_si32(x) == 0; -#endif - } - __except (1) - { - return false; - } - return true; -#elif defined(__GNUC__) - SigHandler oldHandler = signal(SIGILL, SigIllHandlerSSE2); - if (oldHandler == SIG_ERR) - return false; - - bool result = true; - if (setjmp(s_jmpNoSSE2)) - result = false; - else - { -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - __asm __volatile ("por %xmm0, %xmm0"); -#elif CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE - __mm128i x = _mm_setzero_si128(); - result = _mm_cvtsi128_si32(x) == 0; -#endif - } - - signal(SIGILL, oldHandler); - return result; -#else - return false; -#endif -} - -bool g_x86DetectionDone = false; -bool g_hasISSE = false, g_hasSSE2 = false, g_hasSSSE3 = false, g_hasMMX = false, g_isP4 = false; -word32 g_cacheLineSize = CRYPTOPP_L1_CACHE_LINE_SIZE; - -void DetectX86Features() -{ - word32 cpuid[4], cpuid1[4]; - if (!CpuId(0, cpuid)) - return; - if (!CpuId(1, cpuid1)) - return; - - g_hasMMX = (cpuid1[3] & (1 << 23)) != 0; - if ((cpuid1[3] & (1 << 26)) != 0) - g_hasSSE2 = TrySSE2(); - g_hasSSSE3 = g_hasSSE2 && (cpuid1[2] & (1<<9)); - - if ((cpuid1[3] & (1 << 25)) != 0) - g_hasISSE = true; - else - { - word32 cpuid2[4]; - CpuId(0x080000000, cpuid2); - if (cpuid2[0] >= 0x080000001) - { - CpuId(0x080000001, cpuid2); - g_hasISSE = (cpuid2[3] & (1 << 22)) != 0; - } - } - - std::swap(cpuid[2], cpuid[3]); - if (memcmp(cpuid+1, "GenuineIntel", 12) == 0) - { - g_isP4 = ((cpuid1[0] >> 8) & 0xf) == 0xf; - g_cacheLineSize = 8 * GETBYTE(cpuid1[1], 1); - } - else if (memcmp(cpuid+1, "AuthenticAMD", 12) == 0) - { - CpuId(0x80000005, cpuid); - g_cacheLineSize = GETBYTE(cpuid[2], 0); - } - - if (!g_cacheLineSize) - g_cacheLineSize = CRYPTOPP_L1_CACHE_LINE_SIZE; - - g_x86DetectionDone = true; -} - -#endif - -NAMESPACE_END - -#endif diff --git a/cryptopp/src/sha.cpp b/cryptopp/src/sha.cpp deleted file mode 100644 index 1ff6c0b45c..0000000000 --- a/cryptopp/src/sha.cpp +++ /dev/null @@ -1,899 +0,0 @@ -// sha.cpp - modified by Wei Dai from Steve Reid's public domain sha1.c - -// Steve Reid implemented SHA-1. Wei Dai implemented SHA-2. -// Both are in the public domain. - -// use "cl /EP /P /DCRYPTOPP_GENERATE_X64_MASM sha.cpp" to generate MASM code - -#include "cryptopp/pch.h" - -#ifndef CRYPTOPP_IMPORTS -#ifndef CRYPTOPP_GENERATE_X64_MASM - -#include "cryptopp/sha.h" -#include "cryptopp/misc.h" -#include "cryptopp/cpu.h" - -NAMESPACE_BEGIN(CryptoPP) - -// start of Steve Reid's code - -#define blk0(i) (W[i] = data[i]) -#define blk1(i) (W[i&15] = rotlFixed(W[(i+13)&15]^W[(i+8)&15]^W[(i+2)&15]^W[i&15],1)) - -void SHA1::InitState(HashWordType *state) -{ - state[0] = 0x67452301L; - state[1] = 0xEFCDAB89L; - state[2] = 0x98BADCFEL; - state[3] = 0x10325476L; - state[4] = 0xC3D2E1F0L; -} - -#define f1(x,y,z) (z^(x&(y^z))) -#define f2(x,y,z) (x^y^z) -#define f3(x,y,z) ((x&y)|(z&(x|y))) -#define f4(x,y,z) (x^y^z) - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) z+=f1(w,x,y)+blk0(i)+0x5A827999+rotlFixed(v,5);w=rotlFixed(w,30); -#define R1(v,w,x,y,z,i) z+=f1(w,x,y)+blk1(i)+0x5A827999+rotlFixed(v,5);w=rotlFixed(w,30); -#define R2(v,w,x,y,z,i) z+=f2(w,x,y)+blk1(i)+0x6ED9EBA1+rotlFixed(v,5);w=rotlFixed(w,30); -#define R3(v,w,x,y,z,i) z+=f3(w,x,y)+blk1(i)+0x8F1BBCDC+rotlFixed(v,5);w=rotlFixed(w,30); -#define R4(v,w,x,y,z,i) z+=f4(w,x,y)+blk1(i)+0xCA62C1D6+rotlFixed(v,5);w=rotlFixed(w,30); - -void SHA1::Transform(word32 *state, const word32 *data) -{ - word32 W[16]; - /* Copy context->state[] to working vars */ - word32 a = state[0]; - word32 b = state[1]; - word32 c = state[2]; - word32 d = state[3]; - word32 e = state[4]; - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; -} - -// end of Steve Reid's code - -// ************************************************************* - -void SHA224::InitState(HashWordType *state) -{ - static const word32 s[8] = {0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4}; - memcpy(state, s, sizeof(s)); -} - -void SHA256::InitState(HashWordType *state) -{ - static const word32 s[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; - memcpy(state, s, sizeof(s)); -} - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE -CRYPTOPP_ALIGN_DATA(16) extern const word32 SHA256_K[64] CRYPTOPP_SECTION_ALIGN16 = { -#else -extern const word32 SHA256_K[64] = { -#endif - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 -}; - -#endif // #ifndef CRYPTOPP_GENERATE_X64_MASM - -#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_GENERATE_X64_MASM) - -#pragma warning(disable: 4731) // frame pointer register 'ebp' modified by inline assembly code - -static void CRYPTOPP_FASTCALL X86_SHA256_HashBlocks(word32 *state, const word32 *data, size_t len -#if defined(_MSC_VER) && (_MSC_VER == 1200) - , ... // VC60 workaround: prevent VC 6 from inlining this function -#endif - ) -{ -#if defined(_MSC_VER) && (_MSC_VER == 1200) - AS2(mov ecx, [state]) - AS2(mov edx, [data]) -#endif - - #define LOCALS_SIZE 8*4 + 16*4 + 4*WORD_SZ - #define H(i) [BASE+ASM_MOD(1024+7-(i),8)*4] - #define G(i) H(i+1) - #define F(i) H(i+2) - #define E(i) H(i+3) - #define D(i) H(i+4) - #define C(i) H(i+5) - #define B(i) H(i+6) - #define A(i) H(i+7) - #define Wt(i) BASE+8*4+ASM_MOD(1024+15-(i),16)*4 - #define Wt_2(i) Wt((i)-2) - #define Wt_15(i) Wt((i)-15) - #define Wt_7(i) Wt((i)-7) - #define K_END [BASE+8*4+16*4+0*WORD_SZ] - #define STATE_SAVE [BASE+8*4+16*4+1*WORD_SZ] - #define DATA_SAVE [BASE+8*4+16*4+2*WORD_SZ] - #define DATA_END [BASE+8*4+16*4+3*WORD_SZ] - #define Kt(i) WORD_REG(si)+(i)*4 -#if CRYPTOPP_BOOL_X86 - #define BASE esp+4 -#elif defined(__GNUC__) - #define BASE r8 -#else - #define BASE rsp -#endif - -#define RA0(i, edx, edi) \ - AS2( add edx, [Kt(i)] )\ - AS2( add edx, [Wt(i)] )\ - AS2( add edx, H(i) )\ - -#define RA1(i, edx, edi) - -#define RB0(i, edx, edi) - -#define RB1(i, edx, edi) \ - AS2( mov AS_REG_7d, [Wt_2(i)] )\ - AS2( mov edi, [Wt_15(i)])\ - AS2( mov ebx, AS_REG_7d )\ - AS2( shr AS_REG_7d, 10 )\ - AS2( ror ebx, 17 )\ - AS2( xor AS_REG_7d, ebx )\ - AS2( ror ebx, 2 )\ - AS2( xor ebx, AS_REG_7d )/* s1(W_t-2) */\ - AS2( add ebx, [Wt_7(i)])\ - AS2( mov AS_REG_7d, edi )\ - AS2( shr AS_REG_7d, 3 )\ - AS2( ror edi, 7 )\ - AS2( add ebx, [Wt(i)])/* s1(W_t-2) + W_t-7 + W_t-16 */\ - AS2( xor AS_REG_7d, edi )\ - AS2( add edx, [Kt(i)])\ - AS2( ror edi, 11 )\ - AS2( add edx, H(i) )\ - AS2( xor AS_REG_7d, edi )/* s0(W_t-15) */\ - AS2( add AS_REG_7d, ebx )/* W_t = s1(W_t-2) + W_t-7 + s0(W_t-15) W_t-16*/\ - AS2( mov [Wt(i)], AS_REG_7d)\ - AS2( add edx, AS_REG_7d )\ - -#define ROUND(i, r, eax, ecx, edi, edx)\ - /* in: edi = E */\ - /* unused: eax, ecx, temp: ebx, AS_REG_7d, out: edx = T1 */\ - AS2( mov edx, F(i) )\ - AS2( xor edx, G(i) )\ - AS2( and edx, edi )\ - AS2( xor edx, G(i) )/* Ch(E,F,G) = (G^(E&(F^G))) */\ - AS2( mov AS_REG_7d, edi )\ - AS2( ror edi, 6 )\ - AS2( ror AS_REG_7d, 25 )\ - RA##r(i, edx, edi )/* H + Wt + Kt + Ch(E,F,G) */\ - AS2( xor AS_REG_7d, edi )\ - AS2( ror edi, 5 )\ - AS2( xor AS_REG_7d, edi )/* S1(E) */\ - AS2( add edx, AS_REG_7d )/* T1 = S1(E) + Ch(E,F,G) + H + Wt + Kt */\ - RB##r(i, edx, edi )/* H + Wt + Kt + Ch(E,F,G) */\ - /* in: ecx = A, eax = B^C, edx = T1 */\ - /* unused: edx, temp: ebx, AS_REG_7d, out: eax = A, ecx = B^C, edx = E */\ - AS2( mov ebx, ecx )\ - AS2( xor ecx, B(i) )/* A^B */\ - AS2( and eax, ecx )\ - AS2( xor eax, B(i) )/* Maj(A,B,C) = B^((A^B)&(B^C) */\ - AS2( mov AS_REG_7d, ebx )\ - AS2( ror ebx, 2 )\ - AS2( add eax, edx )/* T1 + Maj(A,B,C) */\ - AS2( add edx, D(i) )\ - AS2( mov D(i), edx )\ - AS2( ror AS_REG_7d, 22 )\ - AS2( xor AS_REG_7d, ebx )\ - AS2( ror ebx, 11 )\ - AS2( xor AS_REG_7d, ebx )\ - AS2( add eax, AS_REG_7d )/* T1 + S0(A) + Maj(A,B,C) */\ - AS2( mov H(i), eax )\ - -#define SWAP_COPY(i) \ - AS2( mov WORD_REG(bx), [WORD_REG(dx)+i*WORD_SZ])\ - AS1( bswap WORD_REG(bx))\ - AS2( mov [Wt(i*(1+CRYPTOPP_BOOL_X64)+CRYPTOPP_BOOL_X64)], WORD_REG(bx)) - -#if defined(__GNUC__) - #if CRYPTOPP_BOOL_X64 - FixedSizeAlignedSecBlock workspace; - #endif - __asm__ __volatile__ - ( - #if CRYPTOPP_BOOL_X64 - "lea %4, %%r8;" - #endif - ".intel_syntax noprefix;" -#elif defined(CRYPTOPP_GENERATE_X64_MASM) - ALIGN 8 - X86_SHA256_HashBlocks PROC FRAME - rex_push_reg rsi - push_reg rdi - push_reg rbx - push_reg rbp - alloc_stack(LOCALS_SIZE+8) - .endprolog - mov rdi, r8 - lea rsi, [?SHA256_K@CryptoPP@@3QBIB + 48*4] -#endif - -#if CRYPTOPP_BOOL_X86 - #ifndef __GNUC__ - AS2( mov edi, [len]) - AS2( lea WORD_REG(si), [SHA256_K+48*4]) - #endif - #if !defined(_MSC_VER) || (_MSC_VER < 1400) - AS_PUSH_IF86(bx) - #endif - - AS_PUSH_IF86(bp) - AS2( mov ebx, esp) - AS2( and esp, -16) - AS2( sub WORD_REG(sp), LOCALS_SIZE) - AS_PUSH_IF86(bx) -#endif - AS2( mov STATE_SAVE, WORD_REG(cx)) - AS2( mov DATA_SAVE, WORD_REG(dx)) - AS2( add WORD_REG(di), WORD_REG(dx)) - AS2( mov DATA_END, WORD_REG(di)) - AS2( mov K_END, WORD_REG(si)) - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE -#if CRYPTOPP_BOOL_X86 - AS2( test edi, 1) - ASJ( jnz, 2, f) -#endif - AS2( movdqa xmm0, XMMWORD_PTR [WORD_REG(cx)+0*16]) - AS2( movdqa xmm1, XMMWORD_PTR [WORD_REG(cx)+1*16]) -#endif - -#if CRYPTOPP_BOOL_X86 -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - ASJ( jmp, 0, f) -#endif - ASL(2) // non-SSE2 - AS2( mov esi, ecx) - AS2( lea edi, A(0)) - AS2( mov ecx, 8) - AS1( rep movsd) - AS2( mov esi, K_END) - ASJ( jmp, 3, f) -#endif - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - ASL(0) - AS2( movdqa E(0), xmm1) - AS2( movdqa A(0), xmm0) -#endif -#if CRYPTOPP_BOOL_X86 - ASL(3) -#endif - AS2( sub WORD_REG(si), 48*4) - SWAP_COPY(0) SWAP_COPY(1) SWAP_COPY(2) SWAP_COPY(3) - SWAP_COPY(4) SWAP_COPY(5) SWAP_COPY(6) SWAP_COPY(7) -#if CRYPTOPP_BOOL_X86 - SWAP_COPY(8) SWAP_COPY(9) SWAP_COPY(10) SWAP_COPY(11) - SWAP_COPY(12) SWAP_COPY(13) SWAP_COPY(14) SWAP_COPY(15) -#endif - AS2( mov edi, E(0)) // E - AS2( mov eax, B(0)) // B - AS2( xor eax, C(0)) // B^C - AS2( mov ecx, A(0)) // A - - ROUND(0, 0, eax, ecx, edi, edx) - ROUND(1, 0, ecx, eax, edx, edi) - ROUND(2, 0, eax, ecx, edi, edx) - ROUND(3, 0, ecx, eax, edx, edi) - ROUND(4, 0, eax, ecx, edi, edx) - ROUND(5, 0, ecx, eax, edx, edi) - ROUND(6, 0, eax, ecx, edi, edx) - ROUND(7, 0, ecx, eax, edx, edi) - ROUND(8, 0, eax, ecx, edi, edx) - ROUND(9, 0, ecx, eax, edx, edi) - ROUND(10, 0, eax, ecx, edi, edx) - ROUND(11, 0, ecx, eax, edx, edi) - ROUND(12, 0, eax, ecx, edi, edx) - ROUND(13, 0, ecx, eax, edx, edi) - ROUND(14, 0, eax, ecx, edi, edx) - ROUND(15, 0, ecx, eax, edx, edi) - - ASL(1) - AS2(add WORD_REG(si), 4*16) - ROUND(0, 1, eax, ecx, edi, edx) - ROUND(1, 1, ecx, eax, edx, edi) - ROUND(2, 1, eax, ecx, edi, edx) - ROUND(3, 1, ecx, eax, edx, edi) - ROUND(4, 1, eax, ecx, edi, edx) - ROUND(5, 1, ecx, eax, edx, edi) - ROUND(6, 1, eax, ecx, edi, edx) - ROUND(7, 1, ecx, eax, edx, edi) - ROUND(8, 1, eax, ecx, edi, edx) - ROUND(9, 1, ecx, eax, edx, edi) - ROUND(10, 1, eax, ecx, edi, edx) - ROUND(11, 1, ecx, eax, edx, edi) - ROUND(12, 1, eax, ecx, edi, edx) - ROUND(13, 1, ecx, eax, edx, edi) - ROUND(14, 1, eax, ecx, edi, edx) - ROUND(15, 1, ecx, eax, edx, edi) - AS2( cmp WORD_REG(si), K_END) - ASJ( jne, 1, b) - - AS2( mov WORD_REG(dx), DATA_SAVE) - AS2( add WORD_REG(dx), 64) - AS2( mov AS_REG_7, STATE_SAVE) - AS2( mov DATA_SAVE, WORD_REG(dx)) - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE -#if CRYPTOPP_BOOL_X86 - AS2( test DWORD PTR DATA_END, 1) - ASJ( jnz, 4, f) -#endif - AS2( movdqa xmm1, XMMWORD_PTR [AS_REG_7+1*16]) - AS2( movdqa xmm0, XMMWORD_PTR [AS_REG_7+0*16]) - AS2( paddd xmm1, E(0)) - AS2( paddd xmm0, A(0)) - AS2( movdqa [AS_REG_7+1*16], xmm1) - AS2( movdqa [AS_REG_7+0*16], xmm0) - AS2( cmp WORD_REG(dx), DATA_END) - ASJ( jl, 0, b) -#endif - -#if CRYPTOPP_BOOL_X86 -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - ASJ( jmp, 5, f) - ASL(4) // non-SSE2 -#endif - AS2( add [AS_REG_7+0*4], ecx) // A - AS2( add [AS_REG_7+4*4], edi) // E - AS2( mov eax, B(0)) - AS2( mov ebx, C(0)) - AS2( mov ecx, D(0)) - AS2( add [AS_REG_7+1*4], eax) - AS2( add [AS_REG_7+2*4], ebx) - AS2( add [AS_REG_7+3*4], ecx) - AS2( mov eax, F(0)) - AS2( mov ebx, G(0)) - AS2( mov ecx, H(0)) - AS2( add [AS_REG_7+5*4], eax) - AS2( add [AS_REG_7+6*4], ebx) - AS2( add [AS_REG_7+7*4], ecx) - AS2( mov ecx, AS_REG_7d) - AS2( cmp WORD_REG(dx), DATA_END) - ASJ( jl, 2, b) -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - ASL(5) -#endif -#endif - - AS_POP_IF86(sp) - AS_POP_IF86(bp) - #if !defined(_MSC_VER) || (_MSC_VER < 1400) - AS_POP_IF86(bx) - #endif - -#ifdef CRYPTOPP_GENERATE_X64_MASM - add rsp, LOCALS_SIZE+8 - pop rbp - pop rbx - pop rdi - pop rsi - ret - X86_SHA256_HashBlocks ENDP -#endif - -#ifdef __GNUC__ - ".att_syntax prefix;" - : - : "c" (state), "d" (data), "S" (SHA256_K+48), "D" (len) - #if CRYPTOPP_BOOL_X64 - , "m" (workspace[0]) - #endif - : "memory", "cc", "%eax" - #if CRYPTOPP_BOOL_X64 - , "%rbx", "%r8" - #endif - ); -#endif -} - -#endif // #if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_GENERATE_X64_MASM) - -#ifndef CRYPTOPP_GENERATE_X64_MASM - -#ifdef CRYPTOPP_X64_MASM_AVAILABLE -extern "C" { -void CRYPTOPP_FASTCALL X86_SHA256_HashBlocks(word32 *state, const word32 *data, size_t len); -} -#endif - -#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) - -size_t SHA256::HashMultipleBlocks(const word32 *input, size_t length) -{ - X86_SHA256_HashBlocks(m_state, input, (length&(size_t(0)-BLOCKSIZE)) - !HasSSE2()); - return length % BLOCKSIZE; -} - -size_t SHA224::HashMultipleBlocks(const word32 *input, size_t length) -{ - X86_SHA256_HashBlocks(m_state, input, (length&(size_t(0)-BLOCKSIZE)) - !HasSSE2()); - return length % BLOCKSIZE; -} - -#endif - -#define blk2(i) (W[i&15]+=s1(W[(i-2)&15])+W[(i-7)&15]+s0(W[(i-15)&15])) - -#define Ch(x,y,z) (z^(x&(y^z))) -#define Maj(x,y,z) (y^((x^y)&(y^z))) - -#define a(i) T[(0-i)&7] -#define b(i) T[(1-i)&7] -#define c(i) T[(2-i)&7] -#define d(i) T[(3-i)&7] -#define e(i) T[(4-i)&7] -#define f(i) T[(5-i)&7] -#define g(i) T[(6-i)&7] -#define h(i) T[(7-i)&7] - -#define R(i) h(i)+=S1(e(i))+Ch(e(i),f(i),g(i))+SHA256_K[i+j]+(j?blk2(i):blk0(i));\ - d(i)+=h(i);h(i)+=S0(a(i))+Maj(a(i),b(i),c(i)) - -// for SHA256 -#define S0(x) (rotrFixed(x,2)^rotrFixed(x,13)^rotrFixed(x,22)) -#define S1(x) (rotrFixed(x,6)^rotrFixed(x,11)^rotrFixed(x,25)) -#define s0(x) (rotrFixed(x,7)^rotrFixed(x,18)^(x>>3)) -#define s1(x) (rotrFixed(x,17)^rotrFixed(x,19)^(x>>10)) - -void SHA256::Transform(word32 *state, const word32 *data) -{ - word32 W[16]; -#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) - // this byte reverse is a waste of time, but this function is only called by MDC - ByteReverse(W, data, BLOCKSIZE); - X86_SHA256_HashBlocks(state, W, BLOCKSIZE - !HasSSE2()); -#else - word32 T[8]; - /* Copy context->state[] to working vars */ - memcpy(T, state, sizeof(T)); - /* 64 operations, partially loop unrolled */ - for (unsigned int j=0; j<64; j+=16) - { - R( 0); R( 1); R( 2); R( 3); - R( 4); R( 5); R( 6); R( 7); - R( 8); R( 9); R(10); R(11); - R(12); R(13); R(14); R(15); - } - /* Add the working vars back into context.state[] */ - state[0] += a(0); - state[1] += b(0); - state[2] += c(0); - state[3] += d(0); - state[4] += e(0); - state[5] += f(0); - state[6] += g(0); - state[7] += h(0); -#endif -} - -/* -// smaller but slower -void SHA256::Transform(word32 *state, const word32 *data) -{ - word32 T[20]; - word32 W[32]; - unsigned int i = 0, j = 0; - word32 *t = T+8; - - memcpy(t, state, 8*4); - word32 e = t[4], a = t[0]; - - do - { - word32 w = data[j]; - W[j] = w; - w += SHA256_K[j]; - w += t[7]; - w += S1(e); - w += Ch(e, t[5], t[6]); - e = t[3] + w; - t[3] = t[3+8] = e; - w += S0(t[0]); - a = w + Maj(a, t[1], t[2]); - t[-1] = t[7] = a; - --t; - ++j; - if (j%8 == 0) - t += 8; - } while (j<16); - - do - { - i = j&0xf; - word32 w = s1(W[i+16-2]) + s0(W[i+16-15]) + W[i] + W[i+16-7]; - W[i+16] = W[i] = w; - w += SHA256_K[j]; - w += t[7]; - w += S1(e); - w += Ch(e, t[5], t[6]); - e = t[3] + w; - t[3] = t[3+8] = e; - w += S0(t[0]); - a = w + Maj(a, t[1], t[2]); - t[-1] = t[7] = a; - - w = s1(W[(i+1)+16-2]) + s0(W[(i+1)+16-15]) + W[(i+1)] + W[(i+1)+16-7]; - W[(i+1)+16] = W[(i+1)] = w; - w += SHA256_K[j+1]; - w += (t-1)[7]; - w += S1(e); - w += Ch(e, (t-1)[5], (t-1)[6]); - e = (t-1)[3] + w; - (t-1)[3] = (t-1)[3+8] = e; - w += S0((t-1)[0]); - a = w + Maj(a, (t-1)[1], (t-1)[2]); - (t-1)[-1] = (t-1)[7] = a; - - t-=2; - j+=2; - if (j%8 == 0) - t += 8; - } while (j<64); - - state[0] += a; - state[1] += t[1]; - state[2] += t[2]; - state[3] += t[3]; - state[4] += e; - state[5] += t[5]; - state[6] += t[6]; - state[7] += t[7]; -} -*/ - -#undef S0 -#undef S1 -#undef s0 -#undef s1 -#undef R - -// ************************************************************* - -void SHA384::InitState(HashWordType *state) -{ - static const word64 s[8] = { - W64LIT(0xcbbb9d5dc1059ed8), W64LIT(0x629a292a367cd507), - W64LIT(0x9159015a3070dd17), W64LIT(0x152fecd8f70e5939), - W64LIT(0x67332667ffc00b31), W64LIT(0x8eb44a8768581511), - W64LIT(0xdb0c2e0d64f98fa7), W64LIT(0x47b5481dbefa4fa4)}; - memcpy(state, s, sizeof(s)); -} - -void SHA512::InitState(HashWordType *state) -{ - static const word64 s[8] = { - W64LIT(0x6a09e667f3bcc908), W64LIT(0xbb67ae8584caa73b), - W64LIT(0x3c6ef372fe94f82b), W64LIT(0xa54ff53a5f1d36f1), - W64LIT(0x510e527fade682d1), W64LIT(0x9b05688c2b3e6c1f), - W64LIT(0x1f83d9abfb41bd6b), W64LIT(0x5be0cd19137e2179)}; - memcpy(state, s, sizeof(s)); -} - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 -CRYPTOPP_ALIGN_DATA(16) static const word64 SHA512_K[80] CRYPTOPP_SECTION_ALIGN16 = { -#else -static const word64 SHA512_K[80] = { -#endif - W64LIT(0x428a2f98d728ae22), W64LIT(0x7137449123ef65cd), - W64LIT(0xb5c0fbcfec4d3b2f), W64LIT(0xe9b5dba58189dbbc), - W64LIT(0x3956c25bf348b538), W64LIT(0x59f111f1b605d019), - W64LIT(0x923f82a4af194f9b), W64LIT(0xab1c5ed5da6d8118), - W64LIT(0xd807aa98a3030242), W64LIT(0x12835b0145706fbe), - W64LIT(0x243185be4ee4b28c), W64LIT(0x550c7dc3d5ffb4e2), - W64LIT(0x72be5d74f27b896f), W64LIT(0x80deb1fe3b1696b1), - W64LIT(0x9bdc06a725c71235), W64LIT(0xc19bf174cf692694), - W64LIT(0xe49b69c19ef14ad2), W64LIT(0xefbe4786384f25e3), - W64LIT(0x0fc19dc68b8cd5b5), W64LIT(0x240ca1cc77ac9c65), - W64LIT(0x2de92c6f592b0275), W64LIT(0x4a7484aa6ea6e483), - W64LIT(0x5cb0a9dcbd41fbd4), W64LIT(0x76f988da831153b5), - W64LIT(0x983e5152ee66dfab), W64LIT(0xa831c66d2db43210), - W64LIT(0xb00327c898fb213f), W64LIT(0xbf597fc7beef0ee4), - W64LIT(0xc6e00bf33da88fc2), W64LIT(0xd5a79147930aa725), - W64LIT(0x06ca6351e003826f), W64LIT(0x142929670a0e6e70), - W64LIT(0x27b70a8546d22ffc), W64LIT(0x2e1b21385c26c926), - W64LIT(0x4d2c6dfc5ac42aed), W64LIT(0x53380d139d95b3df), - W64LIT(0x650a73548baf63de), W64LIT(0x766a0abb3c77b2a8), - W64LIT(0x81c2c92e47edaee6), W64LIT(0x92722c851482353b), - W64LIT(0xa2bfe8a14cf10364), W64LIT(0xa81a664bbc423001), - W64LIT(0xc24b8b70d0f89791), W64LIT(0xc76c51a30654be30), - W64LIT(0xd192e819d6ef5218), W64LIT(0xd69906245565a910), - W64LIT(0xf40e35855771202a), W64LIT(0x106aa07032bbd1b8), - W64LIT(0x19a4c116b8d2d0c8), W64LIT(0x1e376c085141ab53), - W64LIT(0x2748774cdf8eeb99), W64LIT(0x34b0bcb5e19b48a8), - W64LIT(0x391c0cb3c5c95a63), W64LIT(0x4ed8aa4ae3418acb), - W64LIT(0x5b9cca4f7763e373), W64LIT(0x682e6ff3d6b2b8a3), - W64LIT(0x748f82ee5defb2fc), W64LIT(0x78a5636f43172f60), - W64LIT(0x84c87814a1f0ab72), W64LIT(0x8cc702081a6439ec), - W64LIT(0x90befffa23631e28), W64LIT(0xa4506cebde82bde9), - W64LIT(0xbef9a3f7b2c67915), W64LIT(0xc67178f2e372532b), - W64LIT(0xca273eceea26619c), W64LIT(0xd186b8c721c0c207), - W64LIT(0xeada7dd6cde0eb1e), W64LIT(0xf57d4f7fee6ed178), - W64LIT(0x06f067aa72176fba), W64LIT(0x0a637dc5a2c898a6), - W64LIT(0x113f9804bef90dae), W64LIT(0x1b710b35131c471b), - W64LIT(0x28db77f523047d84), W64LIT(0x32caab7b40c72493), - W64LIT(0x3c9ebe0a15c9bebc), W64LIT(0x431d67c49c100d4c), - W64LIT(0x4cc5d4becb3e42b6), W64LIT(0x597f299cfc657e2a), - W64LIT(0x5fcb6fab3ad6faec), W64LIT(0x6c44198c4a475817) -}; - -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 -// put assembly version in separate function, otherwise MSVC 2005 SP1 doesn't generate correct code for the non-assembly version -CRYPTOPP_NAKED static void CRYPTOPP_FASTCALL SHA512_SSE2_Transform(word64 *state, const word64 *data) -{ -#ifdef __GNUC__ - __asm__ __volatile__ - ( - ".intel_syntax noprefix;" - AS1( push ebx) - AS2( mov ebx, eax) -#else - AS1( push ebx) - AS1( push esi) - AS1( push edi) - AS2( lea ebx, SHA512_K) -#endif - - AS2( mov eax, esp) - AS2( and esp, 0xfffffff0) - AS2( sub esp, 27*16) // 17*16 for expanded data, 20*8 for state - AS1( push eax) - AS2( xor eax, eax) - AS2( lea edi, [esp+4+8*8]) // start at middle of state buffer. will decrement pointer each round to avoid copying - AS2( lea esi, [esp+4+20*8+8]) // 16-byte alignment, then add 8 - - AS2( movdqa xmm0, [ecx+0*16]) - AS2( movdq2q mm4, xmm0) - AS2( movdqa [edi+0*16], xmm0) - AS2( movdqa xmm0, [ecx+1*16]) - AS2( movdqa [edi+1*16], xmm0) - AS2( movdqa xmm0, [ecx+2*16]) - AS2( movdq2q mm5, xmm0) - AS2( movdqa [edi+2*16], xmm0) - AS2( movdqa xmm0, [ecx+3*16]) - AS2( movdqa [edi+3*16], xmm0) - ASJ( jmp, 0, f) - -#define SSE2_S0_S1(r, a, b, c) \ - AS2( movq mm6, r)\ - AS2( psrlq r, a)\ - AS2( movq mm7, r)\ - AS2( psllq mm6, 64-c)\ - AS2( pxor mm7, mm6)\ - AS2( psrlq r, b-a)\ - AS2( pxor mm7, r)\ - AS2( psllq mm6, c-b)\ - AS2( pxor mm7, mm6)\ - AS2( psrlq r, c-b)\ - AS2( pxor r, mm7)\ - AS2( psllq mm6, b-a)\ - AS2( pxor r, mm6) - -#define SSE2_s0(r, a, b, c) \ - AS2( movdqa xmm6, r)\ - AS2( psrlq r, a)\ - AS2( movdqa xmm7, r)\ - AS2( psllq xmm6, 64-c)\ - AS2( pxor xmm7, xmm6)\ - AS2( psrlq r, b-a)\ - AS2( pxor xmm7, r)\ - AS2( psrlq r, c-b)\ - AS2( pxor r, xmm7)\ - AS2( psllq xmm6, c-a)\ - AS2( pxor r, xmm6) - -#define SSE2_s1(r, a, b, c) \ - AS2( movdqa xmm6, r)\ - AS2( psrlq r, a)\ - AS2( movdqa xmm7, r)\ - AS2( psllq xmm6, 64-c)\ - AS2( pxor xmm7, xmm6)\ - AS2( psrlq r, b-a)\ - AS2( pxor xmm7, r)\ - AS2( psllq xmm6, c-b)\ - AS2( pxor xmm7, xmm6)\ - AS2( psrlq r, c-b)\ - AS2( pxor r, xmm7) - - ASL(SHA512_Round) - // k + w is in mm0, a is in mm4, e is in mm5 - AS2( paddq mm0, [edi+7*8]) // h - AS2( movq mm2, [edi+5*8]) // f - AS2( movq mm3, [edi+6*8]) // g - AS2( pxor mm2, mm3) - AS2( pand mm2, mm5) - SSE2_S0_S1(mm5,14,18,41) - AS2( pxor mm2, mm3) - AS2( paddq mm0, mm2) // h += Ch(e,f,g) - AS2( paddq mm5, mm0) // h += S1(e) - AS2( movq mm2, [edi+1*8]) // b - AS2( movq mm1, mm2) - AS2( por mm2, mm4) - AS2( pand mm2, [edi+2*8]) // c - AS2( pand mm1, mm4) - AS2( por mm1, mm2) - AS2( paddq mm1, mm5) // temp = h + Maj(a,b,c) - AS2( paddq mm5, [edi+3*8]) // e = d + h - AS2( movq [edi+3*8], mm5) - AS2( movq [edi+11*8], mm5) - SSE2_S0_S1(mm4,28,34,39) // S0(a) - AS2( paddq mm4, mm1) // a = temp + S0(a) - AS2( movq [edi-8], mm4) - AS2( movq [edi+7*8], mm4) - AS1( ret) - - // first 16 rounds - ASL(0) - AS2( movq mm0, [edx+eax*8]) - AS2( movq [esi+eax*8], mm0) - AS2( movq [esi+eax*8+16*8], mm0) - AS2( paddq mm0, [ebx+eax*8]) - ASC( call, SHA512_Round) - AS1( inc eax) - AS2( sub edi, 8) - AS2( test eax, 7) - ASJ( jnz, 0, b) - AS2( add edi, 8*8) - AS2( cmp eax, 16) - ASJ( jne, 0, b) - - // rest of the rounds - AS2( movdqu xmm0, [esi+(16-2)*8]) - ASL(1) - // data expansion, W[i-2] already in xmm0 - AS2( movdqu xmm3, [esi]) - AS2( paddq xmm3, [esi+(16-7)*8]) - AS2( movdqa xmm2, [esi+(16-15)*8]) - SSE2_s1(xmm0, 6, 19, 61) - AS2( paddq xmm0, xmm3) - SSE2_s0(xmm2, 1, 7, 8) - AS2( paddq xmm0, xmm2) - AS2( movdq2q mm0, xmm0) - AS2( movhlps xmm1, xmm0) - AS2( paddq mm0, [ebx+eax*8]) - AS2( movlps [esi], xmm0) - AS2( movlps [esi+8], xmm1) - AS2( movlps [esi+8*16], xmm0) - AS2( movlps [esi+8*17], xmm1) - // 2 rounds - ASC( call, SHA512_Round) - AS2( sub edi, 8) - AS2( movdq2q mm0, xmm1) - AS2( paddq mm0, [ebx+eax*8+8]) - ASC( call, SHA512_Round) - // update indices and loop - AS2( add esi, 16) - AS2( add eax, 2) - AS2( sub edi, 8) - AS2( test eax, 7) - ASJ( jnz, 1, b) - // do housekeeping every 8 rounds - AS2( mov esi, 0xf) - AS2( and esi, eax) - AS2( lea esi, [esp+4+20*8+8+esi*8]) - AS2( add edi, 8*8) - AS2( cmp eax, 80) - ASJ( jne, 1, b) - -#define SSE2_CombineState(i) \ - AS2( movdqa xmm0, [edi+i*16])\ - AS2( paddq xmm0, [ecx+i*16])\ - AS2( movdqa [ecx+i*16], xmm0) - - SSE2_CombineState(0) - SSE2_CombineState(1) - SSE2_CombineState(2) - SSE2_CombineState(3) - - AS1( pop esp) - AS1( emms) - -#if defined(__GNUC__) - AS1( pop ebx) - ".att_syntax prefix;" - : - : "a" (SHA512_K), "c" (state), "d" (data) - : "%esi", "%edi", "memory", "cc" - ); -#else - AS1( pop edi) - AS1( pop esi) - AS1( pop ebx) - AS1( ret) -#endif -} -#endif // #if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE - -void SHA512::Transform(word64 *state, const word64 *data) -{ -#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 - if (HasSSE2()) - { - SHA512_SSE2_Transform(state, data); - return; - } -#endif - -#define S0(x) (rotrFixed(x,28)^rotrFixed(x,34)^rotrFixed(x,39)) -#define S1(x) (rotrFixed(x,14)^rotrFixed(x,18)^rotrFixed(x,41)) -#define s0(x) (rotrFixed(x,1)^rotrFixed(x,8)^(x>>7)) -#define s1(x) (rotrFixed(x,19)^rotrFixed(x,61)^(x>>6)) - -#define R(i) h(i)+=S1(e(i))+Ch(e(i),f(i),g(i))+SHA512_K[i+j]+(j?blk2(i):blk0(i));\ - d(i)+=h(i);h(i)+=S0(a(i))+Maj(a(i),b(i),c(i)) - - word64 W[16]; - word64 T[8]; - /* Copy context->state[] to working vars */ - memcpy(T, state, sizeof(T)); - /* 80 operations, partially loop unrolled */ - for (unsigned int j=0; j<80; j+=16) - { - R( 0); R( 1); R( 2); R( 3); - R( 4); R( 5); R( 6); R( 7); - R( 8); R( 9); R(10); R(11); - R(12); R(13); R(14); R(15); - } - /* Add the working vars back into context.state[] */ - state[0] += a(0); - state[1] += b(0); - state[2] += c(0); - state[3] += d(0); - state[4] += e(0); - state[5] += f(0); - state[6] += g(0); - state[7] += h(0); -} - -NAMESPACE_END - -#endif // #ifndef CRYPTOPP_GENERATE_X64_MASM -#endif // #ifndef CRYPTOPP_IMPORTS diff --git a/gui/bitcoin.qrc b/gui/bitcoin.qrc deleted file mode 100644 index 80904b341c..0000000000 --- a/gui/bitcoin.qrc +++ /dev/null @@ -1,12 +0,0 @@ - - - res/icons/address-book.png - res/icons/bitcoin.png - res/icons/quit.png - res/icons/send.png - res/icons/toolbar.png - - - res/images/about.png - - diff --git a/gui/forms/aboutdialog.ui b/gui/forms/aboutdialog.ui deleted file mode 100644 index 45915c85bf..0000000000 --- a/gui/forms/aboutdialog.ui +++ /dev/null @@ -1,162 +0,0 @@ - - - AboutDialog - - - - 0 - 0 - 593 - 319 - - - - About Bitcoin - - - - - - - 0 - 0 - - - - - - - :/images/about - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - <b>Bitcoin</b> version - - - - - - - 0.3.666-beta - - - Qt::RichText - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Copyright © 2009-2011 Bitcoin Developers - -This is experimental software. - -Distributed under the MIT/X11 software license, see the accompanying file license.txt or http://www.opensource.org/licenses/mit-license.php. - -This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Ok - - - - - - - - - - - - - buttonBox - accepted() - AboutDialog - accept() - - - 360 - 308 - - - 157 - 274 - - - - - buttonBox - rejected() - AboutDialog - reject() - - - 428 - 308 - - - 286 - 274 - - - - - diff --git a/gui/forms/addressbookdialog.ui b/gui/forms/addressbookdialog.ui deleted file mode 100644 index 2b3c69fb97..0000000000 --- a/gui/forms/addressbookdialog.ui +++ /dev/null @@ -1,196 +0,0 @@ - - - AddressBookDialog - - - - 0 - 0 - 591 - 347 - - - - Address Book - - - - - - 0 - - - - - - - Sending - - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - true - - - false - - - - - - - - - - - Receiving - - - - - - These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. The highlighted address is displayed in the main window. - - - Qt::AutoText - - - true - - - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - true - - - false - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Create a new address - - - &New Address... - - - - - - - Copy the currently selected address to the system clipboard - - - &Copy to Clipboard - - - - - - - Edit the currently selected address - - - &Edit... - - - - - - - Delete the currently selected address from the list - - - &Delete - - - - - - - - 0 - 0 - - - - QDialogButtonBox::Ok - - - - - - - - - - - receiveTableView - doubleClicked(QModelIndex) - editButton - click() - - - 334 - 249 - - - 333 - 326 - - - - - sendTableView - doubleClicked(QModelIndex) - editButton - click() - - - 329 - 261 - - - 332 - 326 - - - - - diff --git a/gui/forms/editaddressdialog.ui b/gui/forms/editaddressdialog.ui deleted file mode 100644 index f0ba28a854..0000000000 --- a/gui/forms/editaddressdialog.ui +++ /dev/null @@ -1,105 +0,0 @@ - - - EditAddressDialog - - - - 0 - 0 - 458 - 113 - - - - Edit Address - - - - - - QFormLayout::AllNonFixedFieldsGrow - - - - - &Label - - - labelEdit - - - - - - - &Address - - - addressEdit - - - - - - - The label associated with this address book entry - - - - - - - The address associated with this address book entry. This can only be modified for sending addresses. - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - EditAddressDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - EditAddressDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/gui/forms/sendcoinsdialog.ui b/gui/forms/sendcoinsdialog.ui deleted file mode 100644 index 595b7f40ff..0000000000 --- a/gui/forms/sendcoinsdialog.ui +++ /dev/null @@ -1,180 +0,0 @@ - - - SendCoinsDialog - - - - 0 - 0 - 736 - 149 - - - - Send Coins - - - - - - - - &Amount: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - payAmount - - - - - - - Pay &To: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - payTo - - - - - - - The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - - - 34 - - - - - - - - 145 - 16777215 - - - - Amount of bitcoins to send (e.g. 0.05) - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Paste address from system clipboard - - - &Paste - - - false - - - - - - - Look up adress in address book - - - Address &Book... - - - false - - - - - - - - 9 - - - - Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Confirm the send action - - - &Send - - - - :/icons/send:/icons/send - - - true - - - - - - - - 0 - 0 - - - - Abort the send action - - - QDialogButtonBox::Cancel - - - - - - - - - - - - diff --git a/gui/forms/transactiondescdialog.ui b/gui/forms/transactiondescdialog.ui deleted file mode 100644 index 2f70a38214..0000000000 --- a/gui/forms/transactiondescdialog.ui +++ /dev/null @@ -1,71 +0,0 @@ - - - TransactionDescDialog - - - - 0 - 0 - 400 - 300 - - - - Transaction details - - - - - - This pane shows a detailed description of the transaction - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - - - - buttonBox - accepted() - TransactionDescDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - TransactionDescDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/gui/include/aboutdialog.h b/gui/include/aboutdialog.h deleted file mode 100644 index 827cc741c3..0000000000 --- a/gui/include/aboutdialog.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef ABOUTDIALOG_H -#define ABOUTDIALOG_H - -#include - -namespace Ui { - class AboutDialog; -} - -class AboutDialog : public QDialog -{ - Q_OBJECT - -public: - explicit AboutDialog(QWidget *parent = 0); - ~AboutDialog(); - -private: - Ui::AboutDialog *ui; - -private slots: - void on_buttonBox_accepted(); -}; - -#endif // ABOUTDIALOG_H diff --git a/gui/include/addressbookdialog.h b/gui/include/addressbookdialog.h deleted file mode 100644 index bf7c2a65a6..0000000000 --- a/gui/include/addressbookdialog.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef ADDRESSBOOKDIALOG_H -#define ADDRESSBOOKDIALOG_H - -#include - -namespace Ui { - class AddressBookDialog; -} -class AddressTableModel; - -QT_BEGIN_NAMESPACE -class QTableView; -QT_END_NAMESPACE - -class AddressBookDialog : public QDialog -{ - Q_OBJECT - -public: - explicit AddressBookDialog(QWidget *parent = 0); - ~AddressBookDialog(); - - enum { - SendingTab = 0, - ReceivingTab = 1 - } Tabs; - - void setModel(AddressTableModel *model); - void setTab(int tab); - const QString &getReturnValue() const { return returnValue; } -private: - Ui::AddressBookDialog *ui; - AddressTableModel *model; - QString returnValue; - - QTableView *getCurrentTable(); - -private slots: - void on_buttonBox_accepted(); - void on_deleteButton_clicked(); - void on_tabWidget_currentChanged(int index); - void on_newAddressButton_clicked(); - void on_editButton_clicked(); - void on_copyToClipboard_clicked(); -}; - -#endif // ADDRESSBOOKDIALOG_H diff --git a/gui/include/addresstablemodel.h b/gui/include/addresstablemodel.h deleted file mode 100644 index 8799414334..0000000000 --- a/gui/include/addresstablemodel.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef ADDRESSTABLEMODEL_H -#define ADDRESSTABLEMODEL_H - -#include -#include - -class AddressTablePriv; - -class AddressTableModel : public QAbstractTableModel -{ - Q_OBJECT -public: - explicit AddressTableModel(QObject *parent = 0); - ~AddressTableModel(); - - enum ColumnIndex { - Label = 0, /* User specified label */ - Address = 1 /* Bitcoin address */ - }; - - enum { - TypeRole = Qt::UserRole - } RoleIndex; - - static const QString Send; /* Send addres */ - static const QString Receive; /* Receive address */ - - /* Overridden methods from QAbstractTableModel */ - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - bool setData(const QModelIndex & index, const QVariant & value, int role); - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QModelIndex index(int row, int column, const QModelIndex & parent) const; - bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex()); - - /* Add an address to the model. - Returns the added address on success, and an empty string otherwise. - */ - QString addRow(const QString &type, const QString &label, const QString &address); - - /* Update address list from core. Invalidates any indices. - */ - void updateList(); -private: - AddressTablePriv *priv; - QStringList columns; -signals: - -public slots: - -}; - -#endif // ADDRESSTABLEMODEL_H diff --git a/gui/include/bitcoinaddressvalidator.h b/gui/include/bitcoinaddressvalidator.h deleted file mode 100644 index 73f6ea1f61..0000000000 --- a/gui/include/bitcoinaddressvalidator.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef BITCOINADDRESSVALIDATOR_H -#define BITCOINADDRESSVALIDATOR_H - -#include - -/* Base48 entry widget validator. - Corrects near-miss characters and refuses characters that are no part of base48. - */ -class BitcoinAddressValidator : public QValidator -{ - Q_OBJECT -public: - explicit BitcoinAddressValidator(QObject *parent = 0); - - State validate(QString &input, int &pos) const; - - static const int MaxAddressLength = 34; -signals: - -public slots: - -}; - -#endif // BITCOINADDRESSVALIDATOR_H diff --git a/gui/include/bitcoingui.h b/gui/include/bitcoingui.h deleted file mode 100644 index 96452ef18b..0000000000 --- a/gui/include/bitcoingui.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef BITCOINGUI_H -#define BITCOINGUI_H - -#include -#include - -class TransactionTableModel; -class ClientModel; - -QT_BEGIN_NAMESPACE -class QLabel; -class QLineEdit; -class QTableView; -class QAbstractItemModel; -class QModelIndex; -QT_END_NAMESPACE - -class BitcoinGUI : public QMainWindow -{ - Q_OBJECT -public: - explicit BitcoinGUI(QWidget *parent = 0); - void setModel(ClientModel *model); - - /* Transaction table tab indices */ - enum { - AllTransactions = 0, - SentReceived = 1, - Sent = 2, - Received = 3 - } TabIndex; - -protected: - void changeEvent(QEvent *e); - void closeEvent(QCloseEvent *event); - -private: - ClientModel *model; - - QLineEdit *address; - QLabel *labelBalance; - QLabel *labelConnections; - QLabel *labelBlocks; - QLabel *labelTransactions; - - QAction *quit; - QAction *sendcoins; - QAction *addressbook; - QAction *about; - QAction *receivingAddresses; - QAction *options; - QAction *openBitcoin; - - QSystemTrayIcon *trayIcon; - QList transactionViews; - - void createActions(); - QWidget *createTabs(); - void createTrayIcon(); - void setTabsModel(QAbstractItemModel *transaction_model); - -public slots: - void setBalance(qint64 balance); - void setAddress(const QString &address); - void setNumConnections(int count); - void setNumBlocks(int count); - void setNumTransactions(int count); - void error(const QString &title, const QString &message); - /* It is currently not possible to pass a return value to another thread through - BlockingQueuedConnection, so use an indirected pointer. - http://bugreports.qt.nokia.com/browse/QTBUG-10440 - */ - void askFee(qint64 nFeeRequired, bool *payFee); - -private slots: - void sendcoinsClicked(); - void addressbookClicked(); - void optionsClicked(); - void receivingAddressesClicked(); - void aboutClicked(); - void newAddressClicked(); - void copyClipboardClicked(); - void trayIconActivated(QSystemTrayIcon::ActivationReason reason); - void transactionDetails(const QModelIndex& idx); - void incomingTransaction(const QModelIndex & parent, int start, int end); -}; - -#endif diff --git a/gui/include/clientmodel.h b/gui/include/clientmodel.h deleted file mode 100644 index 09d1fc921e..0000000000 --- a/gui/include/clientmodel.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef CLIENTMODEL_H -#define CLIENTMODEL_H - -#include - -class OptionsModel; -class AddressTableModel; -class TransactionTableModel; - -class ClientModel : public QObject -{ - Q_OBJECT -public: - explicit ClientModel(QObject *parent = 0); - - enum StatusCode - { - OK, - InvalidAmount, - InvalidAddress, - AmountExceedsBalance, - AmountWithFeeExceedsBalance, - Aborted, - MiscError - }; - - OptionsModel *getOptionsModel(); - AddressTableModel *getAddressTableModel(); - TransactionTableModel *getTransactionTableModel(); - - qint64 getBalance(); - QString getAddress(); - int getNumConnections(); - int getNumBlocks(); - int getNumTransactions(); - - /* Set default address */ - void setAddress(const QString &defaultAddress); - /* Send coins */ - StatusCode sendCoins(const QString &payTo, qint64 payAmount); -private: - OptionsModel *optionsModel; - AddressTableModel *addressTableModel; - TransactionTableModel *transactionTableModel; - -signals: - void balanceChanged(qint64 balance); - void addressChanged(const QString &address); - void numConnectionsChanged(int count); - void numBlocksChanged(int count); - void numTransactionsChanged(int count); - /* Asynchronous error notification */ - void error(const QString &title, const QString &message); - -public slots: - -private slots: - void update(); -}; - -#endif // CLIENTMODEL_H diff --git a/gui/include/editaddressdialog.h b/gui/include/editaddressdialog.h deleted file mode 100644 index 6f396d0457..0000000000 --- a/gui/include/editaddressdialog.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef EDITADDRESSDIALOG_H -#define EDITADDRESSDIALOG_H - -#include - -QT_BEGIN_NAMESPACE -class QDataWidgetMapper; -QT_END_NAMESPACE - -namespace Ui { - class EditAddressDialog; -} -class AddressTableModel; - -class EditAddressDialog : public QDialog -{ - Q_OBJECT - -public: - enum Mode { - NewReceivingAddress, - NewSendingAddress, - EditReceivingAddress, - EditSendingAddress - }; - - explicit EditAddressDialog(Mode mode, QWidget *parent = 0); - ~EditAddressDialog(); - - void setModel(AddressTableModel *model); - void loadRow(int row); - QString saveCurrentRow(); - -private: - Ui::EditAddressDialog *ui; - QDataWidgetMapper *mapper; - Mode mode; - AddressTableModel *model; -}; - -#endif // EDITADDRESSDIALOG_H diff --git a/gui/include/guiconstants.h b/gui/include/guiconstants.h deleted file mode 100644 index cdd1a74d98..0000000000 --- a/gui/include/guiconstants.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef GUICONSTANTS_H -#define GUICONSTANTS_H - -/* milliseconds between model updates */ -static const int MODEL_UPDATE_DELAY = 250; - -/* size of cache */ -static const unsigned int WALLET_CACHE_SIZE = 100; - - -#endif // GUICONSTANTS_H diff --git a/gui/include/guiutil.h b/gui/include/guiutil.h deleted file mode 100644 index 748e29bf37..0000000000 --- a/gui/include/guiutil.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef GUIUTIL_H -#define GUIUTIL_H - -#include - -QT_BEGIN_NAMESPACE -class QFont; -class QLineEdit; -class QWidget; -QT_END_NAMESPACE - -class GUIUtil -{ -public: - static QString DateTimeStr(qint64 nTime); - - /* Render bitcoin addresses in monospace font */ - static QFont bitcoinAddressFont(); - - static void setupAddressWidget(QLineEdit *widget, QWidget *parent); - - static void setupAmountWidget(QLineEdit *widget, QWidget *parent); -}; - -#endif // GUIUTIL_H diff --git a/gui/include/monitoreddatamapper.h b/gui/include/monitoreddatamapper.h deleted file mode 100644 index 4dd2d1a86a..0000000000 --- a/gui/include/monitoreddatamapper.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef MONITOREDDATAMAPPER_H -#define MONITOREDDATAMAPPER_H - -#include - -QT_BEGIN_NAMESPACE -class QWidget; -QT_END_NAMESPACE - -/* Data <-> Widget mapper that watches for changes, - to be able to notify when 'dirty' (for example, to - enable a commit/apply button). - */ -class MonitoredDataMapper : public QDataWidgetMapper -{ - Q_OBJECT -public: - explicit MonitoredDataMapper(QObject *parent=0); - - void addMapping(QWidget *widget, int section); - void addMapping(QWidget *widget, int section, const QByteArray &propertyName); -private: - void addChangeMonitor(QWidget *widget); - -signals: - void viewModified(); - -}; - - - -#endif // MONITOREDDATAMAPPER_H diff --git a/gui/include/optionsdialog.h b/gui/include/optionsdialog.h deleted file mode 100644 index 07e85297d5..0000000000 --- a/gui/include/optionsdialog.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef OPTIONSDIALOG_H -#define OPTIONSDIALOG_H - -#include - -QT_BEGIN_NAMESPACE -class QStackedWidget; -class QListWidget; -class QListWidgetItem; -class QPushButton; -QT_END_NAMESPACE -class OptionsModel; -class MainOptionsPage; -class MonitoredDataMapper; - -class OptionsDialog : public QDialog -{ - Q_OBJECT -public: - explicit OptionsDialog(QWidget *parent=0); - - void setModel(OptionsModel *model); - -signals: - -public slots: - void changePage(QListWidgetItem *current, QListWidgetItem *previous); -private slots: - void okClicked(); - void cancelClicked(); - void applyClicked(); - void enableApply(); - void disableApply(); -private: - QListWidget *contents_widget; - QStackedWidget *pages_widget; - MainOptionsPage *main_options_page; - OptionsModel *model; - MonitoredDataMapper *mapper; - QPushButton *apply_button; - - void setupMainPage(); -}; - -#endif // OPTIONSDIALOG_H diff --git a/gui/include/optionsmodel.h b/gui/include/optionsmodel.h deleted file mode 100644 index 0124e2ab47..0000000000 --- a/gui/include/optionsmodel.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef OPTIONSMODEL_H -#define OPTIONSMODEL_H - -#include - -/* Interface from QT to configuration data structure for bitcoin client. - To QT, the options are presented as a list with the different options - laid out vertically. - This can be changed to a tree once the settings become sufficiently - complex. - */ -class OptionsModel : public QAbstractListModel -{ - Q_OBJECT -public: - explicit OptionsModel(QObject *parent = 0); - - enum OptionID { - StartAtStartup, - MinimizeToTray, - MapPortUPnP, - MinimizeOnClose, - ConnectSOCKS4, - ProxyIP, - ProxyPort, - Fee, - OptionIDRowCount - }; - - int rowCount(const QModelIndex & parent = QModelIndex()) const; - QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; - bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); - - /* Explicit getters */ - qint64 getTransactionFee(); - bool getMinimizeToTray(); - bool getMinimizeOnClose(); -signals: - -public slots: - -}; - -#endif // OPTIONSMODEL_H diff --git a/gui/include/sendcoinsdialog.h b/gui/include/sendcoinsdialog.h deleted file mode 100644 index f73c38d63a..0000000000 --- a/gui/include/sendcoinsdialog.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SENDCOINSDIALOG_H -#define SENDCOINSDIALOG_H - -#include - -namespace Ui { - class SendCoinsDialog; -} -class ClientModel; - -class SendCoinsDialog : public QDialog -{ - Q_OBJECT - -public: - explicit SendCoinsDialog(QWidget *parent = 0, const QString &address = ""); - ~SendCoinsDialog(); - - void setModel(ClientModel *model); - -private: - Ui::SendCoinsDialog *ui; - ClientModel *model; - -private slots: - void on_buttonBox_rejected(); - void on_addressBookButton_clicked(); - void on_pasteButton_clicked(); - void on_sendButton_clicked(); -}; - -#endif // SENDCOINSDIALOG_H diff --git a/gui/include/transactiondesc.h b/gui/include/transactiondesc.h deleted file mode 100644 index 5a85949341..0000000000 --- a/gui/include/transactiondesc.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef TRANSACTIONDESC_H -#define TRANSACTIONDESC_H - -#include - -class CWalletTx; - -class TransactionDesc -{ -public: - /* Provide human-readable extended HTML description of a transaction */ - static std::string toHTML(CWalletTx &wtx); -}; - -#endif // TRANSACTIONDESC_H diff --git a/gui/include/transactiondescdialog.h b/gui/include/transactiondescdialog.h deleted file mode 100644 index 4f8f754b2b..0000000000 --- a/gui/include/transactiondescdialog.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef TRANSACTIONDESCDIALOG_H -#define TRANSACTIONDESCDIALOG_H - -#include - -namespace Ui { - class TransactionDescDialog; -} -QT_BEGIN_NAMESPACE -class QModelIndex; -QT_END_NAMESPACE - -class TransactionDescDialog : public QDialog -{ - Q_OBJECT - -public: - explicit TransactionDescDialog(const QModelIndex &idx, QWidget *parent = 0); - ~TransactionDescDialog(); - -private: - Ui::TransactionDescDialog *ui; -}; - -#endif // TRANSACTIONDESCDIALOG_H diff --git a/gui/include/transactionrecord.h b/gui/include/transactionrecord.h deleted file mode 100644 index c082fffe9c..0000000000 --- a/gui/include/transactionrecord.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef TRANSACTIONRECORD_H -#define TRANSACTIONRECORD_H - -#include "main.h" - -#include - -class TransactionStatus -{ -public: - TransactionStatus(): - confirmed(false), sortKey(""), maturity(Mature), - matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1) - { } - - enum Maturity - { - Immature, - Mature, - MaturesWarning, /* Will likely not mature because no nodes have confirmed */ - NotAccepted - }; - - enum Status { - OpenUntilDate, - OpenUntilBlock, - Offline, - Unconfirmed, - HaveConfirmations - }; - - bool confirmed; - std::string sortKey; - - /* For "Generated" transactions */ - Maturity maturity; - int matures_in; - - /* Reported status */ - Status status; - int64 depth; - int64 open_for; /* Timestamp if status==OpenUntilDate, otherwise number of blocks */ - - /* Current number of blocks (to know whether cached status is still valid. */ - int cur_num_blocks; -}; - -class TransactionRecord -{ -public: - enum Type - { - Other, - Generated, - SendToAddress, - SendToIP, - RecvWithAddress, - RecvFromIP, - SendToSelf - }; - - TransactionRecord(): - hash(), time(0), type(Other), address(""), debit(0), credit(0), idx(0) - { - } - - TransactionRecord(uint256 hash, int64 time): - hash(hash), time(time), type(Other), address(""), debit(0), - credit(0), idx(0) - { - } - - TransactionRecord(uint256 hash, int64 time, - Type type, const std::string &address, - int64 debit, int64 credit): - hash(hash), time(time), type(type), address(address), debit(debit), credit(credit), - idx(0) - { - } - - /* Decompose CWallet transaction to model transaction records. - */ - static bool showTransaction(const CWalletTx &wtx); - static QList decomposeTransaction(const CWalletTx &wtx); - - /* Fixed */ - uint256 hash; - int64 time; - Type type; - std::string address; - int64 debit; - int64 credit; - - /* Subtransaction index, for sort key */ - int idx; - - /* Status: can change with block chain update */ - TransactionStatus status; - - /* Update status from wallet tx. - */ - void updateStatus(const CWalletTx &wtx); - - /* Is a status update needed? - */ - bool statusUpdateNeeded(); -}; - -#endif // TRANSACTIONRECORD_H diff --git a/gui/include/transactiontablemodel.h b/gui/include/transactiontablemodel.h deleted file mode 100644 index 5974c0e7fe..0000000000 --- a/gui/include/transactiontablemodel.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef TRANSACTIONTABLEMODEL_H -#define TRANSACTIONTABLEMODEL_H - -#include -#include - -class TransactionTablePriv; -class TransactionRecord; - -class TransactionTableModel : public QAbstractTableModel -{ - Q_OBJECT -public: - explicit TransactionTableModel(QObject *parent = 0); - ~TransactionTableModel(); - - enum { - Status = 0, - Date = 1, - Description = 2, - Debit = 3, - Credit = 4 - } ColumnIndex; - - enum { - TypeRole = Qt::UserRole, - LongDescriptionRole = Qt::UserRole+1 - } RoleIndex; - - /* TypeRole values */ - static const QString Sent; - static const QString Received; - static const QString Other; - - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; -private: - QStringList columns; - TransactionTablePriv *priv; - - QVariant formatTxStatus(const TransactionRecord *wtx) const; - QVariant formatTxDate(const TransactionRecord *wtx) const; - QVariant formatTxDescription(const TransactionRecord *wtx) const; - QVariant formatTxDebit(const TransactionRecord *wtx) const; - QVariant formatTxCredit(const TransactionRecord *wtx) const; - -private slots: - void update(); - - friend class TransactionTablePriv; -}; - -#endif - diff --git a/gui/res/icons/address-book.png b/gui/res/icons/address-book.png deleted file mode 100644 index abfb3c3a51..0000000000 Binary files a/gui/res/icons/address-book.png and /dev/null differ diff --git a/gui/res/icons/bitcoin.png b/gui/res/icons/bitcoin.png deleted file mode 100644 index 09520aca23..0000000000 Binary files a/gui/res/icons/bitcoin.png and /dev/null differ diff --git a/gui/res/icons/quit.png b/gui/res/icons/quit.png deleted file mode 100644 index fb510fbea6..0000000000 Binary files a/gui/res/icons/quit.png and /dev/null differ diff --git a/gui/res/icons/send.png b/gui/res/icons/send.png deleted file mode 100644 index 0ba5359d7b..0000000000 Binary files a/gui/res/icons/send.png and /dev/null differ diff --git a/gui/res/icons/toolbar.png b/gui/res/icons/toolbar.png deleted file mode 100644 index f2dcb20636..0000000000 Binary files a/gui/res/icons/toolbar.png and /dev/null differ diff --git a/gui/res/images/about.png b/gui/res/images/about.png deleted file mode 100644 index c9ab9511ef..0000000000 Binary files a/gui/res/images/about.png and /dev/null differ diff --git a/gui/src/aboutdialog.cpp b/gui/src/aboutdialog.cpp deleted file mode 100644 index 13347961dd..0000000000 --- a/gui/src/aboutdialog.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "aboutdialog.h" -#include "ui_aboutdialog.h" - -#include "util.h" - -AboutDialog::AboutDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::AboutDialog) -{ - ui->setupUi(this); - ui->versionLabel->setText(QString::fromStdString(FormatFullVersion())); -} - -AboutDialog::~AboutDialog() -{ - delete ui; -} - -void AboutDialog::on_buttonBox_accepted() -{ - close(); -} diff --git a/gui/src/addressbookdialog.cpp b/gui/src/addressbookdialog.cpp deleted file mode 100644 index 90950a6441..0000000000 --- a/gui/src/addressbookdialog.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#include "addressbookdialog.h" -#include "ui_addressbookdialog.h" - -#include "addresstablemodel.h" -#include "editaddressdialog.h" - -#include -#include -#include - -AddressBookDialog::AddressBookDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::AddressBookDialog), - model(0) -{ - ui->setupUi(this); -} - -AddressBookDialog::~AddressBookDialog() -{ - delete ui; -} - -void AddressBookDialog::setModel(AddressTableModel *model) -{ - this->model = model; - /* Refresh list from core */ - model->updateList(); - - /* Receive filter */ - QSortFilterProxyModel *receive_model = new QSortFilterProxyModel(this); - receive_model->setSourceModel(model); - receive_model->setDynamicSortFilter(true); - receive_model->setFilterRole(AddressTableModel::TypeRole); - receive_model->setFilterFixedString(AddressTableModel::Receive); - ui->receiveTableView->setModel(receive_model); - - /* Send filter */ - QSortFilterProxyModel *send_model = new QSortFilterProxyModel(this); - send_model->setSourceModel(model); - send_model->setDynamicSortFilter(true); - send_model->setFilterRole(AddressTableModel::TypeRole); - send_model->setFilterFixedString(AddressTableModel::Send); - ui->sendTableView->setModel(send_model); - - /* Set column widths */ - ui->receiveTableView->horizontalHeader()->resizeSection( - AddressTableModel::Address, 320); - ui->receiveTableView->horizontalHeader()->setResizeMode( - AddressTableModel::Label, QHeaderView::Stretch); - ui->sendTableView->horizontalHeader()->resizeSection( - AddressTableModel::Address, 320); - ui->sendTableView->horizontalHeader()->setResizeMode( - AddressTableModel::Label, QHeaderView::Stretch); -} - -void AddressBookDialog::setTab(int tab) -{ - ui->tabWidget->setCurrentIndex(tab); -} - -QTableView *AddressBookDialog::getCurrentTable() -{ - switch(ui->tabWidget->currentIndex()) - { - case SendingTab: - return ui->sendTableView; - case ReceivingTab: - return ui->receiveTableView; - default: - return 0; - } -} - -void AddressBookDialog::on_copyToClipboard_clicked() -{ - /* Copy currently selected address to clipboard - (or nothing, if nothing selected) - */ - QTableView *table = getCurrentTable(); - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) - { - QVariant address = index.data(); - QApplication::clipboard()->setText(address.toString()); - } -} - -void AddressBookDialog::on_editButton_clicked() -{ - QModelIndexList indexes = getCurrentTable()->selectionModel()->selectedRows(); - if(indexes.isEmpty()) - { - return; - } - /* Map selected index to source address book model */ - QAbstractProxyModel *proxy_model = static_cast(getCurrentTable()->model()); - QModelIndex selected = proxy_model->mapToSource(indexes.at(0)); - - /* Double click also triggers edit button */ - EditAddressDialog dlg( - ui->tabWidget->currentIndex() == SendingTab ? - EditAddressDialog::EditSendingAddress : - EditAddressDialog::EditReceivingAddress); - dlg.setModel(model); - dlg.loadRow(selected.row()); - if(dlg.exec()) - { - dlg.saveCurrentRow(); - } -} - -void AddressBookDialog::on_newAddressButton_clicked() -{ - EditAddressDialog dlg( - ui->tabWidget->currentIndex() == SendingTab ? - EditAddressDialog::NewSendingAddress : - EditAddressDialog::NewReceivingAddress); - dlg.setModel(model); - if(dlg.exec()) - { - dlg.saveCurrentRow(); - } -} - -void AddressBookDialog::on_tabWidget_currentChanged(int index) -{ - switch(index) - { - case SendingTab: - ui->deleteButton->setEnabled(true); - break; - case ReceivingTab: - ui->deleteButton->setEnabled(false); - break; - } -} - -void AddressBookDialog::on_deleteButton_clicked() -{ - QTableView *table = getCurrentTable(); - QModelIndexList indexes = table->selectionModel()->selectedRows(); - if(!indexes.isEmpty()) - { - table->model()->removeRow(indexes.at(0).row()); - } -} - -void AddressBookDialog::on_buttonBox_accepted() -{ - QTableView *table = getCurrentTable(); - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) - { - QVariant address = table->model()->data(index); - returnValue = address.toString(); - } - if(!returnValue.isEmpty()) - { - accept(); - } - else - { - reject(); - } -} diff --git a/gui/src/addresstablemodel.cpp b/gui/src/addresstablemodel.cpp deleted file mode 100644 index 91b87fb7f1..0000000000 --- a/gui/src/addresstablemodel.cpp +++ /dev/null @@ -1,245 +0,0 @@ -#include "addresstablemodel.h" -#include "guiutil.h" -#include "main.h" - -#include - -const QString AddressTableModel::Send = "S"; -const QString AddressTableModel::Receive = "R"; - -struct AddressTableEntry -{ - enum Type { - Sending, - Receiving - }; - - Type type; - QString label; - QString address; - - AddressTableEntry() {} - AddressTableEntry(Type type, const QString &label, const QString &address): - type(type), label(label), address(address) {} -}; - -/* Private implementation */ -struct AddressTablePriv -{ - QList cachedAddressTable; - - void refreshAddressTable() - { - cachedAddressTable.clear(); - - CRITICAL_BLOCK(cs_mapKeys) - CRITICAL_BLOCK(cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item, mapAddressBook) - { - std::string strAddress = item.first; - std::string strName = item.second; - uint160 hash160; - bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160)); - cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending, - QString::fromStdString(strName), - QString::fromStdString(strAddress))); - } - } - } - - int size() - { - return cachedAddressTable.size(); - } - - AddressTableEntry *index(int idx) - { - if(idx >= 0 && idx < cachedAddressTable.size()) - { - return &cachedAddressTable[idx]; - } - else - { - return 0; - } - } -}; - -AddressTableModel::AddressTableModel(QObject *parent) : - QAbstractTableModel(parent),priv(0) -{ - columns << tr("Label") << tr("Address"); - priv = new AddressTablePriv(); - priv->refreshAddressTable(); -} - -AddressTableModel::~AddressTableModel() -{ - delete priv; -} - -int AddressTableModel::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return priv->size(); -} - -int AddressTableModel::columnCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return columns.length(); -} - -QVariant AddressTableModel::data(const QModelIndex &index, int role) const -{ - if(!index.isValid()) - return QVariant(); - - AddressTableEntry *rec = static_cast(index.internalPointer()); - - if(role == Qt::DisplayRole || role == Qt::EditRole) - { - switch(index.column()) - { - case Label: - return rec->label; - case Address: - return rec->address; - } - } - else if (role == Qt::FontRole) - { - if(index.column() == Address) - { - return GUIUtil::bitcoinAddressFont(); - } - } - else if (role == TypeRole) - { - switch(rec->type) - { - case AddressTableEntry::Sending: - return Send; - case AddressTableEntry::Receiving: - return Receive; - default: break; - } - } - return QVariant(); -} - -bool AddressTableModel::setData(const QModelIndex & index, const QVariant & value, int role) -{ - if(!index.isValid()) - return false; - AddressTableEntry *rec = static_cast(index.internalPointer()); - - if(role == Qt::EditRole) - { - switch(index.column()) - { - case Label: - SetAddressBookName(rec->address.toStdString(), value.toString().toStdString()); - rec->label = value.toString(); - break; - case Address: - /* Double-check that we're not overwriting receiving address */ - if(rec->type == AddressTableEntry::Sending) - { - /* Remove old entry */ - CWalletDB().EraseName(rec->address.toStdString()); - /* Add new entry with new address */ - SetAddressBookName(value.toString().toStdString(), rec->label.toStdString()); - - rec->address = value.toString(); - } - break; - } - emit dataChanged(index, index); - - return true; - } - return false; -} - -QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if(orientation == Qt::Horizontal) - { - if(role == Qt::DisplayRole) - { - return columns[section]; - } - } - return QVariant(); -} - -QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & parent) const -{ - Q_UNUSED(parent); - AddressTableEntry *data = priv->index(row); - if(data) - { - return createIndex(row, column, priv->index(row)); - } - else - { - return QModelIndex(); - } -} - -void AddressTableModel::updateList() -{ - /* Update internal model from Bitcoin core */ - beginResetModel(); - priv->refreshAddressTable(); - endResetModel(); -} - -QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address) -{ - std::string strLabel = label.toStdString(); - std::string strAddress = address.toStdString(); - - if(type == Send) - { - /* Check for duplicate */ - CRITICAL_BLOCK(cs_mapAddressBook) - { - if(mapAddressBook.count(strAddress)) - { - return QString(); - } - } - } - else if(type == Receive) - { - /* Generate a new address to associate with given label */ - strAddress = PubKeyToAddress(GetKeyFromKeyPool()); - } - else - { - return QString(); - } - /* Add entry and update list */ - SetAddressBookName(strAddress, strLabel); - updateList(); - return QString::fromStdString(strAddress); -} - -bool AddressTableModel::removeRows(int row, int count, const QModelIndex & parent) -{ - Q_UNUSED(parent); - AddressTableEntry *rec = priv->index(row); - if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving) - { - /* Can only remove one row at a time, and cannot remove rows not in model. - Also refuse to remove receiving addresses. - */ - return false; - } - CWalletDB().EraseName(rec->address.toStdString()); - updateList(); - return true; -} diff --git a/gui/src/bitcoin.cpp b/gui/src/bitcoin.cpp deleted file mode 100644 index e003267426..0000000000 --- a/gui/src/bitcoin.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * W.J. van der Laan 2011 - */ -#include "bitcoingui.h" -#include "clientmodel.h" -#include "util.h" -#include "init.h" -#include "main.h" -#include "externui.h" - -#include -#include -#include - -// Need a global reference for the notifications to find the GUI -BitcoinGUI *guiref; - -int MyMessageBox(const std::string& message, const std::string& caption, int style, wxWindow* parent, int x, int y) -{ - // Message from main thread - if(guiref) - { - guiref->error(QString::fromStdString(caption), - QString::fromStdString(message)); - } - else - { - QMessageBox::critical(0, QString::fromStdString(caption), - QString::fromStdString(message), - QMessageBox::Ok, QMessageBox::Ok); - } - return 4; -} - -int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style, wxWindow* parent, int x, int y) -{ - // Message from network thread - if(guiref) - { - QMetaObject::invokeMethod(guiref, "error", Qt::QueuedConnection, - Q_ARG(QString, QString::fromStdString(caption)), - Q_ARG(QString, QString::fromStdString(message))); - } - else - { - printf("%s: %s\n", caption.c_str(), message.c_str()); - fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); - } - return 4; -} - -bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent) -{ - if(!guiref) - return false; - if(nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon) - return true; - bool payFee = false; - - /* Call slot on GUI thread. - If called from another thread, use a blocking QueuedConnection. - */ - Qt::ConnectionType connectionType = Qt::DirectConnection; - if(QThread::currentThread() != QCoreApplication::instance()->thread()) - { - connectionType = Qt::BlockingQueuedConnection; - } - - QMetaObject::invokeMethod(guiref, "askFee", connectionType, - Q_ARG(qint64, nFeeRequired), - Q_ARG(bool*, &payFee)); - - return payFee; -} - -void CalledSetStatusBar(const std::string& strText, int nField) -{ - // Only used for built-in mining, which is disabled, simple ignore -} - -void UIThreadCall(boost::function0 fn) -{ - // Only used for built-in mining, which is disabled, simple ignore -} - -void MainFrameRepaint() -{ -} - -int main(int argc, char *argv[]) -{ - QApplication app(argc, argv); - app.setQuitOnLastWindowClosed(false); - - try - { - if(AppInit2(argc, argv)) - { - BitcoinGUI window; - ClientModel model; - guiref = &window; - window.setModel(&model); - - window.show(); - - int retval = app.exec(); - - guiref = 0; - Shutdown(NULL); - - return retval; - } - else - { - return 1; - } - } catch (std::exception& e) { - PrintException(&e, "Runaway exception"); - } catch (...) { - PrintException(NULL, "Runaway exception"); - } -} diff --git a/gui/src/bitcoinaddressvalidator.cpp b/gui/src/bitcoinaddressvalidator.cpp deleted file mode 100644 index 761a266933..0000000000 --- a/gui/src/bitcoinaddressvalidator.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "bitcoinaddressvalidator.h" - -#include - -/* Base58 characters are: - "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" - - This is: - - All numbers except for '0' - - All uppercase letters except for 'I' and 'O' - - All lowercase letters except for 'l' - - User friendly Base58 input can map - - 'l' and 'I' to '1' - - '0' and 'O' to 'o' -*/ - -BitcoinAddressValidator::BitcoinAddressValidator(QObject *parent) : - QValidator(parent) -{ -} - -QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) const -{ - /* Correction */ - for(int idx=0; idx= '0' && ch<='9') || - (ch >= 'a' && ch<='z') || - (ch >= 'A' && ch<='Z')) && - ch != 'l' && ch != 'I' && ch != '0' && ch != 'O') - { - /* Alphanumeric and not a 'forbidden' character */ - } - else - { - state = QValidator::Invalid; - } - } - - return state; -} diff --git a/gui/src/bitcoingui.cpp b/gui/src/bitcoingui.cpp deleted file mode 100644 index c08476cec3..0000000000 --- a/gui/src/bitcoingui.cpp +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Qt4 bitcoin GUI. - * - * W.J. van der Laan 2011 - */ -#include "bitcoingui.h" -#include "transactiontablemodel.h" -#include "addressbookdialog.h" -#include "sendcoinsdialog.h" -#include "optionsdialog.h" -#include "aboutdialog.h" -#include "clientmodel.h" -#include "guiutil.h" -#include "editaddressdialog.h" -#include "optionsmodel.h" -#include "transactiondescdialog.h" - -#include "main.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -BitcoinGUI::BitcoinGUI(QWidget *parent): - QMainWindow(parent), trayIcon(0) -{ - resize(850, 550); - setWindowTitle(tr("Bitcoin")); - setWindowIcon(QIcon(":icons/bitcoin")); - - createActions(); - - // Menus - QMenu *file = menuBar()->addMenu("&File"); - file->addAction(sendcoins); - file->addSeparator(); - file->addAction(quit); - - QMenu *settings = menuBar()->addMenu("&Settings"); - settings->addAction(receivingAddresses); - settings->addAction(options); - - QMenu *help = menuBar()->addMenu("&Help"); - help->addAction(about); - - // Toolbar - QToolBar *toolbar = addToolBar("Main toolbar"); - toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - toolbar->addAction(sendcoins); - toolbar->addAction(addressbook); - - // Address:

: New... : Paste to clipboard - QHBoxLayout *hbox_address = new QHBoxLayout(); - hbox_address->addWidget(new QLabel(tr("Your Bitcoin Address:"))); - address = new QLineEdit(); - address->setReadOnly(true); - address->setFont(GUIUtil::bitcoinAddressFont()); - address->setToolTip(tr("Your current default receiving address")); - hbox_address->addWidget(address); - - QPushButton *button_new = new QPushButton(tr("&New...")); - button_new->setToolTip(tr("Create new receiving address")); - QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); - button_clipboard->setToolTip(tr("Copy current receiving address to the system clipboard")); - hbox_address->addWidget(button_new); - hbox_address->addWidget(button_clipboard); - - // Balance: - QHBoxLayout *hbox_balance = new QHBoxLayout(); - hbox_balance->addWidget(new QLabel(tr("Balance:"))); - hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ - - labelBalance = new QLabel(); - labelBalance->setFont(QFont("Monospace")); - labelBalance->setToolTip(tr("Your current balance")); - hbox_balance->addWidget(labelBalance); - hbox_balance->addStretch(1); - - QVBoxLayout *vbox = new QVBoxLayout(); - vbox->addLayout(hbox_address); - vbox->addLayout(hbox_balance); - - vbox->addWidget(createTabs()); - - QWidget *centralwidget = new QWidget(this); - centralwidget->setLayout(vbox); - setCentralWidget(centralwidget); - - // Create status bar - statusBar(); - - labelConnections = new QLabel(); - labelConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelConnections->setMinimumWidth(130); - labelConnections->setToolTip(tr("Number of connections to other clients")); - - labelBlocks = new QLabel(); - labelBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelBlocks->setMinimumWidth(130); - labelBlocks->setToolTip(tr("Number of blocks in the block chain")); - - labelTransactions = new QLabel(); - labelTransactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelTransactions->setMinimumWidth(130); - labelTransactions->setToolTip(tr("Number of transactions in your wallet")); - - statusBar()->addPermanentWidget(labelConnections); - statusBar()->addPermanentWidget(labelBlocks); - statusBar()->addPermanentWidget(labelTransactions); - - // Action bindings - connect(button_new, SIGNAL(clicked()), this, SLOT(newAddressClicked())); - connect(button_clipboard, SIGNAL(clicked()), this, SLOT(copyClipboardClicked())); - - createTrayIcon(); -} - -void BitcoinGUI::createActions() -{ - quit = new QAction(QIcon(":/icons/quit"), tr("&Exit"), this); - quit->setToolTip(tr("Quit application")); - sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); - sendcoins->setToolTip(tr("Send coins to a bitcoin address")); - addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); - addressbook->setToolTip(tr("Edit the list of stored addresses and labels")); - about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); - about->setToolTip(tr("Show information about Bitcoin")); - receivingAddresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); - receivingAddresses->setToolTip(tr("Show the list of receiving addresses and edit their labels")); - options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); - options->setToolTip(tr("Modify configuration options for bitcoin")); - openBitcoin = new QAction(QIcon(":/icons/bitcoin"), "Open &Bitcoin", this); - openBitcoin->setToolTip(tr("Show the Bitcoin window")); - - connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); - connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked())); - connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked())); - connect(receivingAddresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked())); - connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); - connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); - connect(openBitcoin, SIGNAL(triggered()), this, SLOT(show())); -} - -void BitcoinGUI::setModel(ClientModel *model) -{ - this->model = model; - - // Keep up to date with client - setBalance(model->getBalance()); - connect(model, SIGNAL(balanceChanged(qint64)), this, SLOT(setBalance(qint64))); - - setNumConnections(model->getNumConnections()); - connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); - - setNumTransactions(model->getNumTransactions()); - connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); - - setNumBlocks(model->getNumBlocks()); - connect(model, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int))); - - setAddress(model->getAddress()); - connect(model, SIGNAL(addressChanged(QString)), this, SLOT(setAddress(QString))); - - // Report errors from network/worker thread - connect(model, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); - - // Put transaction list in tabs - setTabsModel(model->getTransactionTableModel()); -} - -void BitcoinGUI::createTrayIcon() -{ - QMenu *trayIconMenu = new QMenu(this); - trayIconMenu->addAction(openBitcoin); - trayIconMenu->addAction(sendcoins); - trayIconMenu->addAction(options); - trayIconMenu->addSeparator(); - trayIconMenu->addAction(quit); - - trayIcon = new QSystemTrayIcon(this); - trayIcon->setContextMenu(trayIconMenu); - trayIcon->setIcon(QIcon(":/icons/toolbar")); - connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), - this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); - trayIcon->show(); -} - -void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) -{ - if(reason == QSystemTrayIcon::DoubleClick) - { - // Doubleclick on system tray icon triggers "open bitcoin" - openBitcoin->trigger(); - } -} - -QWidget *BitcoinGUI::createTabs() -{ - QStringList tab_labels; - tab_labels << tr("All transactions") - << tr("Sent/Received") - << tr("Sent") - << tr("Received"); - - QTabWidget *tabs = new QTabWidget(this); - for(int i = 0; i < tab_labels.size(); ++i) - { - QTableView *view = new QTableView(this); - tabs->addTab(view, tab_labels.at(i)); - - connect(view, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(transactionDetails(const QModelIndex&))); - transactionViews.append(view); - } - - return tabs; -} - -void BitcoinGUI::setTabsModel(QAbstractItemModel *transaction_model) -{ - QStringList tab_filters; - tab_filters << "^." - << "^["+TransactionTableModel::Sent+TransactionTableModel::Received+"]" - << "^["+TransactionTableModel::Sent+"]" - << "^["+TransactionTableModel::Received+"]"; - - for(int i = 0; i < transactionViews.size(); ++i) - { - QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this); - proxy_model->setSourceModel(transaction_model); - proxy_model->setDynamicSortFilter(true); - proxy_model->setFilterRole(TransactionTableModel::TypeRole); - proxy_model->setFilterRegExp(QRegExp(tab_filters.at(i))); - proxy_model->setSortRole(Qt::EditRole); - - QTableView *transaction_table = transactionViews.at(i); - transaction_table->setModel(proxy_model); - transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); - transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); - transaction_table->setSortingEnabled(true); - transaction_table->sortByColumn(TransactionTableModel::Status, Qt::DescendingOrder); - transaction_table->verticalHeader()->hide(); - - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Status, 120); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Date, 120); - transaction_table->horizontalHeader()->setResizeMode( - TransactionTableModel::Description, QHeaderView::Stretch); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Debit, 79); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Credit, 79); - } - - connect(transaction_model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), - this, SLOT(incomingTransaction(const QModelIndex &, int, int))); -} - -void BitcoinGUI::sendcoinsClicked() -{ - SendCoinsDialog dlg; - dlg.setModel(model); - dlg.exec(); -} - -void BitcoinGUI::addressbookClicked() -{ - AddressBookDialog dlg; - dlg.setModel(model->getAddressTableModel()); - dlg.setTab(AddressBookDialog::SendingTab); - dlg.exec(); -} - -void BitcoinGUI::receivingAddressesClicked() -{ - AddressBookDialog dlg; - dlg.setModel(model->getAddressTableModel()); - dlg.setTab(AddressBookDialog::ReceivingTab); - dlg.exec(); -} - -void BitcoinGUI::optionsClicked() -{ - OptionsDialog dlg; - dlg.setModel(model->getOptionsModel()); - dlg.exec(); -} - -void BitcoinGUI::aboutClicked() -{ - AboutDialog dlg; - dlg.exec(); -} - -void BitcoinGUI::newAddressClicked() -{ - EditAddressDialog dlg(EditAddressDialog::NewReceivingAddress); - dlg.setModel(model->getAddressTableModel()); - if(dlg.exec()) - { - QString newAddress = dlg.saveCurrentRow(); - // Set returned address as new default addres - if(!newAddress.isEmpty()) - { - model->setAddress(newAddress); - } - } -} - -void BitcoinGUI::copyClipboardClicked() -{ - // Copy text in address to clipboard - QApplication::clipboard()->setText(address->text()); -} - -void BitcoinGUI::setBalance(qint64 balance) -{ - labelBalance->setText(QString::fromStdString(FormatMoney(balance))); -} - -void BitcoinGUI::setAddress(const QString &addr) -{ - address->setText(addr); -} - -void BitcoinGUI::setNumConnections(int count) -{ - labelConnections->setText(QLocale::system().toString(count)+" "+tr("connections(s)", "", count)); -} - -void BitcoinGUI::setNumBlocks(int count) -{ - labelBlocks->setText(QLocale::system().toString(count)+" "+tr("block(s)", "", count)); -} - -void BitcoinGUI::setNumTransactions(int count) -{ - labelTransactions->setText(QLocale::system().toString(count)+" "+tr("transaction(s)", "", count)); -} - -void BitcoinGUI::error(const QString &title, const QString &message) -{ - // Report errors from network/worker thread - if(trayIcon->supportsMessages()) - { - // Show as "balloon" message if possible - trayIcon->showMessage(title, message, QSystemTrayIcon::Critical); - } - else - { - // Fall back to old fashioned popup dialog if not - QMessageBox::critical(this, title, - message, - QMessageBox::Ok, QMessageBox::Ok); - } -} - -void BitcoinGUI::changeEvent(QEvent *e) -{ - if (e->type() == QEvent::WindowStateChange) - { - if(model->getOptionsModel()->getMinimizeToTray()) - { - if (isMinimized()) - { - hide(); - e->ignore(); - } - else - { - e->accept(); - } - } - } - QMainWindow::changeEvent(e); -} - -void BitcoinGUI::closeEvent(QCloseEvent *event) -{ - if(!model->getOptionsModel()->getMinimizeToTray() && - !model->getOptionsModel()->getMinimizeOnClose()) - { - qApp->quit(); - } - QMainWindow::closeEvent(event); -} - -void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) -{ - QString strMessage = - tr("This transaction is over the size limit. You can still send it for a fee of %1, " - "which goes to the nodes that process your transaction and helps to support the network. " - "Do you want to pay the fee?").arg(QString::fromStdString(FormatMoney(nFeeRequired))); - QMessageBox::StandardButton retval = QMessageBox::question( - this, tr("Sending..."), strMessage, - QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); - *payFee = (retval == QMessageBox::Yes); -} - -void BitcoinGUI::transactionDetails(const QModelIndex& idx) -{ - /* A transaction is doubleclicked */ - TransactionDescDialog dlg(idx); - dlg.exec(); -} - -void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) -{ - TransactionTableModel *ttm = model->getTransactionTableModel(); - qint64 credit = ttm->index(start, TransactionTableModel::Credit, parent) - .data(Qt::EditRole).toULongLong(); - qint64 debit = ttm->index(start, TransactionTableModel::Debit, parent) - .data(Qt::EditRole).toULongLong(); - if((credit+debit)>0) - { - /* On incoming transaction, make an info balloon */ - QString date = ttm->index(start, TransactionTableModel::Date, parent) - .data().toString(); - QString description = ttm->index(start, TransactionTableModel::Description, parent) - .data().toString(); - - trayIcon->showMessage(tr("Incoming transaction"), - "Date: " + date + "\n" + - "Amount: " + QString::fromStdString(FormatMoney(credit+debit, true)) + "\n" + - description, - QSystemTrayIcon::Information); - } -} diff --git a/gui/src/clientmodel.cpp b/gui/src/clientmodel.cpp deleted file mode 100644 index 97391e0938..0000000000 --- a/gui/src/clientmodel.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include "clientmodel.h" -#include "main.h" -#include "guiconstants.h" -#include "optionsmodel.h" -#include "addresstablemodel.h" -#include "transactiontablemodel.h" - -#include - -ClientModel::ClientModel(QObject *parent) : - QObject(parent), optionsModel(0), addressTableModel(0), - transactionTableModel(0) -{ - /* Until signal notifications is built into the bitcoin core, - simply update everything after polling using a timer. - */ - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(update())); - timer->start(MODEL_UPDATE_DELAY); - - optionsModel = new OptionsModel(this); - addressTableModel = new AddressTableModel(this); - transactionTableModel = new TransactionTableModel(this); -} - -qint64 ClientModel::getBalance() -{ - return GetBalance(); -} - -QString ClientModel::getAddress() -{ - std::vector vchPubKey; - if (CWalletDB("r").ReadDefaultKey(vchPubKey)) - { - return QString::fromStdString(PubKeyToAddress(vchPubKey)); - } - else - { - return QString(); - } -} - -int ClientModel::getNumConnections() -{ - return vNodes.size(); -} - -int ClientModel::getNumBlocks() -{ - return nBestHeight; -} - -int ClientModel::getNumTransactions() -{ - int numTransactions = 0; - CRITICAL_BLOCK(cs_mapWallet) - { - numTransactions = mapWallet.size(); - } - return numTransactions; -} - -void ClientModel::update() -{ - /* Plainly emit all signals for now. To be precise this should check - wether the values actually changed first. - */ - emit balanceChanged(getBalance()); - emit addressChanged(getAddress()); - emit numConnectionsChanged(getNumConnections()); - emit numBlocksChanged(getNumBlocks()); - emit numTransactionsChanged(getNumTransactions()); -} - -void ClientModel::setAddress(const QString &defaultAddress) -{ - uint160 hash160; - std::string strAddress = defaultAddress.toStdString(); - if (!AddressToHash160(strAddress, hash160)) - return; - if (!mapPubKeys.count(hash160)) - return; - CWalletDB().WriteDefaultKey(mapPubKeys[hash160]); -} - -ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payAmount) -{ - uint160 hash160 = 0; - bool valid = false; - - if(!AddressToHash160(payTo.toUtf8().constData(), hash160)) - { - return InvalidAddress; - } - - if(payAmount <= 0) - { - return InvalidAmount; - } - - if(payAmount > getBalance()) - { - return AmountExceedsBalance; - } - - if((payAmount + nTransactionFee) > getBalance()) - { - return AmountWithFeeExceedsBalance; - } - - CRITICAL_BLOCK(cs_main) - { - // Send to bitcoin address - CWalletTx wtx; - CScript scriptPubKey; - scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; - - std::string strError = SendMoney(scriptPubKey, payAmount, wtx, true); - if (strError == "") - { - return OK; - } - else if (strError == "ABORTED") - { - return Aborted; - } - else - { - emit error(tr("Sending..."), QString::fromStdString(strError)); - return MiscError; - } - } - // Add addresses that we've sent to to the address book - std::string strAddress = payTo.toStdString(); - CRITICAL_BLOCK(cs_mapAddressBook) - if (!mapAddressBook.count(strAddress)) - SetAddressBookName(strAddress, ""); - - return OK; -} - -OptionsModel *ClientModel::getOptionsModel() -{ - return optionsModel; -} - -AddressTableModel *ClientModel::getAddressTableModel() -{ - return addressTableModel; -} - -TransactionTableModel *ClientModel::getTransactionTableModel() -{ - return transactionTableModel; -} diff --git a/gui/src/editaddressdialog.cpp b/gui/src/editaddressdialog.cpp deleted file mode 100644 index dd0541760b..0000000000 --- a/gui/src/editaddressdialog.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "editaddressdialog.h" -#include "ui_editaddressdialog.h" -#include "addresstablemodel.h" -#include "guiutil.h" - -#include -#include - -EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : - QDialog(parent), - ui(new Ui::EditAddressDialog), mapper(0), mode(mode), model(0) -{ - ui->setupUi(this); - - GUIUtil::setupAddressWidget(ui->addressEdit, this); - - switch(mode) - { - case NewReceivingAddress: - setWindowTitle(tr("New receiving address")); - ui->addressEdit->setEnabled(false); - break; - case NewSendingAddress: - setWindowTitle(tr("New sending address")); - break; - case EditReceivingAddress: - setWindowTitle(tr("Edit receiving address")); - ui->addressEdit->setReadOnly(true); - break; - case EditSendingAddress: - setWindowTitle(tr("Edit sending address")); - break; - } - - mapper = new QDataWidgetMapper(this); - mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); -} - -EditAddressDialog::~EditAddressDialog() -{ - delete ui; -} - -void EditAddressDialog::setModel(AddressTableModel *model) -{ - this->model = model; - mapper->setModel(model); - mapper->addMapping(ui->labelEdit, AddressTableModel::Label); - mapper->addMapping(ui->addressEdit, AddressTableModel::Address); -} - -void EditAddressDialog::loadRow(int row) -{ - mapper->setCurrentIndex(row); -} - -QString EditAddressDialog::saveCurrentRow() -{ - QString address; - switch(mode) - { - case NewReceivingAddress: - case NewSendingAddress: - address = model->addRow( - mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive, - ui->labelEdit->text(), - ui->addressEdit->text()); - if(address.isEmpty()) - { - QMessageBox::warning(this, windowTitle(), - tr("The address %1 is already in the address book.").arg(ui->addressEdit->text()), - QMessageBox::Ok, QMessageBox::Ok); - } - break; - case EditReceivingAddress: - case EditSendingAddress: - if(mapper->submit()) - { - address = ui->addressEdit->text(); - } - break; - } - return address; -} diff --git a/gui/src/guiutil.cpp b/gui/src/guiutil.cpp deleted file mode 100644 index a81cc14fe8..0000000000 --- a/gui/src/guiutil.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "guiutil.h" -#include "bitcoinaddressvalidator.h" - -#include -#include -#include -#include -#include - -QString GUIUtil::DateTimeStr(qint64 nTime) -{ - QDateTime date = QDateTime::fromMSecsSinceEpoch(nTime*1000); - return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm"); -} - -QFont GUIUtil::bitcoinAddressFont() -{ - QFont font("Monospace"); - font.setStyleHint(QFont::TypeWriter); - return font; -} - -void GUIUtil::setupAddressWidget(QLineEdit *widget, QWidget *parent) -{ - widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength); - widget->setValidator(new BitcoinAddressValidator(parent)); - widget->setFont(bitcoinAddressFont()); -} - -void GUIUtil::setupAmountWidget(QLineEdit *widget, QWidget *parent) -{ - QDoubleValidator *amountValidator = new QDoubleValidator(parent); - amountValidator->setDecimals(8); - amountValidator->setBottom(0.0); - widget->setValidator(amountValidator); - widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter); -} - diff --git a/gui/src/monitoreddatamapper.cpp b/gui/src/monitoreddatamapper.cpp deleted file mode 100644 index e70aa7ebf6..0000000000 --- a/gui/src/monitoreddatamapper.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "monitoreddatamapper.h" - -#include -#include -#include -#include - - -MonitoredDataMapper::MonitoredDataMapper(QObject *parent) : - QDataWidgetMapper(parent) -{ -} - - -void MonitoredDataMapper::addMapping(QWidget *widget, int section) -{ - QDataWidgetMapper::addMapping(widget, section); - addChangeMonitor(widget); -} - -void MonitoredDataMapper::addMapping(QWidget *widget, int section, const QByteArray &propertyName) -{ - QDataWidgetMapper::addMapping(widget, section, propertyName); - addChangeMonitor(widget); -} - -void MonitoredDataMapper::addChangeMonitor(QWidget *widget) -{ - /* Watch user property of widget for changes, and connect - the signal to our viewModified signal. - */ - QMetaProperty prop = widget->metaObject()->userProperty(); - int signal = prop.notifySignalIndex(); - int method = this->metaObject()->indexOfMethod("viewModified()"); - if(signal != -1 && method != -1) - { - QMetaObject::connect(widget, signal, this, method); - } -} diff --git a/gui/src/optionsdialog.cpp b/gui/src/optionsdialog.cpp deleted file mode 100644 index d46b48fb29..0000000000 --- a/gui/src/optionsdialog.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include "optionsdialog.h" -#include "optionsmodel.h" -#include "monitoreddatamapper.h" -#include "guiutil.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* First (currently only) page of options */ -class MainOptionsPage : public QWidget -{ -public: - explicit MainOptionsPage(QWidget *parent=0); - - void setMapper(MonitoredDataMapper *mapper); -private: - QCheckBox *bitcoin_at_startup; - QCheckBox *minimize_to_tray; - QCheckBox *map_port_upnp; - QCheckBox *minimize_on_close; - QCheckBox *connect_socks4; - QLineEdit *proxy_ip; - QLineEdit *proxy_port; - QLineEdit *fee_edit; - -signals: - -public slots: - -}; - -OptionsDialog::OptionsDialog(QWidget *parent): - QDialog(parent), contents_widget(0), pages_widget(0), - main_options_page(0), model(0) -{ - contents_widget = new QListWidget(); - contents_widget->setMaximumWidth(128); - - pages_widget = new QStackedWidget(); - pages_widget->setMinimumWidth(300); - - QListWidgetItem *item_main = new QListWidgetItem(tr("Main")); - contents_widget->addItem(item_main); - main_options_page = new MainOptionsPage(this); - pages_widget->addWidget(main_options_page); - - contents_widget->setCurrentRow(0); - - QHBoxLayout *main_layout = new QHBoxLayout(); - main_layout->addWidget(contents_widget); - main_layout->addWidget(pages_widget, 1); - - QVBoxLayout *layout = new QVBoxLayout(); - layout->addLayout(main_layout); - - QHBoxLayout *buttons = new QHBoxLayout(); - buttons->addStretch(1); - QPushButton *ok_button = new QPushButton(tr("OK")); - buttons->addWidget(ok_button); - QPushButton *cancel_button = new QPushButton(tr("Cancel")); - buttons->addWidget(cancel_button); - apply_button = new QPushButton(tr("Apply")); - apply_button->setEnabled(false); - buttons->addWidget(apply_button); - - layout->addLayout(buttons); - - setLayout(layout); - setWindowTitle(tr("Options")); - - /* Widget-to-option mapper */ - mapper = new MonitoredDataMapper(this); - mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); - mapper->setOrientation(Qt::Vertical); - /* enable apply button when data modified */ - connect(mapper, SIGNAL(viewModified()), this, SLOT(enableApply())); - /* disable apply button when new data loaded */ - connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApply())); - - /* Event bindings */ - connect(ok_button, SIGNAL(clicked()), this, SLOT(okClicked())); - connect(cancel_button, SIGNAL(clicked()), this, SLOT(cancelClicked())); - connect(apply_button, SIGNAL(clicked()), this, SLOT(applyClicked())); -} - -void OptionsDialog::setModel(OptionsModel *model) -{ - this->model = model; - - mapper->setModel(model); - main_options_page->setMapper(mapper); - - mapper->toFirst(); -} - -void OptionsDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) -{ - Q_UNUSED(previous); - if(current) - { - pages_widget->setCurrentIndex(contents_widget->row(current)); - } -} - -void OptionsDialog::okClicked() -{ - mapper->submit(); - accept(); -} - -void OptionsDialog::cancelClicked() -{ - reject(); -} - -void OptionsDialog::applyClicked() -{ - mapper->submit(); - apply_button->setEnabled(false); -} - -void OptionsDialog::enableApply() -{ - apply_button->setEnabled(true); -} - -void OptionsDialog::disableApply() -{ - apply_button->setEnabled(false); -} - -MainOptionsPage::MainOptionsPage(QWidget *parent): - QWidget(parent) -{ - QVBoxLayout *layout = new QVBoxLayout(); - - bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup")); - bitcoin_at_startup->setToolTip(tr("Automatically start Bitcoin after the computer is turned on")); - layout->addWidget(bitcoin_at_startup); - - minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar")); - minimize_to_tray->setToolTip(tr("Show only a tray icon after minimizing the window")); - layout->addWidget(minimize_to_tray); - - map_port_upnp = new QCheckBox(tr("Map port using &UPnP")); - map_port_upnp->setToolTip(tr("Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.")); - layout->addWidget(map_port_upnp); - - minimize_on_close = new QCheckBox(tr("M&inimize on close")); - minimize_on_close->setToolTip(tr("Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu.")); - layout->addWidget(minimize_on_close); - - connect_socks4 = new QCheckBox(tr("&Connect through SOCKS4 proxy:")); - connect_socks4->setToolTip(tr("Connect to the Bitcon network through a SOCKS4 proxy (e.g. when connecting through Tor)")); - layout->addWidget(connect_socks4); - - QHBoxLayout *proxy_hbox = new QHBoxLayout(); - proxy_hbox->addSpacing(18); - QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP: ")); - proxy_hbox->addWidget(proxy_ip_label); - proxy_ip = new QLineEdit(); - proxy_ip->setMaximumWidth(140); - proxy_ip->setEnabled(false); - proxy_ip->setValidator(new QRegExpValidator(QRegExp("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"), this)); - proxy_ip->setToolTip(tr("IP address of the proxy (e.g. 127.0.0.1)")); - proxy_ip_label->setBuddy(proxy_ip); - proxy_hbox->addWidget(proxy_ip); - QLabel *proxy_port_label = new QLabel(tr("&Port: ")); - proxy_hbox->addWidget(proxy_port_label); - proxy_port = new QLineEdit(); - proxy_port->setMaximumWidth(55); - proxy_port->setValidator(new QIntValidator(0, 65535, this)); - proxy_port->setEnabled(false); - proxy_port->setToolTip(tr("Port of the proxy (e.g. 1234)")); - proxy_port_label->setBuddy(proxy_port); - proxy_hbox->addWidget(proxy_port); - proxy_hbox->addStretch(1); - - layout->addLayout(proxy_hbox); - QLabel *fee_help = new QLabel(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended.")); - fee_help->setWordWrap(true); - layout->addWidget(fee_help); - - QHBoxLayout *fee_hbox = new QHBoxLayout(); - fee_hbox->addSpacing(18); - QLabel *fee_label = new QLabel(tr("Pay transaction &fee")); - fee_hbox->addWidget(fee_label); - fee_edit = new QLineEdit(); - fee_edit->setMaximumWidth(100); - fee_edit->setToolTip(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended.")); - - GUIUtil::setupAmountWidget(fee_edit, this); - - fee_label->setBuddy(fee_edit); - fee_hbox->addWidget(fee_edit); - fee_hbox->addStretch(1); - - layout->addLayout(fee_hbox); - - layout->addStretch(1); /* Extra space at bottom */ - - setLayout(layout); - - connect(connect_socks4, SIGNAL(toggled(bool)), proxy_ip, SLOT(setEnabled(bool))); - connect(connect_socks4, SIGNAL(toggled(bool)), proxy_port, SLOT(setEnabled(bool))); - -#ifndef USE_UPNP - map_port_upnp->setDisabled(true); -#endif -} - -void MainOptionsPage::setMapper(MonitoredDataMapper *mapper) -{ - /* Map model to widgets */ - mapper->addMapping(bitcoin_at_startup, OptionsModel::StartAtStartup); - mapper->addMapping(minimize_to_tray, OptionsModel::MinimizeToTray); - mapper->addMapping(map_port_upnp, OptionsModel::MapPortUPnP); - mapper->addMapping(minimize_on_close, OptionsModel::MinimizeOnClose); - mapper->addMapping(connect_socks4, OptionsModel::ConnectSOCKS4); - mapper->addMapping(proxy_ip, OptionsModel::ProxyIP); - mapper->addMapping(proxy_port, OptionsModel::ProxyPort); - mapper->addMapping(fee_edit, OptionsModel::Fee); -} - diff --git a/gui/src/optionsmodel.cpp b/gui/src/optionsmodel.cpp deleted file mode 100644 index 1528fdf697..0000000000 --- a/gui/src/optionsmodel.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "optionsmodel.h" -#include "main.h" -#include "net.h" - -#include - -OptionsModel::OptionsModel(QObject *parent) : - QAbstractListModel(parent) -{ -} - -int OptionsModel::rowCount(const QModelIndex & parent) const -{ - return OptionIDRowCount; -} - -QVariant OptionsModel::data(const QModelIndex & index, int role) const -{ - if(role == Qt::EditRole) - { - switch(index.row()) - { - case StartAtStartup: - return QVariant(); - case MinimizeToTray: - return QVariant(fMinimizeToTray); - case MapPortUPnP: - return QVariant(fUseUPnP); - case MinimizeOnClose: - return QVariant(fMinimizeOnClose); - case ConnectSOCKS4: - return QVariant(fUseProxy); - case ProxyIP: - return QVariant(QString::fromStdString(addrProxy.ToStringIP())); - case ProxyPort: - return QVariant(QString::fromStdString(addrProxy.ToStringPort())); - case Fee: - return QVariant(QString::fromStdString(FormatMoney(nTransactionFee))); - default: - return QVariant(); - } - } - return QVariant(); -} - -bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role) -{ - bool successful = true; /* set to false on parse error */ - if(role == Qt::EditRole) - { - CWalletDB walletdb; - switch(index.row()) - { - case StartAtStartup: - successful = false; /*TODO*/ - break; - case MinimizeToTray: - fMinimizeToTray = value.toBool(); - walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray); - break; - case MapPortUPnP: - fUseUPnP = value.toBool(); - walletdb.WriteSetting("fUseUPnP", fUseUPnP); -#ifdef USE_UPNP - MapPort(fUseUPnP); -#endif - break; - case MinimizeOnClose: - fMinimizeOnClose = value.toBool(); - walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose); - break; - case ConnectSOCKS4: - fUseProxy = value.toBool(); - walletdb.WriteSetting("fUseProxy", fUseProxy); - break; - case ProxyIP: - { - /* Use CAddress to parse IP */ - CAddress addr(value.toString().toStdString() + ":1"); - if (addr.ip != INADDR_NONE) - { - addrProxy.ip = addr.ip; - walletdb.WriteSetting("addrProxy", addrProxy); - } - else - { - successful = false; - } - } - break; - case ProxyPort: - { - int nPort = atoi(value.toString().toAscii().data()); - if (nPort > 0 && nPort < USHRT_MAX) - { - addrProxy.port = htons(nPort); - walletdb.WriteSetting("addrProxy", addrProxy); - } - else - { - successful = false; - } - } - break; - case Fee: { - int64 retval; - if(ParseMoney(value.toString().toStdString(), retval)) - { - nTransactionFee = retval; - walletdb.WriteSetting("nTransactionFee", nTransactionFee); - } - else - { - successful = false; /* parse error */ - } - } - break; - default: - break; - } - } - emit dataChanged(index, index); - - return successful; -} - -qint64 OptionsModel::getTransactionFee() -{ - return nTransactionFee; -} - -bool OptionsModel::getMinimizeToTray() -{ - return fMinimizeToTray; -} - -bool OptionsModel::getMinimizeOnClose() -{ - return fMinimizeOnClose; -} diff --git a/gui/src/sendcoinsdialog.cpp b/gui/src/sendcoinsdialog.cpp deleted file mode 100644 index 721ab14108..0000000000 --- a/gui/src/sendcoinsdialog.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "sendcoinsdialog.h" -#include "ui_sendcoinsdialog.h" -#include "clientmodel.h" -#include "guiutil.h" - -#include "addressbookdialog.h" -#include "optionsmodel.h" - -#include -#include -#include -#include -#include - -#include "util.h" -#include "base58.h" - -SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : - QDialog(parent), - ui(new Ui::SendCoinsDialog), - model(0) -{ - ui->setupUi(this); - - GUIUtil::setupAddressWidget(ui->payTo, this); - GUIUtil::setupAmountWidget(ui->payAmount, this); - - /* Set initial address if provided */ - if(!address.isEmpty()) - { - ui->payTo->setText(address); - ui->payAmount->setFocus(); - } -} - -void SendCoinsDialog::setModel(ClientModel *model) -{ - this->model = model; -} - -SendCoinsDialog::~SendCoinsDialog() -{ - delete ui; -} - -void SendCoinsDialog::on_sendButton_clicked() -{ - bool valid; - QString payAmount = ui->payAmount->text(); - qint64 payAmountParsed; - - valid = ParseMoney(payAmount.toStdString(), payAmountParsed); - - if(!valid) - { - QMessageBox::warning(this, tr("Send Coins"), - tr("The amount to pay must be a valid number."), - QMessageBox::Ok, QMessageBox::Ok); - return; - } - - switch(model->sendCoins(ui->payTo->text(), payAmountParsed)) - { - case ClientModel::InvalidAddress: - QMessageBox::warning(this, tr("Send Coins"), - tr("The recepient address is not valid, please recheck."), - QMessageBox::Ok, QMessageBox::Ok); - ui->payTo->setFocus(); - break; - case ClientModel::InvalidAmount: - QMessageBox::warning(this, tr("Send Coins"), - tr("The amount to pay must be larger than 0."), - QMessageBox::Ok, QMessageBox::Ok); - ui->payAmount->setFocus(); - break; - case ClientModel::AmountExceedsBalance: - QMessageBox::warning(this, tr("Send Coins"), - tr("Amount exceeds your balance"), - QMessageBox::Ok, QMessageBox::Ok); - ui->payAmount->setFocus(); - break; - case ClientModel::AmountWithFeeExceedsBalance: - QMessageBox::warning(this, tr("Send Coins"), - tr("Total exceeds your balance when the %1 transaction fee is included"). - arg(QString::fromStdString(FormatMoney(model->getOptionsModel()->getTransactionFee()))), - QMessageBox::Ok, QMessageBox::Ok); - ui->payAmount->setFocus(); - break; - case ClientModel::OK: - accept(); - break; - } -} - -void SendCoinsDialog::on_pasteButton_clicked() -{ - /* Paste text from clipboard into recipient field */ - ui->payTo->setText(QApplication::clipboard()->text()); -} - -void SendCoinsDialog::on_addressBookButton_clicked() -{ - AddressBookDialog dlg; - dlg.setModel(model->getAddressTableModel()); - dlg.setTab(AddressBookDialog::SendingTab); - dlg.exec(); - ui->payTo->setText(dlg.getReturnValue()); -} - -void SendCoinsDialog::on_buttonBox_rejected() -{ - reject(); -} diff --git a/gui/src/transactiondesc.cpp b/gui/src/transactiondesc.cpp deleted file mode 100644 index 4d8a55e99a..0000000000 --- a/gui/src/transactiondesc.cpp +++ /dev/null @@ -1,310 +0,0 @@ -#include - -#include "guiutil.h" -#include "main.h" - -#include - -/* Taken straight from ui.cpp - TODO: Convert to use QStrings, Qt::Escape and tr() - */ - -using namespace std; - -static string HtmlEscape(const char* psz, bool fMultiLine=false) -{ - int len = 0; - for (const char* p = psz; *p; p++) - { - if (*p == '<') len += 4; - else if (*p == '>') len += 4; - else if (*p == '&') len += 5; - else if (*p == '"') len += 6; - else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6; - else if (*p == '\n' && fMultiLine) len += 5; - else - len++; - } - string str; - str.reserve(len); - for (const char* p = psz; *p; p++) - { - if (*p == '<') str += "<"; - else if (*p == '>') str += ">"; - else if (*p == '&') str += "&"; - else if (*p == '"') str += """; - else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " "; - else if (*p == '\n' && fMultiLine) str += "
\n"; - else - str += *p; - } - return str; -} - -static string HtmlEscape(const string& str, bool fMultiLine=false) -{ - return HtmlEscape(str.c_str(), fMultiLine); -} - -static string FormatTxStatus(const CWalletTx& wtx) -{ - // Status - if (!wtx.IsFinal()) - { - if (wtx.nLockTime < 500000000) - return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime); - else - return strprintf(_("Open until %s"), GUIUtil::DateTimeStr(wtx.nLockTime).toStdString().c_str()); - } - else - { - int nDepth = wtx.GetDepthInMainChain(); - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) - return strprintf(_("%d/offline?"), nDepth); - else if (nDepth < 6) - return strprintf(_("%d/unconfirmed"), nDepth); - else - return strprintf(_("%d confirmations"), nDepth); - } -} - -string TransactionDesc::toHTML(CWalletTx &wtx) -{ - string strHTML; - CRITICAL_BLOCK(cs_mapAddressBook) - { - strHTML.reserve(4000); - strHTML += ""; - - int64 nTime = wtx.GetTxTime(); - int64 nCredit = wtx.GetCredit(); - int64 nDebit = wtx.GetDebit(); - int64 nNet = nCredit - nDebit; - - - - strHTML += _("Status: ") + FormatTxStatus(wtx); - int nRequests = wtx.GetRequestCount(); - if (nRequests != -1) - { - if (nRequests == 0) - strHTML += _(", has not been successfully broadcast yet"); - else if (nRequests == 1) - strHTML += strprintf(_(", broadcast through %d node"), nRequests); - else - strHTML += strprintf(_(", broadcast through %d nodes"), nRequests); - } - strHTML += "
"; - - strHTML += _("Date: ") + (nTime ? GUIUtil::DateTimeStr(nTime).toStdString() : "") + "
"; - - - // - // From - // - if (wtx.IsCoinBase()) - { - strHTML += _("Source: Generated
"); - } - else if (!wtx.mapValue["from"].empty()) - { - // Online transaction - if (!wtx.mapValue["from"].empty()) - strHTML += _("From: ") + HtmlEscape(wtx.mapValue["from"]) + "
"; - } - else - { - // Offline transaction - if (nNet > 0) - { - // Credit - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - if (txout.IsMine()) - { - vector vchPubKey; - if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) - { - string strAddress = PubKeyToAddress(vchPubKey); - if (mapAddressBook.count(strAddress)) - { - strHTML += string() + _("From: ") + _("unknown") + "
"; - strHTML += _("To: "); - strHTML += HtmlEscape(strAddress); - if (!mapAddressBook[strAddress].empty()) - strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")"; - else - strHTML += _(" (yours)"); - strHTML += "
"; - } - } - break; - } - } - } - } - - - // - // To - // - string strAddress; - if (!wtx.mapValue["to"].empty()) - { - // Online transaction - strAddress = wtx.mapValue["to"]; - strHTML += _("To: "); - if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty()) - strHTML += mapAddressBook[strAddress] + " "; - strHTML += HtmlEscape(strAddress) + "
"; - } - - - // - // Amount - // - if (wtx.IsCoinBase() && nCredit == 0) - { - // - // Coinbase - // - int64 nUnmatured = 0; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - nUnmatured += txout.GetCredit(); - strHTML += _("Credit: "); - if (wtx.IsInMainChain()) - strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity()); - else - strHTML += _("(not accepted)"); - strHTML += "
"; - } - else if (nNet > 0) - { - // - // Credit - // - strHTML += _("Credit: ") + FormatMoney(nNet) + "
"; - } - else - { - bool fAllFromMe = true; - BOOST_FOREACH(const CTxIn& txin, wtx.vin) - fAllFromMe = fAllFromMe && txin.IsMine(); - - bool fAllToMe = true; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - fAllToMe = fAllToMe && txout.IsMine(); - - if (fAllFromMe) - { - // - // Debit - // - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - if (txout.IsMine()) - continue; - - if (wtx.mapValue["to"].empty()) - { - // Offline transaction - uint160 hash160; - if (ExtractHash160(txout.scriptPubKey, hash160)) - { - string strAddress = Hash160ToAddress(hash160); - strHTML += _("To: "); - if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty()) - strHTML += mapAddressBook[strAddress] + " "; - strHTML += strAddress; - strHTML += "
"; - } - } - - strHTML += _("Debit: ") + FormatMoney(-txout.nValue) + "
"; - } - - if (fAllToMe) - { - // Payment to self - int64 nChange = wtx.GetChange(); - int64 nValue = nCredit - nChange; - strHTML += _("Debit: ") + FormatMoney(-nValue) + "
"; - strHTML += _("Credit: ") + FormatMoney(nValue) + "
"; - } - - int64 nTxFee = nDebit - wtx.GetValueOut(); - if (nTxFee > 0) - strHTML += _("Transaction fee: ") + FormatMoney(-nTxFee) + "
"; - } - else - { - // - // Mixed debit transaction - // - BOOST_FOREACH(const CTxIn& txin, wtx.vin) - if (txin.IsMine()) - strHTML += _("Debit: ") + FormatMoney(-txin.GetDebit()) + "
"; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.IsMine()) - strHTML += _("Credit: ") + FormatMoney(txout.GetCredit()) + "
"; - } - } - - strHTML += _("Net amount: ") + FormatMoney(nNet, true) + "
"; - - - // - // Message - // - if (!wtx.mapValue["message"].empty()) - strHTML += string() + "
" + _("Message:") + "
" + HtmlEscape(wtx.mapValue["message"], true) + "
"; - if (!wtx.mapValue["comment"].empty()) - strHTML += string() + "
" + _("Comment:") + "
" + HtmlEscape(wtx.mapValue["comment"], true) + "
"; - - if (wtx.IsCoinBase()) - strHTML += string() + "
" + _("Generated coins must wait 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, it will change to \"not accepted\" and not be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.") + "
"; - - - // - // Debug view - // - if (fDebug) - { - strHTML += "

debug print

"; - BOOST_FOREACH(const CTxIn& txin, wtx.vin) - if (txin.IsMine()) - strHTML += "Debit: " + FormatMoney(-txin.GetDebit()) + "
"; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.IsMine()) - strHTML += "Credit: " + FormatMoney(txout.GetCredit()) + "
"; - - strHTML += "
Transaction:
"; - strHTML += HtmlEscape(wtx.ToString(), true); - - strHTML += "
Inputs:
"; - CRITICAL_BLOCK(cs_mapWallet) - { - BOOST_FOREACH(const CTxIn& txin, wtx.vin) - { - COutPoint prevout = txin.prevout; - map::iterator mi = mapWallet.find(prevout.hash); - if (mi != mapWallet.end()) - { - const CWalletTx& prev = (*mi).second; - if (prevout.n < prev.vout.size()) - { - strHTML += HtmlEscape(prev.ToString(), true); - strHTML += "    " + FormatTxStatus(prev) + ", "; - strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "
"; - } - } - } - } - } - - - - strHTML += "
"; - } - return strHTML; -} diff --git a/gui/src/transactiondescdialog.cpp b/gui/src/transactiondescdialog.cpp deleted file mode 100644 index 3bd4808cb6..0000000000 --- a/gui/src/transactiondescdialog.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "transactiondescdialog.h" -#include "ui_transactiondescdialog.h" - -#include "transactiontablemodel.h" - -#include - -TransactionDescDialog::TransactionDescDialog(const QModelIndex &idx, QWidget *parent) : - QDialog(parent), - ui(new Ui::TransactionDescDialog) -{ - ui->setupUi(this); - QString desc = idx.data(TransactionTableModel::LongDescriptionRole).toString(); - ui->detailText->setHtml(desc); -} - -TransactionDescDialog::~TransactionDescDialog() -{ - delete ui; -} diff --git a/gui/src/transactionrecord.cpp b/gui/src/transactionrecord.cpp deleted file mode 100644 index 6c1f3a5e58..0000000000 --- a/gui/src/transactionrecord.cpp +++ /dev/null @@ -1,254 +0,0 @@ -#include "transactionrecord.h" - - -/* Return positive answer if transaction should be shown in list. - */ -bool TransactionRecord::showTransaction(const CWalletTx &wtx) -{ - if (wtx.IsCoinBase()) - { - // Don't show generated coin until confirmed by at least one block after it - // so we don't get the user's hopes up until it looks like it's probably accepted. - // - // It is not an error when generated blocks are not accepted. By design, - // some percentage of blocks, like 10% or more, will end up not accepted. - // This is the normal mechanism by which the network copes with latency. - // - // We display regular transactions right away before any confirmation - // because they can always get into some block eventually. Generated coins - // are special because if their block is not accepted, they are not valid. - // - if (wtx.GetDepthInMainChain() < 2) - { - return false; - } - } - return true; -} - -/* Decompose CWallet transaction to model transaction records. - */ -QList TransactionRecord::decomposeTransaction(const CWalletTx &wtx) -{ - QList parts; - int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime(); - int64 nCredit = wtx.GetCredit(true); - int64 nDebit = wtx.GetDebit(); - int64 nNet = nCredit - nDebit; - uint256 hash = wtx.GetHash(); - std::map mapValue = wtx.mapValue; - - if (showTransaction(wtx)) - { - if (nNet > 0 || wtx.IsCoinBase()) - { - // - // Credit - // - TransactionRecord sub(hash, nTime); - - sub.credit = nNet; - - if (wtx.IsCoinBase()) - { - // Generated - sub.type = TransactionRecord::Generated; - - if (nCredit == 0) - { - int64 nUnmatured = 0; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - nUnmatured += txout.GetCredit(); - sub.credit = nUnmatured; - } - } - else if (!mapValue["from"].empty() || !mapValue["message"].empty()) - { - // Received by IP connection - sub.type = TransactionRecord::RecvFromIP; - if (!mapValue["from"].empty()) - sub.address = mapValue["from"]; - } - else - { - // Received by Bitcoin Address - sub.type = TransactionRecord::RecvWithAddress; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - if (txout.IsMine()) - { - std::vector vchPubKey; - if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) - { - sub.address = PubKeyToAddress(vchPubKey); - } - break; - } - } - } - parts.append(sub); - } - else - { - bool fAllFromMe = true; - BOOST_FOREACH(const CTxIn& txin, wtx.vin) - fAllFromMe = fAllFromMe && txin.IsMine(); - - bool fAllToMe = true; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - fAllToMe = fAllToMe && txout.IsMine(); - - if (fAllFromMe && fAllToMe) - { - // Payment to self - int64 nChange = wtx.GetChange(); - - parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", - -(nDebit - nChange), nCredit - nChange)); - } - else if (fAllFromMe) - { - // - // Debit - // - int64 nTxFee = nDebit - wtx.GetValueOut(); - - for (int nOut = 0; nOut < wtx.vout.size(); nOut++) - { - const CTxOut& txout = wtx.vout[nOut]; - TransactionRecord sub(hash, nTime); - sub.idx = parts.size(); - - if (txout.IsMine()) - { - // Ignore parts sent to self, as this is usually the change - // from a transaction sent back to our own address. - continue; - } - else if (!mapValue["to"].empty()) - { - // Sent to IP - sub.type = TransactionRecord::SendToIP; - sub.address = mapValue["to"]; - } - else - { - // Sent to Bitcoin Address - sub.type = TransactionRecord::SendToAddress; - uint160 hash160; - if (ExtractHash160(txout.scriptPubKey, hash160)) - sub.address = Hash160ToAddress(hash160); - } - - int64 nValue = txout.nValue; - /* Add fee to first output */ - if (nTxFee > 0) - { - nValue += nTxFee; - nTxFee = 0; - } - sub.debit = -nValue; - - parts.append(sub); - } - } - else - { - // - // Mixed debit transaction, can't break down payees - // - bool fAllMine = true; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - fAllMine = fAllMine && txout.IsMine(); - BOOST_FOREACH(const CTxIn& txin, wtx.vin) - fAllMine = fAllMine && txin.IsMine(); - - parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0)); - } - } - } - - return parts; -} - -void TransactionRecord::updateStatus(const CWalletTx &wtx) -{ - // Determine transaction status - - // Find the block the tx is in - CBlockIndex* pindex = NULL; - std::map::iterator mi = mapBlockIndex.find(wtx.hashBlock); - if (mi != mapBlockIndex.end()) - pindex = (*mi).second; - - // Sort order, unrecorded transactions sort to the top - status.sortKey = strprintf("%010d-%01d-%010u-%03d", - (pindex ? pindex->nHeight : INT_MAX), - (wtx.IsCoinBase() ? 1 : 0), - wtx.nTimeReceived, - idx); - status.confirmed = wtx.IsConfirmed(); - status.depth = wtx.GetDepthInMainChain(); - status.cur_num_blocks = nBestHeight; - - if (!wtx.IsFinal()) - { - if (wtx.nLockTime < 500000000) - { - status.status = TransactionStatus::OpenUntilBlock; - status.open_for = nBestHeight - wtx.nLockTime; - } - else - { - status.status = TransactionStatus::OpenUntilDate; - status.open_for = wtx.nLockTime; - } - } - else - { - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) - { - status.status = TransactionStatus::Offline; - } - else if (status.depth < 6) - { - status.status = TransactionStatus::Unconfirmed; - } - else - { - status.status = TransactionStatus::HaveConfirmations; - } - } - - // For generated transactions, determine maturity - if(type == TransactionRecord::Generated) - { - int64 nCredit = wtx.GetCredit(true); - if (nCredit == 0) - { - status.maturity = TransactionStatus::Immature; - - if (wtx.IsInMainChain()) - { - status.matures_in = wtx.GetBlocksToMaturity(); - - // Check if the block was requested by anyone - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) - status.maturity = TransactionStatus::MaturesWarning; - } - else - { - status.maturity = TransactionStatus::NotAccepted; - } - } - else - { - status.maturity = TransactionStatus::Mature; - } - } -} - -bool TransactionRecord::statusUpdateNeeded() -{ - return status.cur_num_blocks != nBestHeight; -} diff --git a/gui/src/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp deleted file mode 100644 index 8fe1839930..0000000000 --- a/gui/src/transactiontablemodel.cpp +++ /dev/null @@ -1,512 +0,0 @@ -#include "transactiontablemodel.h" -#include "guiutil.h" -#include "transactionrecord.h" -#include "guiconstants.h" -#include "main.h" -#include "transactiondesc.h" - -#include -#include -#include -#include -#include -#include - -const QString TransactionTableModel::Sent = "s"; -const QString TransactionTableModel::Received = "r"; -const QString TransactionTableModel::Other = "o"; - -/* Comparison operator for sort/binary search of model tx list */ -struct TxLessThan -{ - bool operator()(const TransactionRecord &a, const TransactionRecord &b) const - { - return a.hash < b.hash; - } - bool operator()(const TransactionRecord &a, const uint256 &b) const - { - return a.hash < b; - } - bool operator()(const uint256 &a, const TransactionRecord &b) const - { - return a < b.hash; - } -}; - -/* Private implementation */ -struct TransactionTablePriv -{ - TransactionTablePriv(TransactionTableModel *parent): - parent(parent) - { - } - - TransactionTableModel *parent; - - /* Local cache of wallet. - * As it is in the same order as the CWallet, by definition - * this is sorted by sha256. - */ - QList cachedWallet; - - void refreshWallet() - { - qDebug() << "refreshWallet"; - - /* Query entire wallet from core. - */ - cachedWallet.clear(); - CRITICAL_BLOCK(cs_mapWallet) - { - for(std::map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - cachedWallet.append(TransactionRecord::decomposeTransaction(it->second)); - } - } - } - - /* Update our model of the wallet incrementally. - Call with list of hashes of transactions that were added, removed or changed. - */ - void updateWallet(const QList &updated) - { - /* Walk through updated transactions, update model as needed. - */ - qDebug() << "updateWallet"; - - /* Sort update list, and iterate through it in reverse, so that model updates - can be emitted from end to beginning (so that earlier updates will not influence - the indices of latter ones). - */ - QList updated_sorted = updated; - qSort(updated_sorted); - - CRITICAL_BLOCK(cs_mapWallet) - { - for(int update_idx = updated_sorted.size()-1; update_idx >= 0; --update_idx) - { - const uint256 &hash = updated_sorted.at(update_idx); - /* Find transaction in wallet */ - std::map::iterator mi = mapWallet.find(hash); - bool inWallet = mi != mapWallet.end(); - /* Find bounds of this transaction in model */ - QList::iterator lower = qLowerBound( - cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); - QList::iterator upper = qUpperBound( - cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); - int lowerIndex = (lower - cachedWallet.begin()); - int upperIndex = (upper - cachedWallet.begin()); - - bool inModel = false; - if(lower != upper) - { - inModel = true; - } - - qDebug() << " " << QString::fromStdString(hash.ToString()) << inWallet << " " << inModel - << lowerIndex << "-" << upperIndex; - - if(inWallet && !inModel) - { - /* Added */ - QList toInsert = - TransactionRecord::decomposeTransaction(mi->second); - if(!toInsert.isEmpty()) /* only if something to insert */ - { - parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); - int insert_idx = lowerIndex; - foreach(const TransactionRecord &rec, toInsert) - { - cachedWallet.insert(insert_idx, rec); - insert_idx += 1; - } - parent->endInsertRows(); - } - } - else if(!inWallet && inModel) - { - /* Removed */ - parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); - cachedWallet.erase(lower, upper); - parent->endRemoveRows(); - } - else if(inWallet && inModel) - { - /* Updated -- nothing to do, status update will take care of this */ - } - } - } - } - - int size() - { - return cachedWallet.size(); - } - - TransactionRecord *index(int idx) - { - if(idx >= 0 && idx < cachedWallet.size()) - { - TransactionRecord *rec = &cachedWallet[idx]; - - /* If a status update is needed (blocks came in since last check), - update the status of this transaction from the wallet. Otherwise, - simply re-use the cached status. - */ - if(rec->statusUpdateNeeded()) - { - CRITICAL_BLOCK(cs_mapWallet) - { - std::map::iterator mi = mapWallet.find(rec->hash); - - if(mi != mapWallet.end()) - { - rec->updateStatus(mi->second); - } - } - } - return rec; - } - else - { - return 0; - } - } - - QString describe(TransactionRecord *rec) - { - CRITICAL_BLOCK(cs_mapWallet) - { - std::map::iterator mi = mapWallet.find(rec->hash); - if(mi != mapWallet.end()) - { - return QString::fromStdString(TransactionDesc::toHTML(mi->second)); - } - } - return QString(""); - } - -}; - -/* Credit and Debit columns are right-aligned as they contain numbers */ -static int column_alignments[] = { - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignRight|Qt::AlignVCenter, - Qt::AlignRight|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter - }; - -TransactionTableModel::TransactionTableModel(QObject *parent): - QAbstractTableModel(parent), - priv(new TransactionTablePriv(this)) -{ - columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); - - priv->refreshWallet(); - - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(update())); - timer->start(MODEL_UPDATE_DELAY); -} - -TransactionTableModel::~TransactionTableModel() -{ - delete priv; -} - -void TransactionTableModel::update() -{ - QList updated; - - /* Check if there are changes to wallet map */ - TRY_CRITICAL_BLOCK(cs_mapWallet) - { - if(!vWalletUpdated.empty()) - { - BOOST_FOREACH(uint256 hash, vWalletUpdated) - { - updated.append(hash); - } - vWalletUpdated.clear(); - } - } - - if(!updated.empty()) - { - priv->updateWallet(updated); - - /* Status (number of confirmations) and (possibly) description - columns changed for all rows. - */ - emit dataChanged(index(0, Status), index(priv->size()-1, Status)); - emit dataChanged(index(0, Description), index(priv->size()-1, Description)); - } -} - -int TransactionTableModel::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return priv->size(); -} - -int TransactionTableModel::columnCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return columns.length(); -} - -QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const -{ - QString status; - - switch(wtx->status.status) - { - case TransactionStatus::OpenUntilBlock: - status = tr("Open for %n block(s)","",wtx->status.open_for); - break; - case TransactionStatus::OpenUntilDate: - status = tr("Open until ") + GUIUtil::DateTimeStr(wtx->status.open_for); - break; - case TransactionStatus::Offline: - status = tr("%1/offline").arg(wtx->status.depth); - break; - case TransactionStatus::Unconfirmed: - status = tr("%1/unconfirmed").arg(wtx->status.depth); - break; - case TransactionStatus::HaveConfirmations: - status = tr("%1 confirmations").arg(wtx->status.depth); - break; - } - - return QVariant(status); -} - -QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const -{ - if(wtx->time) - { - return QVariant(GUIUtil::DateTimeStr(wtx->time)); - } - else - { - return QVariant(); - } -} - -/* Look up address in address book, if found return - address[0:12]... (label) - otherwise just return address - */ -std::string lookupAddress(const std::string &address) -{ - std::string description; - CRITICAL_BLOCK(cs_mapAddressBook) - { - std::map::iterator mi = mapAddressBook.find(address); - if (mi != mapAddressBook.end() && !(*mi).second.empty()) - { - std::string label = (*mi).second; - description += address.substr(0,12) + "... "; - description += "(" + label + ")"; - } - else - { - description += address; - } - } - return description; -} - -QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx) const -{ - QString description; - - switch(wtx->type) - { - case TransactionRecord::RecvWithAddress: - description = tr("Received with: ") + QString::fromStdString(lookupAddress(wtx->address)); - break; - case TransactionRecord::RecvFromIP: - description = tr("Received from IP: ") + QString::fromStdString(wtx->address); - break; - case TransactionRecord::SendToAddress: - description = tr("Sent to: ") + QString::fromStdString(lookupAddress(wtx->address)); - break; - case TransactionRecord::SendToIP: - description = tr("Sent to IP: ") + QString::fromStdString(wtx->address); - break; - case TransactionRecord::SendToSelf: - description = tr("Payment to yourself"); - break; - case TransactionRecord::Generated: - switch(wtx->status.maturity) - { - case TransactionStatus::Immature: - description = tr("Generated (matures in %n more blocks)", "", - wtx->status.matures_in); - break; - case TransactionStatus::Mature: - description = tr("Generated"); - break; - case TransactionStatus::MaturesWarning: - description = tr("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!"); - break; - case TransactionStatus::NotAccepted: - description = tr("Generated (not accepted)"); - break; - } - break; - } - return QVariant(description); -} - -QVariant TransactionTableModel::formatTxDebit(const TransactionRecord *wtx) const -{ - if(wtx->debit) - { - QString str = QString::fromStdString(FormatMoney(wtx->debit)); - if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) - { - str = QString("[") + str + QString("]"); - } - return QVariant(str); - } - else - { - return QVariant(); - } -} - -QVariant TransactionTableModel::formatTxCredit(const TransactionRecord *wtx) const -{ - if(wtx->credit) - { - QString str = QString::fromStdString(FormatMoney(wtx->credit)); - if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) - { - str = QString("[") + str + QString("]"); - } - return QVariant(str); - } - else - { - return QVariant(); - } -} - -QVariant TransactionTableModel::data(const QModelIndex &index, int role) const -{ - if(!index.isValid()) - return QVariant(); - TransactionRecord *rec = static_cast(index.internalPointer()); - - if(role == Qt::DisplayRole) - { - /* Delegate to specific column handlers */ - switch(index.column()) - { - case Status: - return formatTxStatus(rec); - case Date: - return formatTxDate(rec); - case Description: - return formatTxDescription(rec); - case Debit: - return formatTxDebit(rec); - case Credit: - return formatTxCredit(rec); - } - } - else if(role == Qt::EditRole) - { - /* Edit role is used for sorting so return the real values */ - switch(index.column()) - { - case Status: - return QString::fromStdString(rec->status.sortKey); - case Date: - return rec->time; - case Description: - return formatTxDescription(rec); - case Debit: - return rec->debit; - case Credit: - return rec->credit; - } - } - else if (role == Qt::TextAlignmentRole) - { - return column_alignments[index.column()]; - } - else if (role == Qt::ForegroundRole) - { - /* Non-confirmed transactions are grey */ - if(rec->status.confirmed) - { - return QColor(0, 0, 0); - } - else - { - return QColor(128, 128, 128); - } - } - else if (role == TypeRole) - { - /* Role for filtering tabs by type */ - switch(rec->type) - { - case TransactionRecord::RecvWithAddress: - case TransactionRecord::RecvFromIP: - return TransactionTableModel::Received; - case TransactionRecord::SendToAddress: - case TransactionRecord::SendToIP: - case TransactionRecord::SendToSelf: - return TransactionTableModel::Sent; - default: - return TransactionTableModel::Other; - } - } - else if (role == LongDescriptionRole) - { - return priv->describe(rec); - } - return QVariant(); -} - -QVariant TransactionTableModel::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]; - } - } - return QVariant(); -} - -Qt::ItemFlags TransactionTableModel::flags(const QModelIndex &index) const -{ - return QAbstractTableModel::flags(index); -} - -QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const -{ - Q_UNUSED(parent); - TransactionRecord *data = priv->index(row); - if(data) - { - return createIndex(row, column, priv->index(row)); - } - else - { - return QModelIndex(); - } -} - diff --git a/json/include/json/json_spirit.h b/json/include/json/json_spirit.h deleted file mode 100644 index ac1879d5b3..0000000000 --- a/json/include/json/json_spirit.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef JSON_SPIRIT -#define JSON_SPIRIT - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include "json_spirit_value.h" -#include "json_spirit_reader.h" -#include "json_spirit_writer.h" -#include "json_spirit_utils.h" - -#endif diff --git a/json/include/json/json_spirit_error_position.h b/json/include/json/json_spirit_error_position.h deleted file mode 100644 index 17208507df..0000000000 --- a/json/include/json/json_spirit_error_position.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef JSON_SPIRIT_ERROR_POSITION -#define JSON_SPIRIT_ERROR_POSITION - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include - -namespace json_spirit -{ - // An Error_position exception is thrown by the "read_or_throw" functions below on finding an error. - // Note the "read_or_throw" functions are around 3 times slower than the standard functions "read" - // functions that return a bool. - // - struct Error_position - { - Error_position(); - Error_position( unsigned int line, unsigned int column, const std::string& reason ); - bool operator==( const Error_position& lhs ) const; - unsigned int line_; - unsigned int column_; - std::string reason_; - }; - - inline Error_position::Error_position() - : line_( 0 ) - , column_( 0 ) - { - } - - inline Error_position::Error_position( unsigned int line, unsigned int column, const std::string& reason ) - : line_( line ) - , column_( column ) - , reason_( reason ) - { - } - - inline bool Error_position::operator==( const Error_position& lhs ) const - { - if( this == &lhs ) return true; - - return ( reason_ == lhs.reason_ ) && - ( line_ == lhs.line_ ) && - ( column_ == lhs.column_ ); -} -} - -#endif diff --git a/json/include/json/json_spirit_reader.h b/json/include/json/json_spirit_reader.h deleted file mode 100644 index 96494a9789..0000000000 --- a/json/include/json/json_spirit_reader.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef JSON_SPIRIT_READER -#define JSON_SPIRIT_READER - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include "json_spirit_value.h" -#include "json_spirit_error_position.h" -#include - -namespace json_spirit -{ - // functions to reads a JSON values - - bool read( const std::string& s, Value& value ); - bool read( std::istream& is, Value& value ); - bool read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); - - void read_or_throw( const std::string& s, Value& value ); - void read_or_throw( std::istream& is, Value& value ); - void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); - -#ifndef BOOST_NO_STD_WSTRING - - bool read( const std::wstring& s, wValue& value ); - bool read( std::wistream& is, wValue& value ); - bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); - - void read_or_throw( const std::wstring& s, wValue& value ); - void read_or_throw( std::wistream& is, wValue& value ); - void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); - -#endif - - bool read( const std::string& s, mValue& value ); - bool read( std::istream& is, mValue& value ); - bool read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); - - void read_or_throw( const std::string& s, mValue& value ); - void read_or_throw( std::istream& is, mValue& value ); - void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); - -#ifndef BOOST_NO_STD_WSTRING - - bool read( const std::wstring& s, wmValue& value ); - bool read( std::wistream& is, wmValue& value ); - bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); - - void read_or_throw( const std::wstring& s, wmValue& value ); - void read_or_throw( std::wistream& is, wmValue& value ); - void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); - -#endif -} - -#endif diff --git a/json/include/json/json_spirit_reader_template.h b/json/include/json/json_spirit_reader_template.h deleted file mode 100644 index 4dec00e6c9..0000000000 --- a/json/include/json/json_spirit_reader_template.h +++ /dev/null @@ -1,612 +0,0 @@ -#ifndef JSON_SPIRIT_READER_TEMPLATE -#define JSON_SPIRIT_READER_TEMPLATE - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#include "json_spirit_value.h" -#include "json_spirit_error_position.h" - -//#define BOOST_SPIRIT_THREADSAFE // uncomment for multithreaded use, requires linking to boost.thread - -#include -#include -#include - -#if BOOST_VERSION >= 103800 - #include - #include - #include - #include - #include - #define spirit_namespace boost::spirit::classic -#else - #include - #include - #include - #include - #include - #define spirit_namespace boost::spirit -#endif - -namespace json_spirit -{ - const spirit_namespace::int_parser < boost::int64_t > int64_p = spirit_namespace::int_parser < boost::int64_t >(); - const spirit_namespace::uint_parser< boost::uint64_t > uint64_p = spirit_namespace::uint_parser< boost::uint64_t >(); - - template< class Iter_type > - bool is_eq( Iter_type first, Iter_type last, const char* c_str ) - { - for( Iter_type i = first; i != last; ++i, ++c_str ) - { - if( *c_str == 0 ) return false; - - if( *i != *c_str ) return false; - } - - return true; - } - - template< class Char_type > - Char_type hex_to_num( const Char_type c ) - { - if( ( c >= '0' ) && ( c <= '9' ) ) return c - '0'; - if( ( c >= 'a' ) && ( c <= 'f' ) ) return c - 'a' + 10; - if( ( c >= 'A' ) && ( c <= 'F' ) ) return c - 'A' + 10; - return 0; - } - - template< class Char_type, class Iter_type > - Char_type hex_str_to_char( Iter_type& begin ) - { - const Char_type c1( *( ++begin ) ); - const Char_type c2( *( ++begin ) ); - - return ( hex_to_num( c1 ) << 4 ) + hex_to_num( c2 ); - } - - template< class Char_type, class Iter_type > - Char_type unicode_str_to_char( Iter_type& begin ) - { - const Char_type c1( *( ++begin ) ); - const Char_type c2( *( ++begin ) ); - const Char_type c3( *( ++begin ) ); - const Char_type c4( *( ++begin ) ); - - return ( hex_to_num( c1 ) << 12 ) + - ( hex_to_num( c2 ) << 8 ) + - ( hex_to_num( c3 ) << 4 ) + - hex_to_num( c4 ); - } - - template< class String_type > - void append_esc_char_and_incr_iter( String_type& s, - typename String_type::const_iterator& begin, - typename String_type::const_iterator end ) - { - typedef typename String_type::value_type Char_type; - - const Char_type c2( *begin ); - - switch( c2 ) - { - case 't': s += '\t'; break; - case 'b': s += '\b'; break; - case 'f': s += '\f'; break; - case 'n': s += '\n'; break; - case 'r': s += '\r'; break; - case '\\': s += '\\'; break; - case '/': s += '/'; break; - case '"': s += '"'; break; - case 'x': - { - if( end - begin >= 3 ) // expecting "xHH..." - { - s += hex_str_to_char< Char_type >( begin ); - } - break; - } - case 'u': - { - if( end - begin >= 5 ) // expecting "uHHHH..." - { - s += unicode_str_to_char< Char_type >( begin ); - } - break; - } - } - } - - template< class String_type > - String_type substitute_esc_chars( typename String_type::const_iterator begin, - typename String_type::const_iterator end ) - { - typedef typename String_type::const_iterator Iter_type; - - if( end - begin < 2 ) return String_type( begin, end ); - - String_type result; - - result.reserve( end - begin ); - - const Iter_type end_minus_1( end - 1 ); - - Iter_type substr_start = begin; - Iter_type i = begin; - - for( ; i < end_minus_1; ++i ) - { - if( *i == '\\' ) - { - result.append( substr_start, i ); - - ++i; // skip the '\' - - append_esc_char_and_incr_iter( result, i, end ); - - substr_start = i + 1; - } - } - - result.append( substr_start, end ); - - return result; - } - - template< class String_type > - String_type get_str_( typename String_type::const_iterator begin, - typename String_type::const_iterator end ) - { - assert( end - begin >= 2 ); - - typedef typename String_type::const_iterator Iter_type; - - Iter_type str_without_quotes( ++begin ); - Iter_type end_without_quotes( --end ); - - return substitute_esc_chars< String_type >( str_without_quotes, end_without_quotes ); - } - - inline std::string get_str( std::string::const_iterator begin, std::string::const_iterator end ) - { - return get_str_< std::string >( begin, end ); - } - - inline std::wstring get_str( std::wstring::const_iterator begin, std::wstring::const_iterator end ) - { - return get_str_< std::wstring >( begin, end ); - } - - template< class String_type, class Iter_type > - String_type get_str( Iter_type begin, Iter_type end ) - { - const String_type tmp( begin, end ); // convert multipass iterators to string iterators - - return get_str( tmp.begin(), tmp.end() ); - } - - // this class's methods get called by the spirit parse resulting - // in the creation of a JSON object or array - // - // NB Iter_type could be a std::string iterator, wstring iterator, a position iterator or a multipass iterator - // - template< class Value_type, class Iter_type > - class Semantic_actions - { - public: - - typedef typename Value_type::Config_type Config_type; - typedef typename Config_type::String_type String_type; - typedef typename Config_type::Object_type Object_type; - typedef typename Config_type::Array_type Array_type; - typedef typename String_type::value_type Char_type; - - Semantic_actions( Value_type& value ) - : value_( value ) - , current_p_( 0 ) - { - } - - void begin_obj( Char_type c ) - { - assert( c == '{' ); - - begin_compound< Object_type >(); - } - - void end_obj( Char_type c ) - { - assert( c == '}' ); - - end_compound(); - } - - void begin_array( Char_type c ) - { - assert( c == '[' ); - - begin_compound< Array_type >(); - } - - void end_array( Char_type c ) - { - assert( c == ']' ); - - end_compound(); - } - - void new_name( Iter_type begin, Iter_type end ) - { - assert( current_p_->type() == obj_type ); - - name_ = get_str< String_type >( begin, end ); - } - - void new_str( Iter_type begin, Iter_type end ) - { - add_to_current( get_str< String_type >( begin, end ) ); - } - - void new_true( Iter_type begin, Iter_type end ) - { - assert( is_eq( begin, end, "true" ) ); - - add_to_current( true ); - } - - void new_false( Iter_type begin, Iter_type end ) - { - assert( is_eq( begin, end, "false" ) ); - - add_to_current( false ); - } - - void new_null( Iter_type begin, Iter_type end ) - { - assert( is_eq( begin, end, "null" ) ); - - add_to_current( Value_type() ); - } - - void new_int( boost::int64_t i ) - { - add_to_current( i ); - } - - void new_uint64( boost::uint64_t ui ) - { - add_to_current( ui ); - } - - void new_real( double d ) - { - add_to_current( d ); - } - - private: - - Semantic_actions& operator=( const Semantic_actions& ); - // to prevent "assignment operator could not be generated" warning - - Value_type* add_first( const Value_type& value ) - { - assert( current_p_ == 0 ); - - value_ = value; - current_p_ = &value_; - return current_p_; - } - - template< class Array_or_obj > - void begin_compound() - { - if( current_p_ == 0 ) - { - add_first( Array_or_obj() ); - } - else - { - stack_.push_back( current_p_ ); - - Array_or_obj new_array_or_obj; // avoid copy by building new array or object in place - - current_p_ = add_to_current( new_array_or_obj ); - } - } - - void end_compound() - { - if( current_p_ != &value_ ) - { - current_p_ = stack_.back(); - - stack_.pop_back(); - } - } - - Value_type* add_to_current( const Value_type& value ) - { - if( current_p_ == 0 ) - { - return add_first( value ); - } - else if( current_p_->type() == array_type ) - { - current_p_->get_array().push_back( value ); - - return ¤t_p_->get_array().back(); - } - - assert( current_p_->type() == obj_type ); - - return &Config_type::add( current_p_->get_obj(), name_, value ); - } - - Value_type& value_; // this is the object or array that is being created - Value_type* current_p_; // the child object or array that is currently being constructed - - std::vector< Value_type* > stack_; // previous child objects and arrays - - String_type name_; // of current name/value pair - }; - - template< typename Iter_type > - void throw_error( spirit_namespace::position_iterator< Iter_type > i, const std::string& reason ) - { - throw Error_position( i.get_position().line, i.get_position().column, reason ); - } - - template< typename Iter_type > - void throw_error( Iter_type i, const std::string& reason ) - { - throw reason; - } - - // the spirit grammer - // - template< class Value_type, class Iter_type > - class Json_grammer : public spirit_namespace::grammar< Json_grammer< Value_type, Iter_type > > - { - public: - - typedef Semantic_actions< Value_type, Iter_type > Semantic_actions_t; - - Json_grammer( Semantic_actions_t& semantic_actions ) - : actions_( semantic_actions ) - { - } - - static void throw_not_value( Iter_type begin, Iter_type end ) - { - throw_error( begin, "not a value" ); - } - - static void throw_not_array( Iter_type begin, Iter_type end ) - { - throw_error( begin, "not an array" ); - } - - static void throw_not_object( Iter_type begin, Iter_type end ) - { - throw_error( begin, "not an object" ); - } - - static void throw_not_pair( Iter_type begin, Iter_type end ) - { - throw_error( begin, "not a pair" ); - } - - static void throw_not_colon( Iter_type begin, Iter_type end ) - { - throw_error( begin, "no colon in pair" ); - } - - static void throw_not_string( Iter_type begin, Iter_type end ) - { - throw_error( begin, "not a string" ); - } - - template< typename ScannerT > - class definition - { - public: - - definition( const Json_grammer& self ) - { - using namespace spirit_namespace; - - typedef typename Value_type::String_type::value_type Char_type; - - // first we convert the semantic action class methods to functors with the - // parameter signature expected by spirit - - typedef boost::function< void( Char_type ) > Char_action; - typedef boost::function< void( Iter_type, Iter_type ) > Str_action; - typedef boost::function< void( double ) > Real_action; - typedef boost::function< void( boost::int64_t ) > Int_action; - typedef boost::function< void( boost::uint64_t ) > Uint64_action; - - Char_action begin_obj ( boost::bind( &Semantic_actions_t::begin_obj, &self.actions_, _1 ) ); - Char_action end_obj ( boost::bind( &Semantic_actions_t::end_obj, &self.actions_, _1 ) ); - Char_action begin_array( boost::bind( &Semantic_actions_t::begin_array, &self.actions_, _1 ) ); - Char_action end_array ( boost::bind( &Semantic_actions_t::end_array, &self.actions_, _1 ) ); - Str_action new_name ( boost::bind( &Semantic_actions_t::new_name, &self.actions_, _1, _2 ) ); - Str_action new_str ( boost::bind( &Semantic_actions_t::new_str, &self.actions_, _1, _2 ) ); - Str_action new_true ( boost::bind( &Semantic_actions_t::new_true, &self.actions_, _1, _2 ) ); - Str_action new_false ( boost::bind( &Semantic_actions_t::new_false, &self.actions_, _1, _2 ) ); - Str_action new_null ( boost::bind( &Semantic_actions_t::new_null, &self.actions_, _1, _2 ) ); - Real_action new_real ( boost::bind( &Semantic_actions_t::new_real, &self.actions_, _1 ) ); - Int_action new_int ( boost::bind( &Semantic_actions_t::new_int, &self.actions_, _1 ) ); - Uint64_action new_uint64 ( boost::bind( &Semantic_actions_t::new_uint64, &self.actions_, _1 ) ); - - // actual grammer - - json_ - = value_ | eps_p[ &throw_not_value ] - ; - - value_ - = string_[ new_str ] - | number_ - | object_ - | array_ - | str_p( "true" ) [ new_true ] - | str_p( "false" )[ new_false ] - | str_p( "null" ) [ new_null ] - ; - - object_ - = ch_p('{')[ begin_obj ] - >> !members_ - >> ( ch_p('}')[ end_obj ] | eps_p[ &throw_not_object ] ) - ; - - members_ - = pair_ >> *( ',' >> pair_ ) - ; - - pair_ - = string_[ new_name ] - >> ( ':' | eps_p[ &throw_not_colon ] ) - >> ( value_ | eps_p[ &throw_not_value ] ) - ; - - array_ - = ch_p('[')[ begin_array ] - >> !elements_ - >> ( ch_p(']')[ end_array ] | eps_p[ &throw_not_array ] ) - ; - - elements_ - = value_ >> *( ',' >> value_ ) - ; - - string_ - = lexeme_d // this causes white space inside a string to be retained - [ - confix_p - ( - '"', - *lex_escape_ch_p, - '"' - ) - ] - ; - - number_ - = strict_real_p[ new_real ] - | int64_p [ new_int ] - | uint64_p [ new_uint64 ] - ; - } - - spirit_namespace::rule< ScannerT > json_, object_, members_, pair_, array_, elements_, value_, string_, number_; - - const spirit_namespace::rule< ScannerT >& start() const { return json_; } - }; - - private: - - Json_grammer& operator=( const Json_grammer& ); // to prevent "assignment operator could not be generated" warning - - Semantic_actions_t& actions_; - }; - - template< class Iter_type, class Value_type > - Iter_type read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) - { - Semantic_actions< Value_type, Iter_type > semantic_actions( value ); - - const spirit_namespace::parse_info< Iter_type > info = - spirit_namespace::parse( begin, end, - Json_grammer< Value_type, Iter_type >( semantic_actions ), - spirit_namespace::space_p ); - - if( !info.hit ) - { - assert( false ); // in theory exception should already have been thrown - throw_error( info.stop, "error" ); - } - - return info.stop; - } - - template< class Iter_type, class Value_type > - void add_posn_iter_and_read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) - { - typedef spirit_namespace::position_iterator< Iter_type > Posn_iter_t; - - const Posn_iter_t posn_begin( begin, end ); - const Posn_iter_t posn_end( end, end ); - - read_range_or_throw( posn_begin, posn_end, value ); - } - - template< class Iter_type, class Value_type > - bool read_range( Iter_type& begin, Iter_type end, Value_type& value ) - { - try - { - begin = read_range_or_throw( begin, end, value ); - - return true; - } - catch( ... ) - { - return false; - } - } - - template< class String_type, class Value_type > - void read_string_or_throw( const String_type& s, Value_type& value ) - { - add_posn_iter_and_read_range_or_throw( s.begin(), s.end(), value ); - } - - template< class String_type, class Value_type > - bool read_string( const String_type& s, Value_type& value ) - { - typename String_type::const_iterator begin = s.begin(); - - return read_range( begin, s.end(), value ); - } - - template< class Istream_type > - struct Multi_pass_iters - { - typedef typename Istream_type::char_type Char_type; - typedef std::istream_iterator< Char_type, Char_type > istream_iter; - typedef spirit_namespace::multi_pass< istream_iter > Mp_iter; - - Multi_pass_iters( Istream_type& is ) - { - is.unsetf( std::ios::skipws ); - - begin_ = spirit_namespace::make_multi_pass( istream_iter( is ) ); - end_ = spirit_namespace::make_multi_pass( istream_iter() ); - } - - Mp_iter begin_; - Mp_iter end_; - }; - - template< class Istream_type, class Value_type > - bool read_stream( Istream_type& is, Value_type& value ) - { - Multi_pass_iters< Istream_type > mp_iters( is ); - - return read_range( mp_iters.begin_, mp_iters.end_, value ); - } - - template< class Istream_type, class Value_type > - void read_stream_or_throw( Istream_type& is, Value_type& value ) - { - const Multi_pass_iters< Istream_type > mp_iters( is ); - - add_posn_iter_and_read_range_or_throw( mp_iters.begin_, mp_iters.end_, value ); - } -} - -#endif diff --git a/json/include/json/json_spirit_stream_reader.h b/json/include/json/json_spirit_stream_reader.h deleted file mode 100644 index 7e59c9adc2..0000000000 --- a/json/include/json/json_spirit_stream_reader.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef JSON_SPIRIT_READ_STREAM -#define JSON_SPIRIT_READ_STREAM - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include "json_spirit_reader_template.h" - -namespace json_spirit -{ - // these classes allows you to read multiple top level contiguous values from a stream, - // the normal stream read functions have a bug that prevent multiple top level values - // from being read unless they are separated by spaces - - template< class Istream_type, class Value_type > - class Stream_reader - { - public: - - Stream_reader( Istream_type& is ) - : iters_( is ) - { - } - - bool read_next( Value_type& value ) - { - return read_range( iters_.begin_, iters_.end_, value ); - } - - private: - - typedef Multi_pass_iters< Istream_type > Mp_iters; - - Mp_iters iters_; - }; - - template< class Istream_type, class Value_type > - class Stream_reader_thrower - { - public: - - Stream_reader_thrower( Istream_type& is ) - : iters_( is ) - , posn_begin_( iters_.begin_, iters_.end_ ) - , posn_end_( iters_.end_, iters_.end_ ) - { - } - - void read_next( Value_type& value ) - { - posn_begin_ = read_range_or_throw( posn_begin_, posn_end_, value ); - } - - private: - - typedef Multi_pass_iters< Istream_type > Mp_iters; - typedef spirit_namespace::position_iterator< typename Mp_iters::Mp_iter > Posn_iter_t; - - Mp_iters iters_; - Posn_iter_t posn_begin_, posn_end_; - }; -} - -#endif diff --git a/json/include/json/json_spirit_utils.h b/json/include/json/json_spirit_utils.h deleted file mode 100644 index 553e3b96a4..0000000000 --- a/json/include/json/json_spirit_utils.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef JSON_SPIRIT_UTILS -#define JSON_SPIRIT_UTILS - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include "json_spirit_value.h" -#include - -namespace json_spirit -{ - template< class Obj_t, class Map_t > - void obj_to_map( const Obj_t& obj, Map_t& mp_obj ) - { - mp_obj.clear(); - - for( typename Obj_t::const_iterator i = obj.begin(); i != obj.end(); ++i ) - { - mp_obj[ i->name_ ] = i->value_; - } - } - - template< class Obj_t, class Map_t > - void map_to_obj( const Map_t& mp_obj, Obj_t& obj ) - { - obj.clear(); - - for( typename Map_t::const_iterator i = mp_obj.begin(); i != mp_obj.end(); ++i ) - { - obj.push_back( typename Obj_t::value_type( i->first, i->second ) ); - } - } - - typedef std::map< std::string, Value > Mapped_obj; - -#ifndef BOOST_NO_STD_WSTRING - typedef std::map< std::wstring, wValue > wMapped_obj; -#endif - - template< class Object_type, class String_type > - const typename Object_type::value_type::Value_type& find_value( const Object_type& obj, const String_type& name ) - { - for( typename Object_type::const_iterator i = obj.begin(); i != obj.end(); ++i ) - { - if( i->name_ == name ) - { - return i->value_; - } - } - - return Object_type::value_type::Value_type::null; - } -} - -#endif diff --git a/json/include/json/json_spirit_value.h b/json/include/json/json_spirit_value.h deleted file mode 100644 index 7e83a2a7e3..0000000000 --- a/json/include/json/json_spirit_value.h +++ /dev/null @@ -1,534 +0,0 @@ -#ifndef JSON_SPIRIT_VALUE -#define JSON_SPIRIT_VALUE - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace json_spirit -{ - enum Value_type{ obj_type, array_type, str_type, bool_type, int_type, real_type, null_type }; - static const char* Value_type_name[]={"obj", "array", "str", "bool", "int", "real", "null"}; - - template< class Config > // Config determines whether the value uses std::string or std::wstring and - // whether JSON Objects are represented as vectors or maps - class Value_impl - { - public: - - typedef Config Config_type; - typedef typename Config::String_type String_type; - typedef typename Config::Object_type Object; - typedef typename Config::Array_type Array; - typedef typename String_type::const_pointer Const_str_ptr; // eg const char* - - Value_impl(); // creates null value - Value_impl( Const_str_ptr value ); - Value_impl( const String_type& value ); - Value_impl( const Object& value ); - Value_impl( const Array& value ); - Value_impl( bool value ); - Value_impl( int value ); - Value_impl( boost::int64_t value ); - Value_impl( boost::uint64_t value ); - Value_impl( double value ); - - Value_impl( const Value_impl& other ); - - bool operator==( const Value_impl& lhs ) const; - - Value_impl& operator=( const Value_impl& lhs ); - - Value_type type() const; - - bool is_uint64() const; - bool is_null() const; - - const String_type& get_str() const; - const Object& get_obj() const; - const Array& get_array() const; - bool get_bool() const; - int get_int() const; - boost::int64_t get_int64() const; - boost::uint64_t get_uint64() const; - double get_real() const; - - Object& get_obj(); - Array& get_array(); - - template< typename T > T get_value() const; // example usage: int i = value.get_value< int >(); - // or double d = value.get_value< double >(); - - static const Value_impl null; - - private: - - void check_type( const Value_type vtype ) const; - - typedef boost::variant< String_type, - boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >, - bool, boost::int64_t, double > Variant; - - Value_type type_; - Variant v_; - bool is_uint64_; - }; - - // vector objects - - template< class Config > - struct Pair_impl - { - typedef typename Config::String_type String_type; - typedef typename Config::Value_type Value_type; - - Pair_impl( const String_type& name, const Value_type& value ); - - bool operator==( const Pair_impl& lhs ) const; - - String_type name_; - Value_type value_; - }; - - template< class String > - struct Config_vector - { - typedef String String_type; - typedef Value_impl< Config_vector > Value_type; - typedef Pair_impl < Config_vector > Pair_type; - typedef std::vector< Value_type > Array_type; - typedef std::vector< Pair_type > Object_type; - - static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) - { - obj.push_back( Pair_type( name , value ) ); - - return obj.back().value_; - } - - static String_type get_name( const Pair_type& pair ) - { - return pair.name_; - } - - static Value_type get_value( const Pair_type& pair ) - { - return pair.value_; - } - }; - - // typedefs for ASCII - - typedef Config_vector< std::string > Config; - - typedef Config::Value_type Value; - typedef Config::Pair_type Pair; - typedef Config::Object_type Object; - typedef Config::Array_type Array; - - // typedefs for Unicode - -#ifndef BOOST_NO_STD_WSTRING - - typedef Config_vector< std::wstring > wConfig; - - typedef wConfig::Value_type wValue; - typedef wConfig::Pair_type wPair; - typedef wConfig::Object_type wObject; - typedef wConfig::Array_type wArray; -#endif - - // map objects - - template< class String > - struct Config_map - { - typedef String String_type; - typedef Value_impl< Config_map > Value_type; - typedef std::vector< Value_type > Array_type; - typedef std::map< String_type, Value_type > Object_type; - typedef typename Object_type::value_type Pair_type; - - static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) - { - return obj[ name ] = value; - } - - static String_type get_name( const Pair_type& pair ) - { - return pair.first; - } - - static Value_type get_value( const Pair_type& pair ) - { - return pair.second; - } - }; - - // typedefs for ASCII - - typedef Config_map< std::string > mConfig; - - typedef mConfig::Value_type mValue; - typedef mConfig::Object_type mObject; - typedef mConfig::Array_type mArray; - - // typedefs for Unicode - -#ifndef BOOST_NO_STD_WSTRING - - typedef Config_map< std::wstring > wmConfig; - - typedef wmConfig::Value_type wmValue; - typedef wmConfig::Object_type wmObject; - typedef wmConfig::Array_type wmArray; - -#endif - - /////////////////////////////////////////////////////////////////////////////////////////////// - // - // implementation - - template< class Config > - const Value_impl< Config > Value_impl< Config >::null; - - template< class Config > - Value_impl< Config >::Value_impl() - : type_( null_type ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( const Const_str_ptr value ) - : type_( str_type ) - , v_( String_type( value ) ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( const String_type& value ) - : type_( str_type ) - , v_( value ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( const Object& value ) - : type_( obj_type ) - , v_( value ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( const Array& value ) - : type_( array_type ) - , v_( value ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( bool value ) - : type_( bool_type ) - , v_( value ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( int value ) - : type_( int_type ) - , v_( static_cast< boost::int64_t >( value ) ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( boost::int64_t value ) - : type_( int_type ) - , v_( value ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( boost::uint64_t value ) - : type_( int_type ) - , v_( static_cast< boost::int64_t >( value ) ) - , is_uint64_( true ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( double value ) - : type_( real_type ) - , v_( value ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( const Value_impl< Config >& other ) - : type_( other.type() ) - , v_( other.v_ ) - , is_uint64_( other.is_uint64_ ) - { - } - - template< class Config > - Value_impl< Config >& Value_impl< Config >::operator=( const Value_impl& lhs ) - { - Value_impl tmp( lhs ); - - std::swap( type_, tmp.type_ ); - std::swap( v_, tmp.v_ ); - std::swap( is_uint64_, tmp.is_uint64_ ); - - return *this; - } - - template< class Config > - bool Value_impl< Config >::operator==( const Value_impl& lhs ) const - { - if( this == &lhs ) return true; - - if( type() != lhs.type() ) return false; - - return v_ == lhs.v_; - } - - template< class Config > - Value_type Value_impl< Config >::type() const - { - return type_; - } - - template< class Config > - bool Value_impl< Config >::is_uint64() const - { - return is_uint64_; - } - - template< class Config > - bool Value_impl< Config >::is_null() const - { - return type() == null_type; - } - - template< class Config > - void Value_impl< Config >::check_type( const Value_type vtype ) const - { - if( type() != vtype ) - { - std::ostringstream os; - - ///// Bitcoin: Tell the types by name instead of by number - os << "value is type " << Value_type_name[type()] << ", expected " << Value_type_name[vtype]; - - throw std::runtime_error( os.str() ); - } - } - - template< class Config > - const typename Config::String_type& Value_impl< Config >::get_str() const - { - check_type( str_type ); - - return *boost::get< String_type >( &v_ ); - } - - template< class Config > - const typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() const - { - check_type( obj_type ); - - return *boost::get< Object >( &v_ ); - } - - template< class Config > - const typename Value_impl< Config >::Array& Value_impl< Config >::get_array() const - { - check_type( array_type ); - - return *boost::get< Array >( &v_ ); - } - - template< class Config > - bool Value_impl< Config >::get_bool() const - { - check_type( bool_type ); - - return boost::get< bool >( v_ ); - } - - template< class Config > - int Value_impl< Config >::get_int() const - { - check_type( int_type ); - - return static_cast< int >( get_int64() ); - } - - template< class Config > - boost::int64_t Value_impl< Config >::get_int64() const - { - check_type( int_type ); - - return boost::get< boost::int64_t >( v_ ); - } - - template< class Config > - boost::uint64_t Value_impl< Config >::get_uint64() const - { - check_type( int_type ); - - return static_cast< boost::uint64_t >( get_int64() ); - } - - template< class Config > - double Value_impl< Config >::get_real() const - { - if( type() == int_type ) - { - return is_uint64() ? static_cast< double >( get_uint64() ) - : static_cast< double >( get_int64() ); - } - - check_type( real_type ); - - return boost::get< double >( v_ ); - } - - template< class Config > - typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() - { - check_type( obj_type ); - - return *boost::get< Object >( &v_ ); - } - - template< class Config > - typename Value_impl< Config >::Array& Value_impl< Config >::get_array() - { - check_type( array_type ); - - return *boost::get< Array >( &v_ ); - } - - template< class Config > - Pair_impl< Config >::Pair_impl( const String_type& name, const Value_type& value ) - : name_( name ) - , value_( value ) - { - } - - template< class Config > - bool Pair_impl< Config >::operator==( const Pair_impl< Config >& lhs ) const - { - if( this == &lhs ) return true; - - return ( name_ == lhs.name_ ) && ( value_ == lhs.value_ ); - } - - // converts a C string, ie. 8 bit char array, to a string object - // - template < class String_type > - String_type to_str( const char* c_str ) - { - String_type result; - - for( const char* p = c_str; *p != 0; ++p ) - { - result += *p; - } - - return result; - } - - // - - namespace internal_ - { - template< typename T > - struct Type_to_type - { - }; - - template< class Value > - int get_value( const Value& value, Type_to_type< int > ) - { - return value.get_int(); - } - - template< class Value > - boost::int64_t get_value( const Value& value, Type_to_type< boost::int64_t > ) - { - return value.get_int64(); - } - - template< class Value > - boost::uint64_t get_value( const Value& value, Type_to_type< boost::uint64_t > ) - { - return value.get_uint64(); - } - - template< class Value > - double get_value( const Value& value, Type_to_type< double > ) - { - return value.get_real(); - } - - template< class Value > - typename Value::String_type get_value( const Value& value, Type_to_type< typename Value::String_type > ) - { - return value.get_str(); - } - - template< class Value > - typename Value::Array get_value( const Value& value, Type_to_type< typename Value::Array > ) - { - return value.get_array(); - } - - template< class Value > - typename Value::Object get_value( const Value& value, Type_to_type< typename Value::Object > ) - { - return value.get_obj(); - } - - template< class Value > - bool get_value( const Value& value, Type_to_type< bool > ) - { - return value.get_bool(); - } - } - - template< class Config > - template< typename T > - T Value_impl< Config >::get_value() const - { - return internal_::get_value( *this, internal_::Type_to_type< T >() ); - } -} - -#endif diff --git a/json/include/json/json_spirit_writer.h b/json/include/json/json_spirit_writer.h deleted file mode 100644 index 52e14068e7..0000000000 --- a/json/include/json/json_spirit_writer.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef JSON_SPIRIT_WRITER -#define JSON_SPIRIT_WRITER - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include "json_spirit_value.h" -#include - -namespace json_spirit -{ - // functions to convert JSON Values to text, - // the "formatted" versions add whitespace to format the output nicely - - void write ( const Value& value, std::ostream& os ); - void write_formatted( const Value& value, std::ostream& os ); - std::string write ( const Value& value ); - std::string write_formatted( const Value& value ); - -#ifndef BOOST_NO_STD_WSTRING - - void write ( const wValue& value, std::wostream& os ); - void write_formatted( const wValue& value, std::wostream& os ); - std::wstring write ( const wValue& value ); - std::wstring write_formatted( const wValue& value ); - -#endif - - void write ( const mValue& value, std::ostream& os ); - void write_formatted( const mValue& value, std::ostream& os ); - std::string write ( const mValue& value ); - std::string write_formatted( const mValue& value ); - -#ifndef BOOST_NO_STD_WSTRING - - void write ( const wmValue& value, std::wostream& os ); - void write_formatted( const wmValue& value, std::wostream& os ); - std::wstring write ( const wmValue& value ); - std::wstring write_formatted( const wmValue& value ); - -#endif -} - -#endif diff --git a/json/include/json/json_spirit_writer_template.h b/json/include/json/json_spirit_writer_template.h deleted file mode 100644 index 28c49ddc64..0000000000 --- a/json/include/json/json_spirit_writer_template.h +++ /dev/null @@ -1,248 +0,0 @@ -#ifndef JSON_SPIRIT_WRITER_TEMPLATE -#define JSON_SPIRIT_WRITER_TEMPLATE - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#include "json_spirit_value.h" - -#include -#include -#include - -namespace json_spirit -{ - inline char to_hex_char( unsigned int c ) - { - assert( c <= 0xF ); - - const char ch = static_cast< char >( c ); - - if( ch < 10 ) return '0' + ch; - - return 'A' - 10 + ch; - } - - template< class String_type > - String_type non_printable_to_string( unsigned int c ) - { - typedef typename String_type::value_type Char_type; - - String_type result( 6, '\\' ); - - result[1] = 'u'; - - result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4; - result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4; - result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4; - result[ 2 ] = to_hex_char( c & 0x000F ); - - return result; - } - - template< typename Char_type, class String_type > - bool add_esc_char( Char_type c, String_type& s ) - { - switch( c ) - { - case '"': s += to_str< String_type >( "\\\"" ); return true; - case '\\': s += to_str< String_type >( "\\\\" ); return true; - case '\b': s += to_str< String_type >( "\\b" ); return true; - case '\f': s += to_str< String_type >( "\\f" ); return true; - case '\n': s += to_str< String_type >( "\\n" ); return true; - case '\r': s += to_str< String_type >( "\\r" ); return true; - case '\t': s += to_str< String_type >( "\\t" ); return true; - } - - return false; - } - - template< class String_type > - String_type add_esc_chars( const String_type& s ) - { - typedef typename String_type::const_iterator Iter_type; - typedef typename String_type::value_type Char_type; - - String_type result; - - const Iter_type end( s.end() ); - - for( Iter_type i = s.begin(); i != end; ++i ) - { - const Char_type c( *i ); - - if( add_esc_char( c, result ) ) continue; - - const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c ); - - if( iswprint( unsigned_c ) ) - { - result += c; - } - else - { - result += non_printable_to_string< String_type >( unsigned_c ); - } - } - - return result; - } - - // this class generates the JSON text, - // it keeps track of the indentation level etc. - // - template< class Value_type, class Ostream_type > - class Generator - { - typedef typename Value_type::Config_type Config_type; - typedef typename Config_type::String_type String_type; - typedef typename Config_type::Object_type Object_type; - typedef typename Config_type::Array_type Array_type; - typedef typename String_type::value_type Char_type; - typedef typename Object_type::value_type Obj_member_type; - - public: - - Generator( const Value_type& value, Ostream_type& os, bool pretty ) - : os_( os ) - , indentation_level_( 0 ) - , pretty_( pretty ) - { - output( value ); - } - - private: - - void output( const Value_type& value ) - { - switch( value.type() ) - { - case obj_type: output( value.get_obj() ); break; - case array_type: output( value.get_array() ); break; - case str_type: output( value.get_str() ); break; - case bool_type: output( value.get_bool() ); break; - case int_type: output_int( value ); break; - - /// Bitcoin: Added std::fixed and changed precision from 16 to 8 - case real_type: os_ << std::showpoint << std::fixed << std::setprecision(8) - << value.get_real(); break; - - case null_type: os_ << "null"; break; - default: assert( false ); - } - } - - void output( const Object_type& obj ) - { - output_array_or_obj( obj, '{', '}' ); - } - - void output( const Array_type& arr ) - { - output_array_or_obj( arr, '[', ']' ); - } - - void output( const Obj_member_type& member ) - { - output( Config_type::get_name( member ) ); space(); - os_ << ':'; space(); - output( Config_type::get_value( member ) ); - } - - void output_int( const Value_type& value ) - { - if( value.is_uint64() ) - { - os_ << value.get_uint64(); - } - else - { - os_ << value.get_int64(); - } - } - - void output( const String_type& s ) - { - os_ << '"' << add_esc_chars( s ) << '"'; - } - - void output( bool b ) - { - os_ << to_str< String_type >( b ? "true" : "false" ); - } - - template< class T > - void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char ) - { - os_ << start_char; new_line(); - - ++indentation_level_; - - for( typename T::const_iterator i = t.begin(); i != t.end(); ++i ) - { - indent(); output( *i ); - - typename T::const_iterator next = i; - - if( ++next != t.end()) - { - os_ << ','; - } - - new_line(); - } - - --indentation_level_; - - indent(); os_ << end_char; - } - - void indent() - { - if( !pretty_ ) return; - - for( int i = 0; i < indentation_level_; ++i ) - { - os_ << " "; - } - } - - void space() - { - if( pretty_ ) os_ << ' '; - } - - void new_line() - { - if( pretty_ ) os_ << '\n'; - } - - Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning - - Ostream_type& os_; - int indentation_level_; - bool pretty_; - }; - - template< class Value_type, class Ostream_type > - void write_stream( const Value_type& value, Ostream_type& os, bool pretty ) - { - Generator< Value_type, Ostream_type >( value, os, pretty ); - } - - template< class Value_type > - typename Value_type::String_type write_string( const Value_type& value, bool pretty ) - { - typedef typename Value_type::String_type::value_type Char_type; - - std::basic_ostringstream< Char_type > os; - - write_stream( value, os, pretty ); - - return os.str(); - } -} - -#endif diff --git a/json/src/json_spirit_reader.cpp b/json/src/json_spirit_reader.cpp deleted file mode 100644 index 3469bf17af..0000000000 --- a/json/src/json_spirit_reader.cpp +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#include "json/json_spirit_reader.h" -#include "json/json_spirit_reader_template.h" - -using namespace json_spirit; - -bool json_spirit::read( const std::string& s, Value& value ) -{ - return read_string( s, value ); -} - -void json_spirit::read_or_throw( const std::string& s, Value& value ) -{ - read_string_or_throw( s, value ); -} - -bool json_spirit::read( std::istream& is, Value& value ) -{ - return read_stream( is, value ); -} - -void json_spirit::read_or_throw( std::istream& is, Value& value ) -{ - read_stream_or_throw( is, value ); -} - -bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) -{ - return read_range( begin, end, value ); -} - -void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) -{ - begin = read_range_or_throw( begin, end, value ); -} - -#ifndef BOOST_NO_STD_WSTRING - -bool json_spirit::read( const std::wstring& s, wValue& value ) -{ - return read_string( s, value ); -} - -void json_spirit::read_or_throw( const std::wstring& s, wValue& value ) -{ - read_string_or_throw( s, value ); -} - -bool json_spirit::read( std::wistream& is, wValue& value ) -{ - return read_stream( is, value ); -} - -void json_spirit::read_or_throw( std::wistream& is, wValue& value ) -{ - read_stream_or_throw( is, value ); -} - -bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) -{ - return read_range( begin, end, value ); -} - -void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) -{ - begin = read_range_or_throw( begin, end, value ); -} - -#endif - -bool json_spirit::read( const std::string& s, mValue& value ) -{ - return read_string( s, value ); -} - -void json_spirit::read_or_throw( const std::string& s, mValue& value ) -{ - read_string_or_throw( s, value ); -} - -bool json_spirit::read( std::istream& is, mValue& value ) -{ - return read_stream( is, value ); -} - -void json_spirit::read_or_throw( std::istream& is, mValue& value ) -{ - read_stream_or_throw( is, value ); -} - -bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) -{ - return read_range( begin, end, value ); -} - -void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) -{ - begin = read_range_or_throw( begin, end, value ); -} - -#ifndef BOOST_NO_STD_WSTRING - -bool json_spirit::read( const std::wstring& s, wmValue& value ) -{ - return read_string( s, value ); -} - -void json_spirit::read_or_throw( const std::wstring& s, wmValue& value ) -{ - read_string_or_throw( s, value ); -} - -bool json_spirit::read( std::wistream& is, wmValue& value ) -{ - return read_stream( is, value ); -} - -void json_spirit::read_or_throw( std::wistream& is, wmValue& value ) -{ - read_stream_or_throw( is, value ); -} - -bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) -{ - return read_range( begin, end, value ); -} - -void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) -{ - begin = read_range_or_throw( begin, end, value ); -} - -#endif diff --git a/json/src/json_spirit_value.cpp b/json/src/json_spirit_value.cpp deleted file mode 100644 index 2d7236855d..0000000000 --- a/json/src/json_spirit_value.cpp +++ /dev/null @@ -1,8 +0,0 @@ -/* Copyright (c) 2007 John W Wilkinson - - This source code can be used for any purpose as long as - this comment is retained. */ - -// json spirit version 2.00 - -#include "json/json_spirit_value.h" diff --git a/json/src/json_spirit_writer.cpp b/json/src/json_spirit_writer.cpp deleted file mode 100644 index 32dfeb2c6c..0000000000 --- a/json/src/json_spirit_writer.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#include "json/json_spirit_writer.h" -#include "json/json_spirit_writer_template.h" - -void json_spirit::write( const Value& value, std::ostream& os ) -{ - write_stream( value, os, false ); -} - -void json_spirit::write_formatted( const Value& value, std::ostream& os ) -{ - write_stream( value, os, true ); -} - -std::string json_spirit::write( const Value& value ) -{ - return write_string( value, false ); -} - -std::string json_spirit::write_formatted( const Value& value ) -{ - return write_string( value, true ); -} - -#ifndef BOOST_NO_STD_WSTRING - -void json_spirit::write( const wValue& value, std::wostream& os ) -{ - write_stream( value, os, false ); -} - -void json_spirit::write_formatted( const wValue& value, std::wostream& os ) -{ - write_stream( value, os, true ); -} - -std::wstring json_spirit::write( const wValue& value ) -{ - return write_string( value, false ); -} - -std::wstring json_spirit::write_formatted( const wValue& value ) -{ - return write_string( value, true ); -} - -#endif - -void json_spirit::write( const mValue& value, std::ostream& os ) -{ - write_stream( value, os, false ); -} - -void json_spirit::write_formatted( const mValue& value, std::ostream& os ) -{ - write_stream( value, os, true ); -} - -std::string json_spirit::write( const mValue& value ) -{ - return write_string( value, false ); -} - -std::string json_spirit::write_formatted( const mValue& value ) -{ - return write_string( value, true ); -} - -#ifndef BOOST_NO_STD_WSTRING - -void json_spirit::write( const wmValue& value, std::wostream& os ) -{ - write_stream( value, os, false ); -} - -void json_spirit::write_formatted( const wmValue& value, std::wostream& os ) -{ - write_stream( value, os, true ); -} - -std::wstring json_spirit::write( const wmValue& value ) -{ - return write_string( value, false ); -} - -std::wstring json_spirit::write_formatted( const wmValue& value ) -{ - return write_string( value, true ); -} - -#endif diff --git a/src/base58.h b/src/base58.h new file mode 100644 index 0000000000..580bd3fc63 --- /dev/null +++ b/src/base58.h @@ -0,0 +1,208 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + + +// +// Why base-58 instead of standard base-64 encoding? +// - Don't want 0OIl characters that look the same in some fonts and +// could be used to create visually identical looking account numbers. +// - A string with non-alphanumeric characters is not as easily accepted as an account number. +// - E-mail usually won't line-break if there's no punctuation to break at. +// - Doubleclicking selects the whole number as one word if it's all alphanumeric. +// +#ifndef BITCOIN_BASE58_H +#define BITCOIN_BASE58_H + +#include +#include +#include "bignum.h" + +static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + + +inline std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) +{ + CAutoBN_CTX pctx; + CBigNum bn58 = 58; + CBigNum bn0 = 0; + + // Convert big endian data to little endian + // Extra zero at the end make sure bignum will interpret as a positive number + std::vector vchTmp(pend-pbegin+1, 0); + reverse_copy(pbegin, pend, vchTmp.begin()); + + // Convert little endian data to bignum + CBigNum bn; + bn.setvch(vchTmp); + + // Convert bignum to std::string + std::string str; + str.reserve((pend - pbegin) * 138 / 100 + 1); + CBigNum dv; + CBigNum rem; + while (bn > bn0) + { + if (!BN_div(&dv, &rem, &bn, &bn58, pctx)) + throw bignum_error("EncodeBase58 : BN_div failed"); + bn = dv; + unsigned int c = rem.getulong(); + str += pszBase58[c]; + } + + // Leading zeroes encoded as base58 zeros + for (const unsigned char* p = pbegin; p < pend && *p == 0; p++) + str += pszBase58[0]; + + // Convert little endian std::string to big endian + reverse(str.begin(), str.end()); + return str; +} + +inline std::string EncodeBase58(const std::vector& vch) +{ + return EncodeBase58(&vch[0], &vch[0] + vch.size()); +} + +inline bool DecodeBase58(const char* psz, std::vector& vchRet) +{ + CAutoBN_CTX pctx; + vchRet.clear(); + CBigNum bn58 = 58; + CBigNum bn = 0; + CBigNum bnChar; + while (isspace(*psz)) + psz++; + + // Convert big endian string to bignum + for (const char* p = psz; *p; p++) + { + const char* p1 = strchr(pszBase58, *p); + if (p1 == NULL) + { + while (isspace(*p)) + p++; + if (*p != '\0') + return false; + break; + } + bnChar.setulong(p1 - pszBase58); + if (!BN_mul(&bn, &bn, &bn58, pctx)) + throw bignum_error("DecodeBase58 : BN_mul failed"); + bn += bnChar; + } + + // Get bignum as little endian data + std::vector vchTmp = bn.getvch(); + + // Trim off sign byte if present + if (vchTmp.size() >= 2 && vchTmp.end()[-1] == 0 && vchTmp.end()[-2] >= 0x80) + vchTmp.erase(vchTmp.end()-1); + + // Restore leading zeros + int nLeadingZeros = 0; + for (const char* p = psz; *p == pszBase58[0]; p++) + nLeadingZeros++; + vchRet.assign(nLeadingZeros + vchTmp.size(), 0); + + // Convert little endian data to big endian + reverse_copy(vchTmp.begin(), vchTmp.end(), vchRet.end() - vchTmp.size()); + return true; +} + +inline bool DecodeBase58(const std::string& str, std::vector& vchRet) +{ + return DecodeBase58(str.c_str(), vchRet); +} + + + + + +inline std::string EncodeBase58Check(const std::vector& vchIn) +{ + // add 4-byte hash check to the end + std::vector vch(vchIn); + uint256 hash = Hash(vch.begin(), vch.end()); + vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4); + return EncodeBase58(vch); +} + +inline bool DecodeBase58Check(const char* psz, std::vector& vchRet) +{ + if (!DecodeBase58(psz, vchRet)) + return false; + if (vchRet.size() < 4) + { + vchRet.clear(); + return false; + } + uint256 hash = Hash(vchRet.begin(), vchRet.end()-4); + if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) + { + vchRet.clear(); + return false; + } + vchRet.resize(vchRet.size()-4); + return true; +} + +inline bool DecodeBase58Check(const std::string& str, std::vector& vchRet) +{ + return DecodeBase58Check(str.c_str(), vchRet); +} + + + + + + +#define ADDRESSVERSION ((unsigned char)(fTestNet ? 111 : 0)) + +inline std::string Hash160ToAddress(uint160 hash160) +{ + // add 1-byte version number to the front + std::vector vch(1, ADDRESSVERSION); + vch.insert(vch.end(), UBEGIN(hash160), UEND(hash160)); + return EncodeBase58Check(vch); +} + +inline bool AddressToHash160(const char* psz, uint160& hash160Ret) +{ + std::vector vch; + if (!DecodeBase58Check(psz, vch)) + return false; + if (vch.empty()) + return false; + unsigned char nVersion = vch[0]; + if (vch.size() != sizeof(hash160Ret) + 1) + return false; + memcpy(&hash160Ret, &vch[1], sizeof(hash160Ret)); + return (nVersion <= ADDRESSVERSION); +} + +inline bool AddressToHash160(const std::string& str, uint160& hash160Ret) +{ + return AddressToHash160(str.c_str(), hash160Ret); +} + +inline bool IsValidBitcoinAddress(const char* psz) +{ + uint160 hash160; + return AddressToHash160(psz, hash160); +} + +inline bool IsValidBitcoinAddress(const std::string& str) +{ + return IsValidBitcoinAddress(str.c_str()); +} + + + + +inline std::string PubKeyToAddress(const std::vector& vchPubKey) +{ + return Hash160ToAddress(Hash160(vchPubKey)); +} + +#endif diff --git a/src/bignum.h b/src/bignum.h new file mode 100644 index 0000000000..5b4c78e7fa --- /dev/null +++ b/src/bignum.h @@ -0,0 +1,534 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_BIGNUM_H +#define BITCOIN_BIGNUM_H + +#include +#include +#include + +#include "util.h" + +class bignum_error : public std::runtime_error +{ +public: + explicit bignum_error(const std::string& str) : std::runtime_error(str) {} +}; + + + +class CAutoBN_CTX +{ +protected: + BN_CTX* pctx; + BN_CTX* operator=(BN_CTX* pnew) { return pctx = pnew; } + +public: + CAutoBN_CTX() + { + pctx = BN_CTX_new(); + if (pctx == NULL) + throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned NULL"); + } + + ~CAutoBN_CTX() + { + if (pctx != NULL) + BN_CTX_free(pctx); + } + + operator BN_CTX*() { return pctx; } + BN_CTX& operator*() { return *pctx; } + BN_CTX** operator&() { return &pctx; } + bool operator!() { return (pctx == NULL); } +}; + + + +class CBigNum : public BIGNUM +{ +public: + CBigNum() + { + BN_init(this); + } + + CBigNum(const CBigNum& b) + { + BN_init(this); + if (!BN_copy(this, &b)) + { + BN_clear_free(this); + throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_copy failed"); + } + } + + CBigNum& operator=(const CBigNum& b) + { + if (!BN_copy(this, &b)) + throw bignum_error("CBigNum::operator= : BN_copy failed"); + return (*this); + } + + ~CBigNum() + { + BN_clear_free(this); + } + + CBigNum(char n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(short n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(long n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int64 n) { BN_init(this); setint64(n); } + CBigNum(unsigned char n) { BN_init(this); setulong(n); } + CBigNum(unsigned short n) { BN_init(this); setulong(n); } + CBigNum(unsigned int n) { BN_init(this); setulong(n); } + CBigNum(unsigned long n) { BN_init(this); setulong(n); } + CBigNum(uint64 n) { BN_init(this); setuint64(n); } + explicit CBigNum(uint256 n) { BN_init(this); setuint256(n); } + + explicit CBigNum(const std::vector& vch) + { + BN_init(this); + setvch(vch); + } + + void setulong(unsigned long n) + { + if (!BN_set_word(this, n)) + throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed"); + } + + unsigned long getulong() const + { + return BN_get_word(this); + } + + unsigned int getuint() const + { + return BN_get_word(this); + } + + int getint() const + { + unsigned long n = BN_get_word(this); + if (!BN_is_negative(this)) + return (n > INT_MAX ? INT_MAX : n); + else + return (n > INT_MAX ? INT_MIN : -(int)n); + } + + void setint64(int64 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fNegative = false; + if (n < (int64)0) + { + n = -n; + fNegative = true; + } + bool fLeadingZeroes = true; + for (int i = 0; i < 8; i++) + { + unsigned char c = (n >> 56) & 0xff; + n <<= 8; + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = (fNegative ? 0x80 : 0); + else if (fNegative) + c |= 0x80; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + void setuint64(uint64 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fLeadingZeroes = true; + for (int i = 0; i < 8; i++) + { + unsigned char c = (n >> 56) & 0xff; + n <<= 8; + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + void setuint256(uint256 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fLeadingZeroes = true; + unsigned char* pbegin = (unsigned char*)&n; + unsigned char* psrc = pbegin + sizeof(n); + while (psrc != pbegin) + { + unsigned char c = *(--psrc); + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize >> 0) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + uint256 getuint256() + { + unsigned int nSize = BN_bn2mpi(this, NULL); + if (nSize < 4) + return 0; + std::vector vch(nSize); + BN_bn2mpi(this, &vch[0]); + if (vch.size() > 4) + vch[4] &= 0x7f; + uint256 n = 0; + for (int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) + ((unsigned char*)&n)[i] = vch[j]; + return n; + } + + void setvch(const std::vector& vch) + { + std::vector vch2(vch.size() + 4); + unsigned int nSize = vch.size(); + vch2[0] = (nSize >> 24) & 0xff; + vch2[1] = (nSize >> 16) & 0xff; + vch2[2] = (nSize >> 8) & 0xff; + vch2[3] = (nSize >> 0) & 0xff; + reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4); + BN_mpi2bn(&vch2[0], vch2.size(), this); + } + + std::vector getvch() const + { + unsigned int nSize = BN_bn2mpi(this, NULL); + if (nSize < 4) + return std::vector(); + std::vector vch(nSize); + BN_bn2mpi(this, &vch[0]); + vch.erase(vch.begin(), vch.begin() + 4); + reverse(vch.begin(), vch.end()); + return vch; + } + + CBigNum& SetCompact(unsigned int nCompact) + { + unsigned int nSize = nCompact >> 24; + std::vector vch(4 + nSize); + vch[3] = nSize; + if (nSize >= 1) vch[4] = (nCompact >> 16) & 0xff; + if (nSize >= 2) vch[5] = (nCompact >> 8) & 0xff; + if (nSize >= 3) vch[6] = (nCompact >> 0) & 0xff; + BN_mpi2bn(&vch[0], vch.size(), this); + return *this; + } + + unsigned int GetCompact() const + { + unsigned int nSize = BN_bn2mpi(this, NULL); + std::vector vch(nSize); + nSize -= 4; + BN_bn2mpi(this, &vch[0]); + unsigned int nCompact = nSize << 24; + if (nSize >= 1) nCompact |= (vch[4] << 16); + if (nSize >= 2) nCompact |= (vch[5] << 8); + if (nSize >= 3) nCompact |= (vch[6] << 0); + return nCompact; + } + + void SetHex(const std::string& str) + { + // skip 0x + const char* psz = str.c_str(); + while (isspace(*psz)) + psz++; + bool fNegative = false; + if (*psz == '-') + { + fNegative = true; + psz++; + } + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + while (isspace(*psz)) + psz++; + + // hex string to bignum + static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; + *this = 0; + while (isxdigit(*psz)) + { + *this <<= 4; + int n = phexdigit[*psz++]; + *this += n; + } + if (fNegative) + *this = 0 - *this; + } + + std::string ToString(int nBase=10) const + { + CAutoBN_CTX pctx; + CBigNum bnBase = nBase; + CBigNum bn0 = 0; + std::string str; + CBigNum bn = *this; + BN_set_negative(&bn, false); + CBigNum dv; + CBigNum rem; + if (BN_cmp(&bn, &bn0) == 0) + return "0"; + while (BN_cmp(&bn, &bn0) > 0) + { + if (!BN_div(&dv, &rem, &bn, &bnBase, pctx)) + throw bignum_error("CBigNum::ToString() : BN_div failed"); + bn = dv; + unsigned int c = rem.getulong(); + str += "0123456789abcdef"[c]; + } + if (BN_is_negative(this)) + str += "-"; + reverse(str.begin(), str.end()); + return str; + } + + std::string GetHex() const + { + return ToString(16); + } + + unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const + { + return ::GetSerializeSize(getvch(), nType, nVersion); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const + { + ::Serialize(s, getvch(), nType, nVersion); + } + + template + void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) + { + std::vector vch; + ::Unserialize(s, vch, nType, nVersion); + setvch(vch); + } + + + bool operator!() const + { + return BN_is_zero(this); + } + + CBigNum& operator+=(const CBigNum& b) + { + if (!BN_add(this, this, &b)) + throw bignum_error("CBigNum::operator+= : BN_add failed"); + return *this; + } + + CBigNum& operator-=(const CBigNum& b) + { + *this = *this - b; + return *this; + } + + CBigNum& operator*=(const CBigNum& b) + { + CAutoBN_CTX pctx; + if (!BN_mul(this, this, &b, pctx)) + throw bignum_error("CBigNum::operator*= : BN_mul failed"); + return *this; + } + + CBigNum& operator/=(const CBigNum& b) + { + *this = *this / b; + return *this; + } + + CBigNum& operator%=(const CBigNum& b) + { + *this = *this % b; + return *this; + } + + CBigNum& operator<<=(unsigned int shift) + { + if (!BN_lshift(this, this, shift)) + throw bignum_error("CBigNum:operator<<= : BN_lshift failed"); + return *this; + } + + CBigNum& operator>>=(unsigned int shift) + { + // Note: BN_rshift segfaults on 64-bit if 2^shift is greater than the number + // if built on ubuntu 9.04 or 9.10, probably depends on version of openssl + CBigNum a = 1; + a <<= shift; + if (BN_cmp(&a, this) > 0) + { + *this = 0; + return *this; + } + + if (!BN_rshift(this, this, shift)) + throw bignum_error("CBigNum:operator>>= : BN_rshift failed"); + return *this; + } + + + CBigNum& operator++() + { + // prefix operator + if (!BN_add(this, this, BN_value_one())) + throw bignum_error("CBigNum::operator++ : BN_add failed"); + return *this; + } + + const CBigNum operator++(int) + { + // postfix operator + const CBigNum ret = *this; + ++(*this); + return ret; + } + + CBigNum& operator--() + { + // prefix operator + CBigNum r; + if (!BN_sub(&r, this, BN_value_one())) + throw bignum_error("CBigNum::operator-- : BN_sub failed"); + *this = r; + return *this; + } + + const CBigNum operator--(int) + { + // postfix operator + const CBigNum ret = *this; + --(*this); + return ret; + } + + + friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator/(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator%(const CBigNum& a, const CBigNum& b); +}; + + + +inline const CBigNum operator+(const CBigNum& a, const CBigNum& b) +{ + CBigNum r; + if (!BN_add(&r, &a, &b)) + throw bignum_error("CBigNum::operator+ : BN_add failed"); + return r; +} + +inline const CBigNum operator-(const CBigNum& a, const CBigNum& b) +{ + CBigNum r; + if (!BN_sub(&r, &a, &b)) + throw bignum_error("CBigNum::operator- : BN_sub failed"); + return r; +} + +inline const CBigNum operator-(const CBigNum& a) +{ + CBigNum r(a); + BN_set_negative(&r, !BN_is_negative(&r)); + return r; +} + +inline const CBigNum operator*(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_mul(&r, &a, &b, pctx)) + throw bignum_error("CBigNum::operator* : BN_mul failed"); + return r; +} + +inline const CBigNum operator/(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_div(&r, NULL, &a, &b, pctx)) + throw bignum_error("CBigNum::operator/ : BN_div failed"); + return r; +} + +inline const CBigNum operator%(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_mod(&r, &a, &b, pctx)) + throw bignum_error("CBigNum::operator% : BN_div failed"); + return r; +} + +inline const CBigNum operator<<(const CBigNum& a, unsigned int shift) +{ + CBigNum r; + if (!BN_lshift(&r, &a, shift)) + throw bignum_error("CBigNum:operator<< : BN_lshift failed"); + return r; +} + +inline const CBigNum operator>>(const CBigNum& a, unsigned int shift) +{ + CBigNum r = a; + r >>= shift; + return r; +} + +inline bool operator==(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) == 0); } +inline bool operator!=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) != 0); } +inline bool operator<=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) <= 0); } +inline bool operator>=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) >= 0); } +inline bool operator<(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) < 0); } +inline bool operator>(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) > 0); } + +#endif diff --git a/src/cryptopp/config.h b/src/cryptopp/config.h new file mode 100644 index 0000000000..0737027f41 --- /dev/null +++ b/src/cryptopp/config.h @@ -0,0 +1,462 @@ +#ifndef CRYPTOPP_CONFIG_H +#define CRYPTOPP_CONFIG_H + +//// Bitcoin: disable SSE2 on 32-bit +#if !defined(_M_X64) && !defined(__x86_64__) +#define CRYPTOPP_DISABLE_SSE2 1 +#endif +//////////// end of Bitcoin changes + + +// ***************** Important Settings ******************** + +// define this if running on a big-endian CPU +#if !defined(IS_LITTLE_ENDIAN) && (defined(__BIG_ENDIAN__) || defined(__sparc) || defined(__sparc__) || defined(__hppa__) || defined(__mips__) || (defined(__MWERKS__) && !defined(__INTEL__))) +# define IS_BIG_ENDIAN +#endif + +// define this if running on a little-endian CPU +// big endian will be assumed if IS_LITTLE_ENDIAN is not defined +#ifndef IS_BIG_ENDIAN +# define IS_LITTLE_ENDIAN +#endif + +// define this if you want to disable all OS-dependent features, +// such as sockets and OS-provided random number generators +// #define NO_OS_DEPENDENCE + +// Define this to use features provided by Microsoft's CryptoAPI. +// Currently the only feature used is random number generation. +// This macro will be ignored if NO_OS_DEPENDENCE is defined. +#define USE_MS_CRYPTOAPI + +// Define this to 1 to enforce the requirement in FIPS 186-2 Change Notice 1 that only 1024 bit moduli be used +#ifndef DSA_1024_BIT_MODULUS_ONLY +# define DSA_1024_BIT_MODULUS_ONLY 1 +#endif + +// ***************** Less Important Settings *************** + +// define this to retain (as much as possible) old deprecated function and class names +// #define CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + +#define GZIP_OS_CODE 0 + +// Try this if your CPU has 256K internal cache or a slow multiply instruction +// and you want a (possibly) faster IDEA implementation using log tables +// #define IDEA_LARGECACHE + +// Define this if, for the linear congruential RNG, you want to use +// the original constants as specified in S.K. Park and K.W. Miller's +// CACM paper. +// #define LCRNG_ORIGINAL_NUMBERS + +// choose which style of sockets to wrap (mostly useful for cygwin which has both) +#define PREFER_BERKELEY_STYLE_SOCKETS +// #define PREFER_WINDOWS_STYLE_SOCKETS + +// set the name of Rijndael cipher, was "Rijndael" before version 5.3 +#define CRYPTOPP_RIJNDAEL_NAME "AES" + +// ***************** Important Settings Again ******************** +// But the defaults should be ok. + +// namespace support is now required +#ifdef NO_NAMESPACE +# error namespace support is now required +#endif + +// Define this to workaround a Microsoft CryptoAPI bug where +// each call to CryptAcquireContext causes a 100 KB memory leak. +// Defining this will cause Crypto++ to make only one call to CryptAcquireContext. +#define WORKAROUND_MS_BUG_Q258000 + +#ifdef CRYPTOPP_DOXYGEN_PROCESSING +// Avoid putting "CryptoPP::" in front of everything in Doxygen output +# define CryptoPP +# define NAMESPACE_BEGIN(x) +# define NAMESPACE_END +// Get Doxygen to generate better documentation for these typedefs +# define DOCUMENTED_TYPEDEF(x, y) class y : public x {}; +#else +# define NAMESPACE_BEGIN(x) namespace x { +# define NAMESPACE_END } +# define DOCUMENTED_TYPEDEF(x, y) typedef x y; +#endif +#define ANONYMOUS_NAMESPACE_BEGIN namespace { +#define USING_NAMESPACE(x) using namespace x; +#define DOCUMENTED_NAMESPACE_BEGIN(x) namespace x { +#define DOCUMENTED_NAMESPACE_END } + +// What is the type of the third parameter to bind? +// For Unix, the new standard is ::socklen_t (typically unsigned int), and the old standard is int. +// Unfortunately there is no way to tell whether or not socklen_t is defined. +// To work around this, TYPE_OF_SOCKLEN_T is a macro so that you can change it from the makefile. +#ifndef TYPE_OF_SOCKLEN_T +# if defined(_WIN32) || defined(__CYGWIN__) +# define TYPE_OF_SOCKLEN_T int +# else +# define TYPE_OF_SOCKLEN_T ::socklen_t +# endif +#endif + +#if defined(__CYGWIN__) && defined(PREFER_WINDOWS_STYLE_SOCKETS) +# define __USE_W32_SOCKETS +#endif + +typedef unsigned char byte; // put in global namespace to avoid ambiguity with other byte typedefs + +NAMESPACE_BEGIN(CryptoPP) + +typedef unsigned short word16; +typedef unsigned int word32; + +#if defined(_MSC_VER) || defined(__BORLANDC__) + typedef unsigned __int64 word64; + #define W64LIT(x) x##ui64 +#else + typedef unsigned long long word64; + #define W64LIT(x) x##ULL +#endif + +// define large word type, used for file offsets and such +typedef word64 lword; +const lword LWORD_MAX = W64LIT(0xffffffffffffffff); + +#ifdef __GNUC__ + #define CRYPTOPP_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif + +// define hword, word, and dword. these are used for multiprecision integer arithmetic +// Intel compiler won't have _umul128 until version 10.0. See http://softwarecommunity.intel.com/isn/Community/en-US/forums/thread/30231625.aspx +#if (defined(_MSC_VER) && (!defined(__INTEL_COMPILER) || __INTEL_COMPILER >= 1000) && (defined(_M_X64) || defined(_M_IA64))) || (defined(__DECCXX) && defined(__alpha__)) || (defined(__INTEL_COMPILER) && defined(__x86_64__)) || (defined(__SUNPRO_CC) && defined(__x86_64__)) + typedef word32 hword; + typedef word64 word; +#else + #define CRYPTOPP_NATIVE_DWORD_AVAILABLE + #if defined(__alpha__) || defined(__ia64__) || defined(_ARCH_PPC64) || defined(__x86_64__) || defined(__mips64) || defined(__sparc64__) + #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !(CRYPTOPP_GCC_VERSION == 40001 && defined(__APPLE__)) && CRYPTOPP_GCC_VERSION >= 30400 + // GCC 4.0.1 on MacOS X is missing __umodti3 and __udivti3 + // mode(TI) division broken on amd64 with GCC earlier than GCC 3.4 + typedef word32 hword; + typedef word64 word; + typedef __uint128_t dword; + typedef __uint128_t word128; + #define CRYPTOPP_WORD128_AVAILABLE + #else + // if we're here, it means we're on a 64-bit CPU but we don't have a way to obtain 128-bit multiplication results + typedef word16 hword; + typedef word32 word; + typedef word64 dword; + #endif + #else + // being here means the native register size is probably 32 bits or less + #define CRYPTOPP_BOOL_SLOW_WORD64 1 + typedef word16 hword; + typedef word32 word; + typedef word64 dword; + #endif +#endif +#ifndef CRYPTOPP_BOOL_SLOW_WORD64 + #define CRYPTOPP_BOOL_SLOW_WORD64 0 +#endif + +const unsigned int WORD_SIZE = sizeof(word); +const unsigned int WORD_BITS = WORD_SIZE * 8; + +NAMESPACE_END + +#ifndef CRYPTOPP_L1_CACHE_LINE_SIZE + // This should be a lower bound on the L1 cache line size. It's used for defense against timing attacks. + #if defined(_M_X64) || defined(__x86_64__) + #define CRYPTOPP_L1_CACHE_LINE_SIZE 64 + #else + // L1 cache line size is 32 on Pentium III and earlier + #define CRYPTOPP_L1_CACHE_LINE_SIZE 32 + #endif +#endif + +#if defined(_MSC_VER) + #if _MSC_VER == 1200 + #include + #endif + #if _MSC_VER > 1200 || defined(_mm_free) + #define CRYPTOPP_MSVC6PP_OR_LATER // VC 6 processor pack or later + #else + #define CRYPTOPP_MSVC6_NO_PP // VC 6 without processor pack + #endif +#endif + +#ifndef CRYPTOPP_ALIGN_DATA + #if defined(CRYPTOPP_MSVC6PP_OR_LATER) + #define CRYPTOPP_ALIGN_DATA(x) __declspec(align(x)) + #elif defined(__GNUC__) + #define CRYPTOPP_ALIGN_DATA(x) __attribute__((aligned(x))) + #else + #define CRYPTOPP_ALIGN_DATA(x) + #endif +#endif + +#ifndef CRYPTOPP_SECTION_ALIGN16 + #if defined(__GNUC__) && !defined(__APPLE__) + // the alignment attribute doesn't seem to work without this section attribute when -fdata-sections is turned on + #define CRYPTOPP_SECTION_ALIGN16 __attribute__((section ("CryptoPP_Align16"))) + #else + #define CRYPTOPP_SECTION_ALIGN16 + #endif +#endif + +#if defined(_MSC_VER) || defined(__fastcall) + #define CRYPTOPP_FASTCALL __fastcall +#else + #define CRYPTOPP_FASTCALL +#endif + +// VC60 workaround: it doesn't allow typename in some places +#if defined(_MSC_VER) && (_MSC_VER < 1300) +#define CPP_TYPENAME +#else +#define CPP_TYPENAME typename +#endif + +// VC60 workaround: can't cast unsigned __int64 to float or double +#if defined(_MSC_VER) && !defined(CRYPTOPP_MSVC6PP_OR_LATER) +#define CRYPTOPP_VC6_INT64 (__int64) +#else +#define CRYPTOPP_VC6_INT64 +#endif + +#ifdef _MSC_VER +#define CRYPTOPP_NO_VTABLE __declspec(novtable) +#else +#define CRYPTOPP_NO_VTABLE +#endif + +#ifdef _MSC_VER + // 4231: nonstandard extension used : 'extern' before template explicit instantiation + // 4250: dominance + // 4251: member needs to have dll-interface + // 4275: base needs to have dll-interface + // 4660: explicitly instantiating a class that's already implicitly instantiated + // 4661: no suitable definition provided for explicit template instantiation request + // 4786: identifer was truncated in debug information + // 4355: 'this' : used in base member initializer list + // 4910: '__declspec(dllexport)' and 'extern' are incompatible on an explicit instantiation +# pragma warning(disable: 4231 4250 4251 4275 4660 4661 4786 4355 4910) +#endif + +#ifdef __BORLANDC__ +// 8037: non-const function called for const object. needed to work around BCB2006 bug +# pragma warn -8037 +#endif + +#if (defined(_MSC_VER) && _MSC_VER <= 1300) || defined(__MWERKS__) || defined(_STLPORT_VERSION) +#define CRYPTOPP_DISABLE_UNCAUGHT_EXCEPTION +#endif + +#ifndef CRYPTOPP_DISABLE_UNCAUGHT_EXCEPTION +#define CRYPTOPP_UNCAUGHT_EXCEPTION_AVAILABLE +#endif + +#ifdef CRYPTOPP_DISABLE_X86ASM // for backwards compatibility: this macro had both meanings +#define CRYPTOPP_DISABLE_ASM +#define CRYPTOPP_DISABLE_SSE2 +#endif + +#if !defined(CRYPTOPP_DISABLE_ASM) && ((defined(_MSC_VER) && defined(_M_IX86)) || (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))) + #define CRYPTOPP_X86_ASM_AVAILABLE + + #if !defined(CRYPTOPP_DISABLE_SSE2) && (defined(CRYPTOPP_MSVC6PP_OR_LATER) || CRYPTOPP_GCC_VERSION >= 30300) + #define CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE 1 + #else + #define CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE 0 + #endif + + // SSSE3 was actually introduced in GNU as 2.17, which was released 6/23/2006, but we can't tell what version of binutils is installed. + // GCC 4.1.2 was released on 2/13/2007, so we'll use that as a proxy for the binutils version. + #if !defined(CRYPTOPP_DISABLE_SSSE3) && (_MSC_VER >= 1400 || CRYPTOPP_GCC_VERSION >= 40102) + #define CRYPTOPP_BOOL_SSSE3_ASM_AVAILABLE 1 + #else + #define CRYPTOPP_BOOL_SSSE3_ASM_AVAILABLE 0 + #endif +#endif + +#if !defined(CRYPTOPP_DISABLE_ASM) && defined(_MSC_VER) && defined(_M_X64) + #define CRYPTOPP_X64_MASM_AVAILABLE +#endif + +#if !defined(CRYPTOPP_DISABLE_ASM) && defined(__GNUC__) && defined(__x86_64__) + #define CRYPTOPP_X64_ASM_AVAILABLE +#endif + +#if !defined(CRYPTOPP_DISABLE_SSE2) && (defined(CRYPTOPP_MSVC6PP_OR_LATER) || defined(__SSE2__)) + #define CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE 1 +#else + #define CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE 0 +#endif + +#if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE || CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE) + #define CRYPTOPP_BOOL_ALIGN16_ENABLED 1 +#else + #define CRYPTOPP_BOOL_ALIGN16_ENABLED 0 +#endif + +// how to allocate 16-byte aligned memory (for SSE2) +#if defined(CRYPTOPP_MSVC6PP_OR_LATER) + #define CRYPTOPP_MM_MALLOC_AVAILABLE +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define CRYPTOPP_MALLOC_ALIGNMENT_IS_16 +#elif defined(__linux__) || defined(__sun__) || defined(__CYGWIN__) + #define CRYPTOPP_MEMALIGN_AVAILABLE +#else + #define CRYPTOPP_NO_ALIGNED_ALLOC +#endif + +// how to disable inlining +#if defined(_MSC_VER) && _MSC_VER >= 1300 +# define CRYPTOPP_NOINLINE_DOTDOTDOT +# define CRYPTOPP_NOINLINE __declspec(noinline) +#elif defined(__GNUC__) +# define CRYPTOPP_NOINLINE_DOTDOTDOT +# define CRYPTOPP_NOINLINE __attribute__((noinline)) +#else +# define CRYPTOPP_NOINLINE_DOTDOTDOT ... +# define CRYPTOPP_NOINLINE +#endif + +// how to declare class constants +#if (defined(_MSC_VER) && _MSC_VER <= 1300) || defined(__INTEL_COMPILER) +# define CRYPTOPP_CONSTANT(x) enum {x}; +#else +# define CRYPTOPP_CONSTANT(x) static const int x; +#endif + +#if defined(_M_X64) || defined(__x86_64__) + #define CRYPTOPP_BOOL_X64 1 +#else + #define CRYPTOPP_BOOL_X64 0 +#endif + +// see http://predef.sourceforge.net/prearch.html +#if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(_X86_) || defined(__I86__) || defined(__INTEL__) + #define CRYPTOPP_BOOL_X86 1 +#else + #define CRYPTOPP_BOOL_X86 0 +#endif + +#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X86 || defined(__powerpc__) + #define CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS +#endif + +#define CRYPTOPP_VERSION 560 + +// ***************** determine availability of OS features ******************** + +#ifndef NO_OS_DEPENDENCE + +#if defined(_WIN32) || defined(__CYGWIN__) +#define CRYPTOPP_WIN32_AVAILABLE +#endif + +#if defined(__unix__) || defined(__MACH__) || defined(__NetBSD__) || defined(__sun) +#define CRYPTOPP_UNIX_AVAILABLE +#endif + +#if defined(CRYPTOPP_WIN32_AVAILABLE) || defined(CRYPTOPP_UNIX_AVAILABLE) +# define HIGHRES_TIMER_AVAILABLE +#endif + +#ifdef CRYPTOPP_UNIX_AVAILABLE +# define HAS_BERKELEY_STYLE_SOCKETS +#endif + +#ifdef CRYPTOPP_WIN32_AVAILABLE +# define HAS_WINDOWS_STYLE_SOCKETS +#endif + +#if defined(HIGHRES_TIMER_AVAILABLE) && (defined(HAS_BERKELEY_STYLE_SOCKETS) || defined(HAS_WINDOWS_STYLE_SOCKETS)) +# define SOCKETS_AVAILABLE +#endif + +#if defined(HAS_WINDOWS_STYLE_SOCKETS) && (!defined(HAS_BERKELEY_STYLE_SOCKETS) || defined(PREFER_WINDOWS_STYLE_SOCKETS)) +# define USE_WINDOWS_STYLE_SOCKETS +#else +# define USE_BERKELEY_STYLE_SOCKETS +#endif + +#if defined(HIGHRES_TIMER_AVAILABLE) && defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(USE_BERKELEY_STYLE_SOCKETS) +# define WINDOWS_PIPES_AVAILABLE +#endif + +#if defined(CRYPTOPP_WIN32_AVAILABLE) && defined(USE_MS_CRYPTOAPI) +# define NONBLOCKING_RNG_AVAILABLE +# define OS_RNG_AVAILABLE +#endif + +#if defined(CRYPTOPP_UNIX_AVAILABLE) || defined(CRYPTOPP_DOXYGEN_PROCESSING) +# define NONBLOCKING_RNG_AVAILABLE +# define BLOCKING_RNG_AVAILABLE +# define OS_RNG_AVAILABLE +# define HAS_PTHREADS +# define THREADS_AVAILABLE +#endif + +#ifdef CRYPTOPP_WIN32_AVAILABLE +# define HAS_WINTHREADS +# define THREADS_AVAILABLE +#endif + +#endif // NO_OS_DEPENDENCE + +// ***************** DLL related ******************** + +#ifdef CRYPTOPP_WIN32_AVAILABLE + +#ifdef CRYPTOPP_EXPORTS +#define CRYPTOPP_IS_DLL +#define CRYPTOPP_DLL __declspec(dllexport) +#elif defined(CRYPTOPP_IMPORTS) +#define CRYPTOPP_IS_DLL +#define CRYPTOPP_DLL __declspec(dllimport) +#else +#define CRYPTOPP_DLL +#endif + +#define CRYPTOPP_API __cdecl + +#else // CRYPTOPP_WIN32_AVAILABLE + +#define CRYPTOPP_DLL +#define CRYPTOPP_API + +#endif // CRYPTOPP_WIN32_AVAILABLE + +#if defined(__MWERKS__) +#define CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS extern class CRYPTOPP_DLL +#elif defined(__BORLANDC__) || defined(__SUNPRO_CC) +#define CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS template class CRYPTOPP_DLL +#else +#define CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS extern template class CRYPTOPP_DLL +#endif + +#if defined(CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES) && !defined(CRYPTOPP_IMPORTS) +#define CRYPTOPP_DLL_TEMPLATE_CLASS template class CRYPTOPP_DLL +#else +#define CRYPTOPP_DLL_TEMPLATE_CLASS CRYPTOPP_EXTERN_DLL_TEMPLATE_CLASS +#endif + +#if defined(__MWERKS__) +#define CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS extern class +#elif defined(__BORLANDC__) || defined(__SUNPRO_CC) +#define CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS template class +#else +#define CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS extern template class +#endif + +#if defined(CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES) && !defined(CRYPTOPP_EXPORTS) +#define CRYPTOPP_STATIC_TEMPLATE_CLASS template class +#else +#define CRYPTOPP_STATIC_TEMPLATE_CLASS CRYPTOPP_EXTERN_STATIC_TEMPLATE_CLASS +#endif + +#endif diff --git a/src/cryptopp/cpu.cpp b/src/cryptopp/cpu.cpp new file mode 100644 index 0000000000..d36d3253ae --- /dev/null +++ b/src/cryptopp/cpu.cpp @@ -0,0 +1,199 @@ +// cpu.cpp - written and placed in the public domain by Wei Dai + +#include "cryptopp/pch.h" + +#ifndef CRYPTOPP_IMPORTS + +#include "cryptopp/cpu.h" +#include "cryptopp/misc.h" +#include + +#ifdef __GNUC__ +#include +#include +#endif + +#ifdef CRYPTOPP_MSVC6PP_OR_LATER +#include +#endif + +NAMESPACE_BEGIN(CryptoPP) + +#ifdef CRYPTOPP_X86_ASM_AVAILABLE + +#ifndef _MSC_VER +typedef void (*SigHandler)(int); + +static jmp_buf s_jmpNoCPUID; +static void SigIllHandlerCPUID(int) +{ + longjmp(s_jmpNoCPUID, 1); +} +#endif + +bool CpuId(word32 input, word32 *output) +{ +#ifdef _MSC_VER + __try + { + __asm + { + mov eax, input + cpuid + mov edi, output + mov [edi], eax + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], edx + } + } + __except (1) + { + return false; + } + return true; +#else + SigHandler oldHandler = signal(SIGILL, SigIllHandlerCPUID); + if (oldHandler == SIG_ERR) + return false; + + bool result = true; + if (setjmp(s_jmpNoCPUID)) + result = false; + else + { + __asm__ + ( + // save ebx in case -fPIC is being used +#if CRYPTOPP_BOOL_X86 + "push %%ebx; cpuid; mov %%ebx, %%edi; pop %%ebx" +#else + "pushq %%rbx; cpuid; mov %%ebx, %%edi; popq %%rbx" +#endif + : "=a" (output[0]), "=D" (output[1]), "=c" (output[2]), "=d" (output[3]) + : "a" (input) + ); + } + + signal(SIGILL, oldHandler); + return result; +#endif +} + +#ifndef _MSC_VER +static jmp_buf s_jmpNoSSE2; +static void SigIllHandlerSSE2(int) +{ + longjmp(s_jmpNoSSE2, 1); +} +#endif + +#elif _MSC_VER >= 1400 && CRYPTOPP_BOOL_X64 + +bool CpuId(word32 input, word32 *output) +{ + __cpuid((int *)output, input); + return true; +} + +#endif + +#ifdef CRYPTOPP_CPUID_AVAILABLE + +static bool TrySSE2() +{ +#if CRYPTOPP_BOOL_X64 + return true; +#elif defined(_MSC_VER) + __try + { +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + AS2(por xmm0, xmm0) // executing SSE2 instruction +#elif CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE + __mm128i x = _mm_setzero_si128(); + return _mm_cvtsi128_si32(x) == 0; +#endif + } + __except (1) + { + return false; + } + return true; +#elif defined(__GNUC__) + SigHandler oldHandler = signal(SIGILL, SigIllHandlerSSE2); + if (oldHandler == SIG_ERR) + return false; + + bool result = true; + if (setjmp(s_jmpNoSSE2)) + result = false; + else + { +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + __asm __volatile ("por %xmm0, %xmm0"); +#elif CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE + __mm128i x = _mm_setzero_si128(); + result = _mm_cvtsi128_si32(x) == 0; +#endif + } + + signal(SIGILL, oldHandler); + return result; +#else + return false; +#endif +} + +bool g_x86DetectionDone = false; +bool g_hasISSE = false, g_hasSSE2 = false, g_hasSSSE3 = false, g_hasMMX = false, g_isP4 = false; +word32 g_cacheLineSize = CRYPTOPP_L1_CACHE_LINE_SIZE; + +void DetectX86Features() +{ + word32 cpuid[4], cpuid1[4]; + if (!CpuId(0, cpuid)) + return; + if (!CpuId(1, cpuid1)) + return; + + g_hasMMX = (cpuid1[3] & (1 << 23)) != 0; + if ((cpuid1[3] & (1 << 26)) != 0) + g_hasSSE2 = TrySSE2(); + g_hasSSSE3 = g_hasSSE2 && (cpuid1[2] & (1<<9)); + + if ((cpuid1[3] & (1 << 25)) != 0) + g_hasISSE = true; + else + { + word32 cpuid2[4]; + CpuId(0x080000000, cpuid2); + if (cpuid2[0] >= 0x080000001) + { + CpuId(0x080000001, cpuid2); + g_hasISSE = (cpuid2[3] & (1 << 22)) != 0; + } + } + + std::swap(cpuid[2], cpuid[3]); + if (memcmp(cpuid+1, "GenuineIntel", 12) == 0) + { + g_isP4 = ((cpuid1[0] >> 8) & 0xf) == 0xf; + g_cacheLineSize = 8 * GETBYTE(cpuid1[1], 1); + } + else if (memcmp(cpuid+1, "AuthenticAMD", 12) == 0) + { + CpuId(0x80000005, cpuid); + g_cacheLineSize = GETBYTE(cpuid[2], 0); + } + + if (!g_cacheLineSize) + g_cacheLineSize = CRYPTOPP_L1_CACHE_LINE_SIZE; + + g_x86DetectionDone = true; +} + +#endif + +NAMESPACE_END + +#endif diff --git a/src/cryptopp/cpu.h b/src/cryptopp/cpu.h new file mode 100644 index 0000000000..113b8a1f3b --- /dev/null +++ b/src/cryptopp/cpu.h @@ -0,0 +1,263 @@ +#ifndef CRYPTOPP_CPU_H +#define CRYPTOPP_CPU_H + +#ifdef CRYPTOPP_GENERATE_X64_MASM + +#define CRYPTOPP_X86_ASM_AVAILABLE +#define CRYPTOPP_BOOL_X64 1 +#define CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE 1 +#define NAMESPACE_END + +#else + +#include "cryptopp/config.h" + +#ifdef CRYPTOPP_MSVC6PP_OR_LATER + #include +#endif + +NAMESPACE_BEGIN(CryptoPP) + +#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || (_MSC_VER >= 1400 && CRYPTOPP_BOOL_X64) + +#define CRYPTOPP_CPUID_AVAILABLE + +// these should not be used directly +extern CRYPTOPP_DLL bool g_x86DetectionDone; +extern CRYPTOPP_DLL bool g_hasSSE2; +extern CRYPTOPP_DLL bool g_hasISSE; +extern CRYPTOPP_DLL bool g_hasMMX; +extern CRYPTOPP_DLL bool g_hasSSSE3; +extern CRYPTOPP_DLL bool g_isP4; +extern CRYPTOPP_DLL word32 g_cacheLineSize; +CRYPTOPP_DLL void CRYPTOPP_API DetectX86Features(); + +CRYPTOPP_DLL bool CRYPTOPP_API CpuId(word32 input, word32 *output); + +#if CRYPTOPP_BOOL_X64 +inline bool HasSSE2() {return true;} +inline bool HasISSE() {return true;} +inline bool HasMMX() {return true;} +#else + +inline bool HasSSE2() +{ + if (!g_x86DetectionDone) + DetectX86Features(); + return g_hasSSE2; +} + +inline bool HasISSE() +{ + if (!g_x86DetectionDone) + DetectX86Features(); + return g_hasISSE; +} + +inline bool HasMMX() +{ + if (!g_x86DetectionDone) + DetectX86Features(); + return g_hasMMX; +} + +#endif + +inline bool HasSSSE3() +{ + if (!g_x86DetectionDone) + DetectX86Features(); + return g_hasSSSE3; +} + +inline bool IsP4() +{ + if (!g_x86DetectionDone) + DetectX86Features(); + return g_isP4; +} + +inline int GetCacheLineSize() +{ + if (!g_x86DetectionDone) + DetectX86Features(); + return g_cacheLineSize; +} + +#else + +inline int GetCacheLineSize() +{ + return CRYPTOPP_L1_CACHE_LINE_SIZE; +} + +inline bool HasSSSE3() {return false;} +inline bool IsP4() {return false;} + +// assume MMX and SSE2 if intrinsics are enabled +#if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE || CRYPTOPP_BOOL_X64 +inline bool HasSSE2() {return true;} +inline bool HasISSE() {return true;} +inline bool HasMMX() {return true;} +#else +inline bool HasSSE2() {return false;} +inline bool HasISSE() {return false;} +inline bool HasMMX() {return false;} +#endif + +#endif // #ifdef CRYPTOPP_X86_ASM_AVAILABLE || _MSC_VER >= 1400 + +#endif + +#ifdef CRYPTOPP_GENERATE_X64_MASM + #define AS1(x) x*newline* + #define AS2(x, y) x, y*newline* + #define AS3(x, y, z) x, y, z*newline* + #define ASS(x, y, a, b, c, d) x, y, a*64+b*16+c*4+d*newline* + #define ASL(x) label##x:*newline* + #define ASJ(x, y, z) x label##y*newline* + #define ASC(x, y) x label##y*newline* + #define AS_HEX(y) 0##y##h +#elif defined(__GNUC__) + // define these in two steps to allow arguments to be expanded + #define GNU_AS1(x) #x ";" + #define GNU_AS2(x, y) #x ", " #y ";" + #define GNU_AS3(x, y, z) #x ", " #y ", " #z ";" + #define GNU_ASL(x) "\n" #x ":" + #define GNU_ASJ(x, y, z) #x " " #y #z ";" + #define AS1(x) GNU_AS1(x) + #define AS2(x, y) GNU_AS2(x, y) + #define AS3(x, y, z) GNU_AS3(x, y, z) + #define ASS(x, y, a, b, c, d) #x ", " #y ", " #a "*64+" #b "*16+" #c "*4+" #d ";" + #define ASL(x) GNU_ASL(x) + #define ASJ(x, y, z) GNU_ASJ(x, y, z) + #define ASC(x, y) #x " " #y ";" + #define CRYPTOPP_NAKED + #define AS_HEX(y) 0x##y +#else + #define AS1(x) __asm {x} + #define AS2(x, y) __asm {x, y} + #define AS3(x, y, z) __asm {x, y, z} + #define ASS(x, y, a, b, c, d) __asm {x, y, _MM_SHUFFLE(a, b, c, d)} + #define ASL(x) __asm {label##x:} + #define ASJ(x, y, z) __asm {x label##y} + #define ASC(x, y) __asm {x label##y} + #define CRYPTOPP_NAKED __declspec(naked) + #define AS_HEX(y) 0x##y +#endif + +#define IF0(y) +#define IF1(y) y + +#ifdef CRYPTOPP_GENERATE_X64_MASM +#define ASM_MOD(x, y) ((x) MOD (y)) +#define XMMWORD_PTR XMMWORD PTR +#else +// GNU assembler doesn't seem to have mod operator +#define ASM_MOD(x, y) ((x)-((x)/(y))*(y)) +// GAS 2.15 doesn't support XMMWORD PTR. it seems necessary only for MASM +#define XMMWORD_PTR +#endif + +#if CRYPTOPP_BOOL_X86 + #define AS_REG_1 ecx + #define AS_REG_2 edx + #define AS_REG_3 esi + #define AS_REG_4 edi + #define AS_REG_5 eax + #define AS_REG_6 ebx + #define AS_REG_7 ebp + #define AS_REG_1d ecx + #define AS_REG_2d edx + #define AS_REG_3d esi + #define AS_REG_4d edi + #define AS_REG_5d eax + #define AS_REG_6d ebx + #define AS_REG_7d ebp + #define WORD_SZ 4 + #define WORD_REG(x) e##x + #define WORD_PTR DWORD PTR + #define AS_PUSH_IF86(x) AS1(push e##x) + #define AS_POP_IF86(x) AS1(pop e##x) + #define AS_JCXZ jecxz +#elif CRYPTOPP_BOOL_X64 + #ifdef CRYPTOPP_GENERATE_X64_MASM + #define AS_REG_1 rcx + #define AS_REG_2 rdx + #define AS_REG_3 r8 + #define AS_REG_4 r9 + #define AS_REG_5 rax + #define AS_REG_6 r10 + #define AS_REG_7 r11 + #define AS_REG_1d ecx + #define AS_REG_2d edx + #define AS_REG_3d r8d + #define AS_REG_4d r9d + #define AS_REG_5d eax + #define AS_REG_6d r10d + #define AS_REG_7d r11d + #else + #define AS_REG_1 rdi + #define AS_REG_2 rsi + #define AS_REG_3 rdx + #define AS_REG_4 rcx + #define AS_REG_5 r8 + #define AS_REG_6 r9 + #define AS_REG_7 r10 + #define AS_REG_1d edi + #define AS_REG_2d esi + #define AS_REG_3d edx + #define AS_REG_4d ecx + #define AS_REG_5d r8d + #define AS_REG_6d r9d + #define AS_REG_7d r10d + #endif + #define WORD_SZ 8 + #define WORD_REG(x) r##x + #define WORD_PTR QWORD PTR + #define AS_PUSH_IF86(x) + #define AS_POP_IF86(x) + #define AS_JCXZ jrcxz +#endif + +// helper macro for stream cipher output +#define AS_XMM_OUTPUT4(labelPrefix, inputPtr, outputPtr, x0, x1, x2, x3, t, p0, p1, p2, p3, increment)\ + AS2( test inputPtr, inputPtr)\ + ASC( jz, labelPrefix##3)\ + AS2( test inputPtr, 15)\ + ASC( jnz, labelPrefix##7)\ + AS2( pxor xmm##x0, [inputPtr+p0*16])\ + AS2( pxor xmm##x1, [inputPtr+p1*16])\ + AS2( pxor xmm##x2, [inputPtr+p2*16])\ + AS2( pxor xmm##x3, [inputPtr+p3*16])\ + AS2( add inputPtr, increment*16)\ + ASC( jmp, labelPrefix##3)\ + ASL(labelPrefix##7)\ + AS2( movdqu xmm##t, [inputPtr+p0*16])\ + AS2( pxor xmm##x0, xmm##t)\ + AS2( movdqu xmm##t, [inputPtr+p1*16])\ + AS2( pxor xmm##x1, xmm##t)\ + AS2( movdqu xmm##t, [inputPtr+p2*16])\ + AS2( pxor xmm##x2, xmm##t)\ + AS2( movdqu xmm##t, [inputPtr+p3*16])\ + AS2( pxor xmm##x3, xmm##t)\ + AS2( add inputPtr, increment*16)\ + ASL(labelPrefix##3)\ + AS2( test outputPtr, 15)\ + ASC( jnz, labelPrefix##8)\ + AS2( movdqa [outputPtr+p0*16], xmm##x0)\ + AS2( movdqa [outputPtr+p1*16], xmm##x1)\ + AS2( movdqa [outputPtr+p2*16], xmm##x2)\ + AS2( movdqa [outputPtr+p3*16], xmm##x3)\ + ASC( jmp, labelPrefix##9)\ + ASL(labelPrefix##8)\ + AS2( movdqu [outputPtr+p0*16], xmm##x0)\ + AS2( movdqu [outputPtr+p1*16], xmm##x1)\ + AS2( movdqu [outputPtr+p2*16], xmm##x2)\ + AS2( movdqu [outputPtr+p3*16], xmm##x3)\ + ASL(labelPrefix##9)\ + AS2( add outputPtr, increment*16) + +NAMESPACE_END + +#endif diff --git a/src/cryptopp/cryptlib.h b/src/cryptopp/cryptlib.h new file mode 100644 index 0000000000..1461af7b7b --- /dev/null +++ b/src/cryptopp/cryptlib.h @@ -0,0 +1,1668 @@ +// cryptlib.h - written and placed in the public domain by Wei Dai +/*! \file + This file contains the declarations for the abstract base + classes that provide a uniform interface to this library. +*/ + +/*! \mainpage Crypto++ Library 5.6.0 API Reference +
+
Abstract Base Classes
+ cryptlib.h +
Authenticated Encryption
+ AuthenticatedSymmetricCipherDocumentation +
Symmetric Ciphers
+ SymmetricCipherDocumentation +
Hash Functions
+ SHA1, SHA224, SHA256, SHA384, SHA512, Tiger, Whirlpool, RIPEMD160, RIPEMD320, RIPEMD128, RIPEMD256, Weak1::MD2, Weak1::MD4, Weak1::MD5 +
Non-Cryptographic Checksums
+ CRC32, Adler32 +
Message Authentication Codes
+ VMAC, HMAC, CBC_MAC, CMAC, DMAC, TTMAC, GCM (GMAC) +
Random Number Generators
+ NullRNG(), LC_RNG, RandomPool, BlockingRng, NonblockingRng, AutoSeededRandomPool, AutoSeededX917RNG, DefaultAutoSeededRNG +
Password-based Cryptography
+ PasswordBasedKeyDerivationFunction +
Public Key Cryptosystems
+ DLIES, ECIES, LUCES, RSAES, RabinES, LUC_IES +
Public Key Signature Schemes
+ DSA, GDSA, ECDSA, NR, ECNR, LUCSS, RSASS, RSASS_ISO, RabinSS, RWSS, ESIGN +
Key Agreement
+ #DH, DH2, #MQV, ECDH, ECMQV, XTR_DH +
Algebraic Structures
+ Integer, PolynomialMod2, PolynomialOver, RingOfPolynomialsOver, + ModularArithmetic, MontgomeryRepresentation, GFP2_ONB, + GF2NP, GF256, GF2_32, EC2N, ECP +
Secret Sharing and Information Dispersal
+ SecretSharing, SecretRecovery, InformationDispersal, InformationRecovery +
Compression
+ Deflator, Inflator, Gzip, Gunzip, ZlibCompressor, ZlibDecompressor +
Input Source Classes
+ StringSource, ArraySource, FileSource, SocketSource, WindowsPipeSource, RandomNumberSource +
Output Sink Classes
+ StringSinkTemplate, ArraySink, FileSink, SocketSink, WindowsPipeSink, RandomNumberSink +
Filter Wrappers
+ StreamTransformationFilter, HashFilter, HashVerificationFilter, SignerFilter, SignatureVerificationFilter +
Binary to Text Encoders and Decoders
+ HexEncoder, HexDecoder, Base64Encoder, Base64Decoder, Base32Encoder, Base32Decoder +
Wrappers for OS features
+ Timer, Socket, WindowsHandle, ThreadLocalStorage, ThreadUserTimer +
FIPS 140 related
+ fips140.h +
+ +In the FIPS 140-2 validated DLL version of Crypto++, only the following implementation class are available. +
+
Block Ciphers
+ AES, DES_EDE2, DES_EDE3, SKIPJACK +
Cipher Modes (replace template parameter BC with one of the block ciphers above)
+ ECB_Mode\, CTR_Mode\, CBC_Mode\, CFB_FIPS_Mode\, OFB_Mode\ +
Hash Functions
+ SHA1, SHA224, SHA256, SHA384, SHA512 +
Public Key Signature Schemes (replace template parameter H with one of the hash functions above)
+ RSASS\, RSASS\, RSASS_ISO\, RWSS\, DSA, ECDSA\, ECDSA\ +
Message Authentication Codes (replace template parameter H with one of the hash functions above)
+ HMAC\, CBC_MAC\, CBC_MAC\ +
Random Number Generators
+ DefaultAutoSeededRNG (AutoSeededX917RNG\) +
Key Agreement
+ #DH +
Public Key Cryptosystems
+ RSAES\ \> +
+ +

This reference manual is a work in progress. Some classes are still lacking detailed descriptions. +

Click here to download a zip archive containing this manual. +

Thanks to Ryan Phillips for providing the Doxygen configuration file +and getting me started with this manual. +*/ + +#ifndef CRYPTOPP_CRYPTLIB_H +#define CRYPTOPP_CRYPTLIB_H + +#include "cryptopp/config.h" +#include "cryptopp/stdcpp.h" + +NAMESPACE_BEGIN(CryptoPP) + +// forward declarations +class Integer; +class RandomNumberGenerator; +class BufferedTransformation; + +//! used to specify a direction for a cipher to operate in (encrypt or decrypt) +enum CipherDir {ENCRYPTION, DECRYPTION}; + +//! used to represent infinite time +const unsigned long INFINITE_TIME = ULONG_MAX; + +// VC60 workaround: using enums as template parameters causes problems +template +struct EnumToType +{ + static ENUM_TYPE ToEnum() {return (ENUM_TYPE)VALUE;} +}; + +enum ByteOrder {LITTLE_ENDIAN_ORDER = 0, BIG_ENDIAN_ORDER = 1}; +typedef EnumToType LittleEndian; +typedef EnumToType BigEndian; + +//! base class for all exceptions thrown by Crypto++ +class CRYPTOPP_DLL Exception : public std::exception +{ +public: + //! error types + enum ErrorType { + //! a method is not implemented + NOT_IMPLEMENTED, + //! invalid function argument + INVALID_ARGUMENT, + //! BufferedTransformation received a Flush(true) signal but can't flush buffers + CANNOT_FLUSH, + //! data integerity check (such as CRC or MAC) failed + DATA_INTEGRITY_CHECK_FAILED, + //! received input data that doesn't conform to expected format + INVALID_DATA_FORMAT, + //! error reading from input device or writing to output device + IO_ERROR, + //! some error not belong to any of the above categories + OTHER_ERROR + }; + + explicit Exception(ErrorType errorType, const std::string &s) : m_errorType(errorType), m_what(s) {} + virtual ~Exception() throw() {} + const char *what() const throw() {return (m_what.c_str());} + const std::string &GetWhat() const {return m_what;} + void SetWhat(const std::string &s) {m_what = s;} + ErrorType GetErrorType() const {return m_errorType;} + void SetErrorType(ErrorType errorType) {m_errorType = errorType;} + +private: + ErrorType m_errorType; + std::string m_what; +}; + +//! exception thrown when an invalid argument is detected +class CRYPTOPP_DLL InvalidArgument : public Exception +{ +public: + explicit InvalidArgument(const std::string &s) : Exception(INVALID_ARGUMENT, s) {} +}; + +//! exception thrown when input data is received that doesn't conform to expected format +class CRYPTOPP_DLL InvalidDataFormat : public Exception +{ +public: + explicit InvalidDataFormat(const std::string &s) : Exception(INVALID_DATA_FORMAT, s) {} +}; + +//! exception thrown by decryption filters when trying to decrypt an invalid ciphertext +class CRYPTOPP_DLL InvalidCiphertext : public InvalidDataFormat +{ +public: + explicit InvalidCiphertext(const std::string &s) : InvalidDataFormat(s) {} +}; + +//! exception thrown by a class if a non-implemented method is called +class CRYPTOPP_DLL NotImplemented : public Exception +{ +public: + explicit NotImplemented(const std::string &s) : Exception(NOT_IMPLEMENTED, s) {} +}; + +//! exception thrown by a class when Flush(true) is called but it can't completely flush its buffers +class CRYPTOPP_DLL CannotFlush : public Exception +{ +public: + explicit CannotFlush(const std::string &s) : Exception(CANNOT_FLUSH, s) {} +}; + +//! error reported by the operating system +class CRYPTOPP_DLL OS_Error : public Exception +{ +public: + OS_Error(ErrorType errorType, const std::string &s, const std::string& operation, int errorCode) + : Exception(errorType, s), m_operation(operation), m_errorCode(errorCode) {} + ~OS_Error() throw() {} + + // the operating system API that reported the error + const std::string & GetOperation() const {return m_operation;} + // the error code return by the operating system + int GetErrorCode() const {return m_errorCode;} + +protected: + std::string m_operation; + int m_errorCode; +}; + +//! used to return decoding results +struct CRYPTOPP_DLL DecodingResult +{ + explicit DecodingResult() : isValidCoding(false), messageLength(0) {} + explicit DecodingResult(size_t len) : isValidCoding(true), messageLength(len) {} + + bool operator==(const DecodingResult &rhs) const {return isValidCoding == rhs.isValidCoding && messageLength == rhs.messageLength;} + bool operator!=(const DecodingResult &rhs) const {return !operator==(rhs);} + + bool isValidCoding; + size_t messageLength; + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + operator size_t() const {return isValidCoding ? messageLength : 0;} +#endif +}; + +//! interface for retrieving values given their names +/*! \note This class is used to safely pass a variable number of arbitrarily typed arguments to functions + and to read values from keys and crypto parameters. + \note To obtain an object that implements NameValuePairs for the purpose of parameter + passing, use the MakeParameters() function. + \note To get a value from NameValuePairs, you need to know the name and the type of the value. + Call GetValueNames() on a NameValuePairs object to obtain a list of value names that it supports. + Then look at the Name namespace documentation to see what the type of each value is, or + alternatively, call GetIntValue() with the value name, and if the type is not int, a + ValueTypeMismatch exception will be thrown and you can get the actual type from the exception object. +*/ +class CRYPTOPP_NO_VTABLE NameValuePairs +{ +public: + virtual ~NameValuePairs() {} + + //! exception thrown when trying to retrieve a value using a different type than expected + class CRYPTOPP_DLL ValueTypeMismatch : public InvalidArgument + { + public: + ValueTypeMismatch(const std::string &name, const std::type_info &stored, const std::type_info &retrieving) + : InvalidArgument("NameValuePairs: type mismatch for '" + name + "', stored '" + stored.name() + "', trying to retrieve '" + retrieving.name() + "'") + , m_stored(stored), m_retrieving(retrieving) {} + + const std::type_info & GetStoredTypeInfo() const {return m_stored;} + const std::type_info & GetRetrievingTypeInfo() const {return m_retrieving;} + + private: + const std::type_info &m_stored; + const std::type_info &m_retrieving; + }; + + //! get a copy of this object or a subobject of it + template + bool GetThisObject(T &object) const + { + return GetValue((std::string("ThisObject:")+typeid(T).name()).c_str(), object); + } + + //! get a pointer to this object, as a pointer to T + template + bool GetThisPointer(T *&p) const + { + return GetValue((std::string("ThisPointer:")+typeid(T).name()).c_str(), p); + } + + //! get a named value, returns true if the name exists + template + bool GetValue(const char *name, T &value) const + { + return GetVoidValue(name, typeid(T), &value); + } + + //! get a named value, returns the default if the name doesn't exist + template + T GetValueWithDefault(const char *name, T defaultValue) const + { + GetValue(name, defaultValue); + return defaultValue; + } + + //! get a list of value names that can be retrieved + CRYPTOPP_DLL std::string GetValueNames() const + {std::string result; GetValue("ValueNames", result); return result;} + + //! get a named value with type int + /*! used to ensure we don't accidentally try to get an unsigned int + or some other type when we mean int (which is the most common case) */ + CRYPTOPP_DLL bool GetIntValue(const char *name, int &value) const + {return GetValue(name, value);} + + //! get a named value with type int, with default + CRYPTOPP_DLL int GetIntValueWithDefault(const char *name, int defaultValue) const + {return GetValueWithDefault(name, defaultValue);} + + //! used by derived classes to check for type mismatch + CRYPTOPP_DLL static void CRYPTOPP_API ThrowIfTypeMismatch(const char *name, const std::type_info &stored, const std::type_info &retrieving) + {if (stored != retrieving) throw ValueTypeMismatch(name, stored, retrieving);} + + template + void GetRequiredParameter(const char *className, const char *name, T &value) const + { + if (!GetValue(name, value)) + throw InvalidArgument(std::string(className) + ": missing required parameter '" + name + "'"); + } + + CRYPTOPP_DLL void GetRequiredIntParameter(const char *className, const char *name, int &value) const + { + if (!GetIntValue(name, value)) + throw InvalidArgument(std::string(className) + ": missing required parameter '" + name + "'"); + } + + //! to be implemented by derived classes, users should use one of the above functions instead + CRYPTOPP_DLL virtual bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const =0; +}; + +//! namespace containing value name definitions +/*! value names, types and semantics: + + ThisObject:ClassName (ClassName, copy of this object or a subobject) + ThisPointer:ClassName (const ClassName *, pointer to this object or a subobject) +*/ +DOCUMENTED_NAMESPACE_BEGIN(Name) +// more names defined in argnames.h +DOCUMENTED_NAMESPACE_END + +//! empty set of name-value pairs +class CRYPTOPP_DLL NullNameValuePairs : public NameValuePairs +{ +public: + bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const {return false;} +}; + +//! _ +extern CRYPTOPP_DLL const NullNameValuePairs g_nullNameValuePairs; + +// ******************************************************** + +//! interface for cloning objects, this is not implemented by most classes yet +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Clonable +{ +public: + virtual ~Clonable() {} + //! this is not implemented by most classes yet + virtual Clonable* Clone() const {throw NotImplemented("Clone() is not implemented yet.");} // TODO: make this =0 +}; + +//! interface for all crypto algorithms + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE Algorithm : public Clonable +{ +public: + /*! When FIPS 140-2 compliance is enabled and checkSelfTestStatus == true, + this constructor throws SelfTestFailure if the self test hasn't been run or fails. */ + Algorithm(bool checkSelfTestStatus = true); + //! returns name of this algorithm, not universally implemented yet + virtual std::string AlgorithmName() const {return "unknown";} +}; + +//! keying interface for crypto algorithms that take byte strings as keys +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE SimpleKeyingInterface +{ +public: + virtual ~SimpleKeyingInterface() {} + + //! returns smallest valid key length in bytes */ + virtual size_t MinKeyLength() const =0; + //! returns largest valid key length in bytes */ + virtual size_t MaxKeyLength() const =0; + //! returns default (recommended) key length in bytes */ + virtual size_t DefaultKeyLength() const =0; + + //! returns the smallest valid key length in bytes that is >= min(n, GetMaxKeyLength()) + virtual size_t GetValidKeyLength(size_t n) const =0; + + //! returns whether n is a valid key length + virtual bool IsValidKeyLength(size_t n) const + {return n == GetValidKeyLength(n);} + + //! set or reset the key of this object + /*! \param params is used to specify Rounds, BlockSize, etc. */ + virtual void SetKey(const byte *key, size_t length, const NameValuePairs ¶ms = g_nullNameValuePairs); + + //! calls SetKey() with an NameValuePairs object that just specifies "Rounds" + void SetKeyWithRounds(const byte *key, size_t length, int rounds); + + //! calls SetKey() with an NameValuePairs object that just specifies "IV" + void SetKeyWithIV(const byte *key, size_t length, const byte *iv, size_t ivLength); + + //! calls SetKey() with an NameValuePairs object that just specifies "IV" + void SetKeyWithIV(const byte *key, size_t length, const byte *iv) + {SetKeyWithIV(key, length, iv, IVSize());} + + enum IV_Requirement {UNIQUE_IV = 0, RANDOM_IV, UNPREDICTABLE_RANDOM_IV, INTERNALLY_GENERATED_IV, NOT_RESYNCHRONIZABLE}; + //! returns the minimal requirement for secure IVs + virtual IV_Requirement IVRequirement() const =0; + + //! returns whether this object can be resynchronized (i.e. supports initialization vectors) + /*! If this function returns true, and no IV is passed to SetKey() and CanUseStructuredIVs()==true, an IV of all 0's will be assumed. */ + bool IsResynchronizable() const {return IVRequirement() < NOT_RESYNCHRONIZABLE;} + //! returns whether this object can use random IVs (in addition to ones returned by GetNextIV) + bool CanUseRandomIVs() const {return IVRequirement() <= UNPREDICTABLE_RANDOM_IV;} + //! returns whether this object can use random but possibly predictable IVs (in addition to ones returned by GetNextIV) + bool CanUsePredictableIVs() const {return IVRequirement() <= RANDOM_IV;} + //! returns whether this object can use structured IVs, for example a counter (in addition to ones returned by GetNextIV) + bool CanUseStructuredIVs() const {return IVRequirement() <= UNIQUE_IV;} + + virtual unsigned int IVSize() const {throw NotImplemented(GetAlgorithm().AlgorithmName() + ": this object doesn't support resynchronization");} + //! returns default length of IVs accepted by this object + unsigned int DefaultIVLength() const {return IVSize();} + //! returns minimal length of IVs accepted by this object + virtual unsigned int MinIVLength() const {return IVSize();} + //! returns maximal length of IVs accepted by this object + virtual unsigned int MaxIVLength() const {return IVSize();} + //! resynchronize with an IV. ivLength=-1 means use IVSize() + virtual void Resynchronize(const byte *iv, int ivLength=-1) {throw NotImplemented(GetAlgorithm().AlgorithmName() + ": this object doesn't support resynchronization");} + //! get a secure IV for the next message + /*! This method should be called after you finish encrypting one message and are ready to start the next one. + After calling it, you must call SetKey() or Resynchronize() before using this object again. + This method is not implemented on decryption objects. */ + virtual void GetNextIV(RandomNumberGenerator &rng, byte *IV); + +protected: + virtual const Algorithm & GetAlgorithm() const =0; + virtual void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) =0; + + void ThrowIfInvalidKeyLength(size_t length); + void ThrowIfResynchronizable(); // to be called when no IV is passed + void ThrowIfInvalidIV(const byte *iv); // check for NULL IV if it can't be used + size_t ThrowIfInvalidIVLength(int size); + const byte * GetIVAndThrowIfInvalid(const NameValuePairs ¶ms, size_t &size); + inline void AssertValidKeyLength(size_t length) const + {assert(IsValidKeyLength(length));} +}; + +//! interface for the data processing part of block ciphers + +/*! Classes derived from BlockTransformation are block ciphers + in ECB mode (for example the DES::Encryption class), which are stateless. + These classes should not be used directly, but only in combination with + a mode class (see CipherModeDocumentation in modes.h). +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE BlockTransformation : public Algorithm +{ +public: + //! encrypt or decrypt inBlock, xor with xorBlock, and write to outBlock + virtual void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const =0; + + //! encrypt or decrypt one block + /*! \pre size of inBlock and outBlock == BlockSize() */ + void ProcessBlock(const byte *inBlock, byte *outBlock) const + {ProcessAndXorBlock(inBlock, NULL, outBlock);} + + //! encrypt or decrypt one block in place + void ProcessBlock(byte *inoutBlock) const + {ProcessAndXorBlock(inoutBlock, NULL, inoutBlock);} + + //! block size of the cipher in bytes + virtual unsigned int BlockSize() const =0; + + //! returns how inputs and outputs should be aligned for optimal performance + virtual unsigned int OptimalDataAlignment() const; + + //! returns true if this is a permutation (i.e. there is an inverse transformation) + virtual bool IsPermutation() const {return true;} + + //! returns true if this is an encryption object + virtual bool IsForwardTransformation() const =0; + + //! return number of blocks that can be processed in parallel, for bit-slicing implementations + virtual unsigned int OptimalNumberOfParallelBlocks() const {return 1;} + + enum {BT_InBlockIsCounter=1, BT_DontIncrementInOutPointers=2, BT_XorInput=4, BT_ReverseDirection=8} FlagsForAdvancedProcessBlocks; + + //! encrypt and xor blocks according to flags (see FlagsForAdvancedProcessBlocks) + /*! /note If BT_InBlockIsCounter is set, last byte of inBlocks may be modified. */ + virtual size_t AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags) const; + + inline CipherDir GetCipherDirection() const {return IsForwardTransformation() ? ENCRYPTION : DECRYPTION;} +}; + +//! interface for the data processing part of stream ciphers + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE StreamTransformation : public Algorithm +{ +public: + //! return a reference to this object, + /*! This function is useful for passing a temporary StreamTransformation object to a + function that takes a non-const reference. */ + StreamTransformation& Ref() {return *this;} + + //! returns block size, if input must be processed in blocks, otherwise 1 + virtual unsigned int MandatoryBlockSize() const {return 1;} + + //! returns the input block size that is most efficient for this cipher + /*! \note optimal input length is n * OptimalBlockSize() - GetOptimalBlockSizeUsed() for any n > 0 */ + virtual unsigned int OptimalBlockSize() const {return MandatoryBlockSize();} + //! returns how much of the current block is used up + virtual unsigned int GetOptimalBlockSizeUsed() const {return 0;} + + //! returns how input should be aligned for optimal performance + virtual unsigned int OptimalDataAlignment() const; + + //! encrypt or decrypt an array of bytes of specified length + /*! \note either inString == outString, or they don't overlap */ + virtual void ProcessData(byte *outString, const byte *inString, size_t length) =0; + + //! for ciphers where the last block of data is special, encrypt or decrypt the last block of data + /*! For now the only use of this function is for CBC-CTS mode. */ + virtual void ProcessLastBlock(byte *outString, const byte *inString, size_t length); + //! returns the minimum size of the last block, 0 indicating the last block is not special + virtual unsigned int MinLastBlockSize() const {return 0;} + + //! same as ProcessData(inoutString, inoutString, length) + inline void ProcessString(byte *inoutString, size_t length) + {ProcessData(inoutString, inoutString, length);} + //! same as ProcessData(outString, inString, length) + inline void ProcessString(byte *outString, const byte *inString, size_t length) + {ProcessData(outString, inString, length);} + //! implemented as {ProcessData(&input, &input, 1); return input;} + inline byte ProcessByte(byte input) + {ProcessData(&input, &input, 1); return input;} + + //! returns whether this cipher supports random access + virtual bool IsRandomAccess() const =0; + //! for random access ciphers, seek to an absolute position + virtual void Seek(lword n) + { + assert(!IsRandomAccess()); + throw NotImplemented("StreamTransformation: this object doesn't support random access"); + } + + //! returns whether this transformation is self-inverting (e.g. xor with a keystream) + virtual bool IsSelfInverting() const =0; + //! returns whether this is an encryption object + virtual bool IsForwardTransformation() const =0; +}; + +//! interface for hash functions and data processing part of MACs + +/*! HashTransformation objects are stateful. They are created in an initial state, + change state as Update() is called, and return to the initial + state when Final() is called. This interface allows a large message to + be hashed in pieces by calling Update() on each piece followed by + calling Final(). +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE HashTransformation : public Algorithm +{ +public: + //! return a reference to this object, + /*! This function is useful for passing a temporary HashTransformation object to a + function that takes a non-const reference. */ + HashTransformation& Ref() {return *this;} + + //! process more input + virtual void Update(const byte *input, size_t length) =0; + + //! request space to write input into + virtual byte * CreateUpdateSpace(size_t &size) {size=0; return NULL;} + + //! compute hash for current message, then restart for a new message + /*! \pre size of digest == DigestSize(). */ + virtual void Final(byte *digest) + {TruncatedFinal(digest, DigestSize());} + + //! discard the current state, and restart with a new message + virtual void Restart() + {TruncatedFinal(NULL, 0);} + + //! size of the hash/digest/MAC returned by Final() + virtual unsigned int DigestSize() const =0; + + //! same as DigestSize() + unsigned int TagSize() const {return DigestSize();} + + + //! block size of underlying compression function, or 0 if not block based + virtual unsigned int BlockSize() const {return 0;} + + //! input to Update() should have length a multiple of this for optimal speed + virtual unsigned int OptimalBlockSize() const {return 1;} + + //! returns how input should be aligned for optimal performance + virtual unsigned int OptimalDataAlignment() const; + + //! use this if your input is in one piece and you don't want to call Update() and Final() separately + virtual void CalculateDigest(byte *digest, const byte *input, size_t length) + {Update(input, length); Final(digest);} + + //! verify that digest is a valid digest for the current message, then reinitialize the object + /*! Default implementation is to call Final() and do a bitwise comparison + between its output and digest. */ + virtual bool Verify(const byte *digest) + {return TruncatedVerify(digest, DigestSize());} + + //! use this if your input is in one piece and you don't want to call Update() and Verify() separately + virtual bool VerifyDigest(const byte *digest, const byte *input, size_t length) + {Update(input, length); return Verify(digest);} + + //! truncated version of Final() + virtual void TruncatedFinal(byte *digest, size_t digestSize) =0; + + //! truncated version of CalculateDigest() + virtual void CalculateTruncatedDigest(byte *digest, size_t digestSize, const byte *input, size_t length) + {Update(input, length); TruncatedFinal(digest, digestSize);} + + //! truncated version of Verify() + virtual bool TruncatedVerify(const byte *digest, size_t digestLength); + + //! truncated version of VerifyDigest() + virtual bool VerifyTruncatedDigest(const byte *digest, size_t digestLength, const byte *input, size_t length) + {Update(input, length); return TruncatedVerify(digest, digestLength);} + +protected: + void ThrowIfInvalidTruncatedSize(size_t size) const; +}; + +typedef HashTransformation HashFunction; + +//! interface for one direction (encryption or decryption) of a block cipher +/*! \note These objects usually should not be used directly. See BlockTransformation for more details. */ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE BlockCipher : public SimpleKeyingInterface, public BlockTransformation +{ +protected: + const Algorithm & GetAlgorithm() const {return *this;} +}; + +//! interface for one direction (encryption or decryption) of a stream cipher or cipher mode +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE SymmetricCipher : public SimpleKeyingInterface, public StreamTransformation +{ +protected: + const Algorithm & GetAlgorithm() const {return *this;} +}; + +//! interface for message authentication codes +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE MessageAuthenticationCode : public SimpleKeyingInterface, public HashTransformation +{ +protected: + const Algorithm & GetAlgorithm() const {return *this;} +}; + +//! interface for for one direction (encryption or decryption) of a stream cipher or block cipher mode with authentication +/*! The StreamTransformation part of this interface is used to encrypt/decrypt the data, and the MessageAuthenticationCode part of this + interface is used to input additional authenticated data (AAD, which is MAC'ed but not encrypted), and to generate/verify the MAC. */ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AuthenticatedSymmetricCipher : public MessageAuthenticationCode, public StreamTransformation +{ +public: + //! this indicates that a member function was called in the wrong state, for example trying to encrypt a message before having set the key or IV + class BadState : public Exception + { + public: + explicit BadState(const std::string &name, const char *message) : Exception(OTHER_ERROR, name + ": " + message) {} + explicit BadState(const std::string &name, const char *function, const char *state) : Exception(OTHER_ERROR, name + ": " + function + " was called before " + state) {} + }; + + //! the maximum length of AAD that can be input before the encrypted data + virtual lword MaxHeaderLength() const =0; + //! the maximum length of encrypted data + virtual lword MaxMessageLength() const =0; + //! the maximum length of AAD that can be input after the encrypted data + virtual lword MaxFooterLength() const {return 0;} + //! if this function returns true, SpecifyDataLengths() must be called before attempting to input data + /*! This is the case for some schemes, such as CCM. */ + virtual bool NeedsPrespecifiedDataLengths() const {return false;} + //! this function only needs to be called if NeedsPrespecifiedDataLengths() returns true + void SpecifyDataLengths(lword headerLength, lword messageLength, lword footerLength=0); + //! encrypt and generate MAC in one call. will truncate MAC if macSize < TagSize() + virtual void EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *header, size_t headerLength, const byte *message, size_t messageLength); + //! decrypt and verify MAC in one call, returning true iff MAC is valid. will assume MAC is truncated if macLength < TagSize() + virtual bool DecryptAndVerify(byte *message, const byte *mac, size_t macLength, const byte *iv, int ivLength, const byte *header, size_t headerLength, const byte *ciphertext, size_t ciphertextLength); + + // redeclare this to avoid compiler ambiguity errors + virtual std::string AlgorithmName() const =0; + +protected: + const Algorithm & GetAlgorithm() const {return *static_cast(this);} + virtual void UncheckedSpecifyDataLengths(lword headerLength, lword messageLength, lword footerLength) {} +}; + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY +typedef SymmetricCipher StreamCipher; +#endif + +//! interface for random number generators +/*! All return values are uniformly distributed over the range specified. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE RandomNumberGenerator : public Algorithm +{ +public: + //! update RNG state with additional unpredictable values + virtual void IncorporateEntropy(const byte *input, size_t length) {throw NotImplemented("RandomNumberGenerator: IncorporateEntropy not implemented");} + + //! returns true if IncorporateEntropy is implemented + virtual bool CanIncorporateEntropy() const {return false;} + + //! generate new random byte and return it + virtual byte GenerateByte(); + + //! generate new random bit and return it + /*! Default implementation is to call GenerateByte() and return its lowest bit. */ + virtual unsigned int GenerateBit(); + + //! generate a random 32 bit word in the range min to max, inclusive + virtual word32 GenerateWord32(word32 a=0, word32 b=0xffffffffL); + + //! generate random array of bytes + virtual void GenerateBlock(byte *output, size_t size); + + //! generate and discard n bytes + virtual void DiscardBytes(size_t n); + + //! generate random bytes as input to a BufferedTransformation + virtual void GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword length); + + //! randomly shuffle the specified array, resulting permutation is uniformly distributed + template void Shuffle(IT begin, IT end) + { + for (; begin != end; ++begin) + std::iter_swap(begin, begin + GenerateWord32(0, end-begin-1)); + } + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + byte GetByte() {return GenerateByte();} + unsigned int GetBit() {return GenerateBit();} + word32 GetLong(word32 a=0, word32 b=0xffffffffL) {return GenerateWord32(a, b);} + word16 GetShort(word16 a=0, word16 b=0xffff) {return (word16)GenerateWord32(a, b);} + void GetBlock(byte *output, size_t size) {GenerateBlock(output, size);} +#endif +}; + +//! returns a reference that can be passed to functions that ask for a RNG but doesn't actually use it +CRYPTOPP_DLL RandomNumberGenerator & CRYPTOPP_API NullRNG(); + +class WaitObjectContainer; +class CallStack; + +//! interface for objects that you can wait for + +class CRYPTOPP_NO_VTABLE Waitable +{ +public: + virtual ~Waitable() {} + + //! maximum number of wait objects that this object can return + virtual unsigned int GetMaxWaitObjectCount() const =0; + //! put wait objects into container + /*! \param callStack is used for tracing no wait loops, example: + something.GetWaitObjects(c, CallStack("my func after X", 0)); + - or in an outer GetWaitObjects() method that itself takes a callStack parameter: + innerThing.GetWaitObjects(c, CallStack("MyClass::GetWaitObjects at X", &callStack)); */ + virtual void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) =0; + //! wait on this object + /*! same as creating an empty container, calling GetWaitObjects(), and calling Wait() on the container */ + bool Wait(unsigned long milliseconds, CallStack const& callStack); +}; + +//! the default channel for BufferedTransformation, equal to the empty string +extern CRYPTOPP_DLL const std::string DEFAULT_CHANNEL; + +//! channel for additional authenticated data, equal to "AAD" +extern CRYPTOPP_DLL const std::string AAD_CHANNEL; + +//! interface for buffered transformations + +/*! BufferedTransformation is a generalization of BlockTransformation, + StreamTransformation, and HashTransformation. + + A buffered transformation is an object that takes a stream of bytes + as input (this may be done in stages), does some computation on them, and + then places the result into an internal buffer for later retrieval. Any + partial result already in the output buffer is not modified by further + input. + + If a method takes a "blocking" parameter, and you + pass "false" for it, the method will return before all input has been processed if + the input cannot be processed without waiting (for network buffers to become available, for example). + In this case the method will return true + or a non-zero integer value. When this happens you must continue to call the method with the same + parameters until it returns false or zero, before calling any other method on it or + attached BufferedTransformation. The integer return value in this case is approximately + the number of bytes left to be processed, and can be used to implement a progress bar. + + For functions that take a "propagation" parameter, propagation != 0 means pass on the signal to attached + BufferedTransformation objects, with propagation decremented at each step until it reaches 0. + -1 means unlimited propagation. + + \nosubgrouping +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE BufferedTransformation : public Algorithm, public Waitable +{ +public: + // placed up here for CW8 + static const std::string &NULL_CHANNEL; // same as DEFAULT_CHANNEL, for backwards compatibility + + BufferedTransformation() : Algorithm(false) {} + + //! return a reference to this object + /*! This function is useful for passing a temporary BufferedTransformation object to a + function that takes a non-const reference. */ + BufferedTransformation& Ref() {return *this;} + + //! \name INPUT + //@{ + //! input a byte for processing + size_t Put(byte inByte, bool blocking=true) + {return Put(&inByte, 1, blocking);} + //! input multiple bytes + size_t Put(const byte *inString, size_t length, bool blocking=true) + {return Put2(inString, length, 0, blocking);} + + //! input a 16-bit word + size_t PutWord16(word16 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); + //! input a 32-bit word + size_t PutWord32(word32 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); + + //! request space which can be written into by the caller, and then used as input to Put() + /*! \param size is requested size (as a hint) for input, and size of the returned space for output */ + /*! \note The purpose of this method is to help avoid doing extra memory allocations. */ + virtual byte * CreatePutSpace(size_t &size) {size=0; return NULL;} + + virtual bool CanModifyInput() const {return false;} + + //! input multiple bytes that may be modified by callee + size_t PutModifiable(byte *inString, size_t length, bool blocking=true) + {return PutModifiable2(inString, length, 0, blocking);} + + bool MessageEnd(int propagation=-1, bool blocking=true) + {return !!Put2(NULL, 0, propagation < 0 ? -1 : propagation+1, blocking);} + size_t PutMessageEnd(const byte *inString, size_t length, int propagation=-1, bool blocking=true) + {return Put2(inString, length, propagation < 0 ? -1 : propagation+1, blocking);} + + //! input multiple bytes for blocking or non-blocking processing + /*! \param messageEnd means how many filters to signal MessageEnd to, including this one */ + virtual size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking) =0; + //! input multiple bytes that may be modified by callee for blocking or non-blocking processing + /*! \param messageEnd means how many filters to signal MessageEnd to, including this one */ + virtual size_t PutModifiable2(byte *inString, size_t length, int messageEnd, bool blocking) + {return Put2(inString, length, messageEnd, blocking);} + + //! thrown by objects that have not implemented nonblocking input processing + struct BlockingInputOnly : public NotImplemented + {BlockingInputOnly(const std::string &s) : NotImplemented(s + ": Nonblocking input is not implemented by this object.") {}}; + //@} + + //! \name WAITING + //@{ + unsigned int GetMaxWaitObjectCount() const; + void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); + //@} + + //! \name SIGNALS + //@{ + virtual void IsolatedInitialize(const NameValuePairs ¶meters) {throw NotImplemented("BufferedTransformation: this object can't be reinitialized");} + virtual bool IsolatedFlush(bool hardFlush, bool blocking) =0; + virtual bool IsolatedMessageSeriesEnd(bool blocking) {return false;} + + //! initialize or reinitialize this object + virtual void Initialize(const NameValuePairs ¶meters=g_nullNameValuePairs, int propagation=-1); + //! flush buffered input and/or output + /*! \param hardFlush is used to indicate whether all data should be flushed + \note Hard flushes must be used with care. It means try to process and output everything, even if + there may not be enough data to complete the action. For example, hard flushing a HexDecoder would + cause an error if you do it after inputing an odd number of hex encoded characters. + For some types of filters, for example ZlibDecompressor, hard flushes can only + be done at "synchronization points". These synchronization points are positions in the data + stream that are created by hard flushes on the corresponding reverse filters, in this + example ZlibCompressor. This is useful when zlib compressed data is moved across a + network in packets and compression state is preserved across packets, as in the ssh2 protocol. + */ + virtual bool Flush(bool hardFlush, int propagation=-1, bool blocking=true); + //! mark end of a series of messages + /*! There should be a MessageEnd immediately before MessageSeriesEnd. */ + virtual bool MessageSeriesEnd(int propagation=-1, bool blocking=true); + + //! set propagation of automatically generated and transferred signals + /*! propagation == 0 means do not automaticly generate signals */ + virtual void SetAutoSignalPropagation(int propagation) {} + + //! + virtual int GetAutoSignalPropagation() const {return 0;} +public: + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + void Close() {MessageEnd();} +#endif + //@} + + //! \name RETRIEVAL OF ONE MESSAGE + //@{ + //! returns number of bytes that is currently ready for retrieval + /*! All retrieval functions return the actual number of bytes + retrieved, which is the lesser of the request number and + MaxRetrievable(). */ + virtual lword MaxRetrievable() const; + + //! returns whether any bytes are currently ready for retrieval + virtual bool AnyRetrievable() const; + + //! try to retrieve a single byte + virtual size_t Get(byte &outByte); + //! try to retrieve multiple bytes + virtual size_t Get(byte *outString, size_t getMax); + + //! peek at the next byte without removing it from the output buffer + virtual size_t Peek(byte &outByte) const; + //! peek at multiple bytes without removing them from the output buffer + virtual size_t Peek(byte *outString, size_t peekMax) const; + + //! try to retrieve a 16-bit word + size_t GetWord16(word16 &value, ByteOrder order=BIG_ENDIAN_ORDER); + //! try to retrieve a 32-bit word + size_t GetWord32(word32 &value, ByteOrder order=BIG_ENDIAN_ORDER); + + //! try to peek at a 16-bit word + size_t PeekWord16(word16 &value, ByteOrder order=BIG_ENDIAN_ORDER) const; + //! try to peek at a 32-bit word + size_t PeekWord32(word32 &value, ByteOrder order=BIG_ENDIAN_ORDER) const; + + //! move transferMax bytes of the buffered output to target as input + lword TransferTo(BufferedTransformation &target, lword transferMax=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL) + {TransferTo2(target, transferMax, channel); return transferMax;} + + //! discard skipMax bytes from the output buffer + virtual lword Skip(lword skipMax=LWORD_MAX); + + //! copy copyMax bytes of the buffered output to target as input + lword CopyTo(BufferedTransformation &target, lword copyMax=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL) const + {return CopyRangeTo(target, 0, copyMax, channel);} + + //! copy copyMax bytes of the buffered output, starting at position (relative to current position), to target as input + lword CopyRangeTo(BufferedTransformation &target, lword position, lword copyMax=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL) const + {lword i = position; CopyRangeTo2(target, i, i+copyMax, channel); return i-position;} + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + unsigned long MaxRetrieveable() const {return MaxRetrievable();} +#endif + //@} + + //! \name RETRIEVAL OF MULTIPLE MESSAGES + //@{ + //! + virtual lword TotalBytesRetrievable() const; + //! number of times MessageEnd() has been received minus messages retrieved or skipped + virtual unsigned int NumberOfMessages() const; + //! returns true if NumberOfMessages() > 0 + virtual bool AnyMessages() const; + //! start retrieving the next message + /*! + Returns false if no more messages exist or this message + is not completely retrieved. + */ + virtual bool GetNextMessage(); + //! skip count number of messages + virtual unsigned int SkipMessages(unsigned int count=UINT_MAX); + //! + unsigned int TransferMessagesTo(BufferedTransformation &target, unsigned int count=UINT_MAX, const std::string &channel=DEFAULT_CHANNEL) + {TransferMessagesTo2(target, count, channel); return count;} + //! + unsigned int CopyMessagesTo(BufferedTransformation &target, unsigned int count=UINT_MAX, const std::string &channel=DEFAULT_CHANNEL) const; + + //! + virtual void SkipAll(); + //! + void TransferAllTo(BufferedTransformation &target, const std::string &channel=DEFAULT_CHANNEL) + {TransferAllTo2(target, channel);} + //! + void CopyAllTo(BufferedTransformation &target, const std::string &channel=DEFAULT_CHANNEL) const; + + virtual bool GetNextMessageSeries() {return false;} + virtual unsigned int NumberOfMessagesInThisSeries() const {return NumberOfMessages();} + virtual unsigned int NumberOfMessageSeries() const {return 0;} + //@} + + //! \name NON-BLOCKING TRANSFER OF OUTPUT + //@{ + //! upon return, byteCount contains number of bytes that have finished being transfered, and returns the number of bytes left in the current transfer block + virtual size_t TransferTo2(BufferedTransformation &target, lword &byteCount, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) =0; + //! upon return, begin contains the start position of data yet to be finished copying, and returns the number of bytes left in the current transfer block + virtual size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const =0; + //! upon return, messageCount contains number of messages that have finished being transfered, and returns the number of bytes left in the current transfer block + size_t TransferMessagesTo2(BufferedTransformation &target, unsigned int &messageCount, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); + //! returns the number of bytes left in the current transfer block + size_t TransferAllTo2(BufferedTransformation &target, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true); + //@} + + //! \name CHANNELS + //@{ + struct NoChannelSupport : public NotImplemented + {NoChannelSupport(const std::string &name) : NotImplemented(name + ": this object doesn't support multiple channels") {}}; + struct InvalidChannelName : public InvalidArgument + {InvalidChannelName(const std::string &name, const std::string &channel) : InvalidArgument(name + ": unexpected channel name \"" + channel + "\"") {}}; + + size_t ChannelPut(const std::string &channel, byte inByte, bool blocking=true) + {return ChannelPut(channel, &inByte, 1, blocking);} + size_t ChannelPut(const std::string &channel, const byte *inString, size_t length, bool blocking=true) + {return ChannelPut2(channel, inString, length, 0, blocking);} + + size_t ChannelPutModifiable(const std::string &channel, byte *inString, size_t length, bool blocking=true) + {return ChannelPutModifiable2(channel, inString, length, 0, blocking);} + + size_t ChannelPutWord16(const std::string &channel, word16 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); + size_t ChannelPutWord32(const std::string &channel, word32 value, ByteOrder order=BIG_ENDIAN_ORDER, bool blocking=true); + + bool ChannelMessageEnd(const std::string &channel, int propagation=-1, bool blocking=true) + {return !!ChannelPut2(channel, NULL, 0, propagation < 0 ? -1 : propagation+1, blocking);} + size_t ChannelPutMessageEnd(const std::string &channel, const byte *inString, size_t length, int propagation=-1, bool blocking=true) + {return ChannelPut2(channel, inString, length, propagation < 0 ? -1 : propagation+1, blocking);} + + virtual byte * ChannelCreatePutSpace(const std::string &channel, size_t &size); + + virtual size_t ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking); + virtual size_t ChannelPutModifiable2(const std::string &channel, byte *begin, size_t length, int messageEnd, bool blocking); + + virtual bool ChannelFlush(const std::string &channel, bool hardFlush, int propagation=-1, bool blocking=true); + virtual bool ChannelMessageSeriesEnd(const std::string &channel, int propagation=-1, bool blocking=true); + + virtual void SetRetrievalChannel(const std::string &channel); + //@} + + //! \name ATTACHMENT + /*! Some BufferedTransformation objects (e.g. Filter objects) + allow other BufferedTransformation objects to be attached. When + this is done, the first object instead of buffering its output, + sents that output to the attached object as input. The entire + attachment chain is deleted when the anchor object is destructed. + */ + //@{ + //! returns whether this object allows attachment + virtual bool Attachable() {return false;} + //! returns the object immediately attached to this object or NULL for no attachment + virtual BufferedTransformation *AttachedTransformation() {assert(!Attachable()); return 0;} + //! + virtual const BufferedTransformation *AttachedTransformation() const + {return const_cast(this)->AttachedTransformation();} + //! delete the current attachment chain and replace it with newAttachment + virtual void Detach(BufferedTransformation *newAttachment = 0) + {assert(!Attachable()); throw NotImplemented("BufferedTransformation: this object is not attachable");} + //! add newAttachment to the end of attachment chain + virtual void Attach(BufferedTransformation *newAttachment); + //@} + +protected: + static int DecrementPropagation(int propagation) + {return propagation != 0 ? propagation - 1 : 0;} + +private: + byte m_buf[4]; // for ChannelPutWord16 and ChannelPutWord32, to ensure buffer isn't deallocated before non-blocking operation completes +}; + +//! returns a reference to a BufferedTransformation object that discards all input +BufferedTransformation & TheBitBucket(); + +//! interface for crypto material, such as public and private keys, and crypto parameters + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CryptoMaterial : public NameValuePairs +{ +public: + //! exception thrown when invalid crypto material is detected + class CRYPTOPP_DLL InvalidMaterial : public InvalidDataFormat + { + public: + explicit InvalidMaterial(const std::string &s) : InvalidDataFormat(s) {} + }; + + //! assign values from source to this object + /*! \note This function can be used to create a public key from a private key. */ + virtual void AssignFrom(const NameValuePairs &source) =0; + + //! check this object for errors + /*! \param level denotes the level of thoroughness: + 0 - using this object won't cause a crash or exception (rng is ignored) + 1 - this object will probably function (encrypt, sign, etc.) correctly (but may not check for weak keys and such) + 2 - make sure this object will function correctly, and do reasonable security checks + 3 - do checks that may take a long time + \return true if the tests pass */ + virtual bool Validate(RandomNumberGenerator &rng, unsigned int level) const =0; + + //! throws InvalidMaterial if this object fails Validate() test + virtual void ThrowIfInvalid(RandomNumberGenerator &rng, unsigned int level) const + {if (!Validate(rng, level)) throw InvalidMaterial("CryptoMaterial: this object contains invalid values");} + +// virtual std::vector GetSupportedFormats(bool includeSaveOnly=false, bool includeLoadOnly=false); + + //! save key into a BufferedTransformation + virtual void Save(BufferedTransformation &bt) const + {throw NotImplemented("CryptoMaterial: this object does not support saving");} + + //! load key from a BufferedTransformation + /*! \throws KeyingErr if decode fails + \note Generally does not check that the key is valid. + Call ValidateKey() or ThrowIfInvalidKey() to check that. */ + virtual void Load(BufferedTransformation &bt) + {throw NotImplemented("CryptoMaterial: this object does not support loading");} + + //! \return whether this object supports precomputation + virtual bool SupportsPrecomputation() const {return false;} + //! do precomputation + /*! The exact semantics of Precompute() is varies, but + typically it means calculate a table of n objects + that can be used later to speed up computation. */ + virtual void Precompute(unsigned int n) + {assert(!SupportsPrecomputation()); throw NotImplemented("CryptoMaterial: this object does not support precomputation");} + //! retrieve previously saved precomputation + virtual void LoadPrecomputation(BufferedTransformation &storedPrecomputation) + {assert(!SupportsPrecomputation()); throw NotImplemented("CryptoMaterial: this object does not support precomputation");} + //! save precomputation for later use + virtual void SavePrecomputation(BufferedTransformation &storedPrecomputation) const + {assert(!SupportsPrecomputation()); throw NotImplemented("CryptoMaterial: this object does not support precomputation");} + + // for internal library use + void DoQuickSanityCheck() const {ThrowIfInvalid(NullRNG(), 0);} + +#if (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) + // Sun Studio 11/CC 5.8 workaround: it generates incorrect code when casting to an empty virtual base class + char m_sunCCworkaround; +#endif +}; + +//! interface for generatable crypto material, such as private keys and crypto parameters + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE GeneratableCryptoMaterial : virtual public CryptoMaterial +{ +public: + //! generate a random key or crypto parameters + /*! \throws KeyingErr if algorithm parameters are invalid, or if a key can't be generated + (e.g., if this is a public key object) */ + virtual void GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs ¶ms = g_nullNameValuePairs) + {throw NotImplemented("GeneratableCryptoMaterial: this object does not support key/parameter generation");} + + //! calls the above function with a NameValuePairs object that just specifies "KeySize" + void GenerateRandomWithKeySize(RandomNumberGenerator &rng, unsigned int keySize); +}; + +//! interface for public keys + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PublicKey : virtual public CryptoMaterial +{ +}; + +//! interface for private keys + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PrivateKey : public GeneratableCryptoMaterial +{ +}; + +//! interface for crypto prameters + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CryptoParameters : public GeneratableCryptoMaterial +{ +}; + +//! interface for asymmetric algorithms + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AsymmetricAlgorithm : public Algorithm +{ +public: + //! returns a reference to the crypto material used by this object + virtual CryptoMaterial & AccessMaterial() =0; + //! returns a const reference to the crypto material used by this object + virtual const CryptoMaterial & GetMaterial() const =0; + + //! for backwards compatibility, calls AccessMaterial().Load(bt) + void BERDecode(BufferedTransformation &bt) + {AccessMaterial().Load(bt);} + //! for backwards compatibility, calls GetMaterial().Save(bt) + void DEREncode(BufferedTransformation &bt) const + {GetMaterial().Save(bt);} +}; + +//! interface for asymmetric algorithms using public keys + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PublicKeyAlgorithm : public AsymmetricAlgorithm +{ +public: + // VC60 workaround: no co-variant return type + CryptoMaterial & AccessMaterial() {return AccessPublicKey();} + const CryptoMaterial & GetMaterial() const {return GetPublicKey();} + + virtual PublicKey & AccessPublicKey() =0; + virtual const PublicKey & GetPublicKey() const {return const_cast(this)->AccessPublicKey();} +}; + +//! interface for asymmetric algorithms using private keys + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PrivateKeyAlgorithm : public AsymmetricAlgorithm +{ +public: + CryptoMaterial & AccessMaterial() {return AccessPrivateKey();} + const CryptoMaterial & GetMaterial() const {return GetPrivateKey();} + + virtual PrivateKey & AccessPrivateKey() =0; + virtual const PrivateKey & GetPrivateKey() const {return const_cast(this)->AccessPrivateKey();} +}; + +//! interface for key agreement algorithms + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE KeyAgreementAlgorithm : public AsymmetricAlgorithm +{ +public: + CryptoMaterial & AccessMaterial() {return AccessCryptoParameters();} + const CryptoMaterial & GetMaterial() const {return GetCryptoParameters();} + + virtual CryptoParameters & AccessCryptoParameters() =0; + virtual const CryptoParameters & GetCryptoParameters() const {return const_cast(this)->AccessCryptoParameters();} +}; + +//! interface for public-key encryptors and decryptors + +/*! This class provides an interface common to encryptors and decryptors + for querying their plaintext and ciphertext lengths. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_CryptoSystem +{ +public: + virtual ~PK_CryptoSystem() {} + + //! maximum length of plaintext for a given ciphertext length + /*! \note This function returns 0 if ciphertextLength is not valid (too long or too short). */ + virtual size_t MaxPlaintextLength(size_t ciphertextLength) const =0; + + //! calculate length of ciphertext given length of plaintext + /*! \note This function returns 0 if plaintextLength is not valid (too long). */ + virtual size_t CiphertextLength(size_t plaintextLength) const =0; + + //! this object supports the use of the parameter with the given name + /*! some possible parameter names: EncodingParameters, KeyDerivationParameters */ + virtual bool ParameterSupported(const char *name) const =0; + + //! return fixed ciphertext length, if one exists, otherwise return 0 + /*! \note "Fixed" here means length of ciphertext does not depend on length of plaintext. + It usually does depend on the key length. */ + virtual size_t FixedCiphertextLength() const {return 0;} + + //! return maximum plaintext length given the fixed ciphertext length, if one exists, otherwise return 0 + virtual size_t FixedMaxPlaintextLength() const {return 0;} + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + size_t MaxPlainTextLength(size_t cipherTextLength) const {return MaxPlaintextLength(cipherTextLength);} + size_t CipherTextLength(size_t plainTextLength) const {return CiphertextLength(plainTextLength);} +#endif +}; + +//! interface for public-key encryptors +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Encryptor : public PK_CryptoSystem, public PublicKeyAlgorithm +{ +public: + //! exception thrown when trying to encrypt plaintext of invalid length + class CRYPTOPP_DLL InvalidPlaintextLength : public Exception + { + public: + InvalidPlaintextLength() : Exception(OTHER_ERROR, "PK_Encryptor: invalid plaintext length") {} + }; + + //! encrypt a byte string + /*! \pre CiphertextLength(plaintextLength) != 0 (i.e., plaintext isn't too long) + \pre size of ciphertext == CiphertextLength(plaintextLength) + */ + virtual void Encrypt(RandomNumberGenerator &rng, + const byte *plaintext, size_t plaintextLength, + byte *ciphertext, const NameValuePairs ¶meters = g_nullNameValuePairs) const =0; + + //! create a new encryption filter + /*! \note The caller is responsible for deleting the returned pointer. + \note Encoding parameters should be passed in the "EP" channel. + */ + virtual BufferedTransformation * CreateEncryptionFilter(RandomNumberGenerator &rng, + BufferedTransformation *attachment=NULL, const NameValuePairs ¶meters = g_nullNameValuePairs) const; +}; + +//! interface for public-key decryptors + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Decryptor : public PK_CryptoSystem, public PrivateKeyAlgorithm +{ +public: + //! decrypt a byte string, and return the length of plaintext + /*! \pre size of plaintext == MaxPlaintextLength(ciphertextLength) bytes. + \return the actual length of the plaintext, indication that decryption failed. + */ + virtual DecodingResult Decrypt(RandomNumberGenerator &rng, + const byte *ciphertext, size_t ciphertextLength, + byte *plaintext, const NameValuePairs ¶meters = g_nullNameValuePairs) const =0; + + //! create a new decryption filter + /*! \note caller is responsible for deleting the returned pointer + */ + virtual BufferedTransformation * CreateDecryptionFilter(RandomNumberGenerator &rng, + BufferedTransformation *attachment=NULL, const NameValuePairs ¶meters = g_nullNameValuePairs) const; + + //! decrypt a fixed size ciphertext + DecodingResult FixedLengthDecrypt(RandomNumberGenerator &rng, const byte *ciphertext, byte *plaintext, const NameValuePairs ¶meters = g_nullNameValuePairs) const + {return Decrypt(rng, ciphertext, FixedCiphertextLength(), plaintext, parameters);} +}; + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY +typedef PK_CryptoSystem PK_FixedLengthCryptoSystem; +typedef PK_Encryptor PK_FixedLengthEncryptor; +typedef PK_Decryptor PK_FixedLengthDecryptor; +#endif + +//! interface for public-key signers and verifiers + +/*! This class provides an interface common to signers and verifiers + for querying scheme properties. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_SignatureScheme +{ +public: + //! invalid key exception, may be thrown by any function in this class if the private or public key has a length that can't be used + class CRYPTOPP_DLL InvalidKeyLength : public Exception + { + public: + InvalidKeyLength(const std::string &message) : Exception(OTHER_ERROR, message) {} + }; + + //! key too short exception, may be thrown by any function in this class if the private or public key is too short to sign or verify anything + class CRYPTOPP_DLL KeyTooShort : public InvalidKeyLength + { + public: + KeyTooShort() : InvalidKeyLength("PK_Signer: key too short for this signature scheme") {} + }; + + virtual ~PK_SignatureScheme() {} + + //! signature length if it only depends on the key, otherwise 0 + virtual size_t SignatureLength() const =0; + + //! maximum signature length produced for a given length of recoverable message part + virtual size_t MaxSignatureLength(size_t recoverablePartLength = 0) const {return SignatureLength();} + + //! length of longest message that can be recovered, or 0 if this signature scheme does not support message recovery + virtual size_t MaxRecoverableLength() const =0; + + //! length of longest message that can be recovered from a signature of given length, or 0 if this signature scheme does not support message recovery + virtual size_t MaxRecoverableLengthFromSignatureLength(size_t signatureLength) const =0; + + //! requires a random number generator to sign + /*! if this returns false, NullRNG() can be passed to functions that take RandomNumberGenerator & */ + virtual bool IsProbabilistic() const =0; + + //! whether or not a non-recoverable message part can be signed + virtual bool AllowNonrecoverablePart() const =0; + + //! if this function returns true, during verification you must input the signature before the message, otherwise you can input it at anytime */ + virtual bool SignatureUpfront() const {return false;} + + //! whether you must input the recoverable part before the non-recoverable part during signing + virtual bool RecoverablePartFirst() const =0; +}; + +//! interface for accumulating messages to be signed or verified +/*! Only Update() should be called + on this class. No other functions inherited from HashTransformation should be called. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_MessageAccumulator : public HashTransformation +{ +public: + //! should not be called on PK_MessageAccumulator + unsigned int DigestSize() const + {throw NotImplemented("PK_MessageAccumulator: DigestSize() should not be called");} + //! should not be called on PK_MessageAccumulator + void TruncatedFinal(byte *digest, size_t digestSize) + {throw NotImplemented("PK_MessageAccumulator: TruncatedFinal() should not be called");} +}; + +//! interface for public-key signers + +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Signer : public PK_SignatureScheme, public PrivateKeyAlgorithm +{ +public: + //! create a new HashTransformation to accumulate the message to be signed + virtual PK_MessageAccumulator * NewSignatureAccumulator(RandomNumberGenerator &rng) const =0; + + virtual void InputRecoverableMessage(PK_MessageAccumulator &messageAccumulator, const byte *recoverableMessage, size_t recoverableMessageLength) const =0; + + //! sign and delete messageAccumulator (even in case of exception thrown) + /*! \pre size of signature == MaxSignatureLength() + \return actual signature length + */ + virtual size_t Sign(RandomNumberGenerator &rng, PK_MessageAccumulator *messageAccumulator, byte *signature) const; + + //! sign and restart messageAccumulator + /*! \pre size of signature == MaxSignatureLength() + \return actual signature length + */ + virtual size_t SignAndRestart(RandomNumberGenerator &rng, PK_MessageAccumulator &messageAccumulator, byte *signature, bool restart=true) const =0; + + //! sign a message + /*! \pre size of signature == MaxSignatureLength() + \return actual signature length + */ + virtual size_t SignMessage(RandomNumberGenerator &rng, const byte *message, size_t messageLen, byte *signature) const; + + //! sign a recoverable message + /*! \pre size of signature == MaxSignatureLength(recoverableMessageLength) + \return actual signature length + */ + virtual size_t SignMessageWithRecovery(RandomNumberGenerator &rng, const byte *recoverableMessage, size_t recoverableMessageLength, + const byte *nonrecoverableMessage, size_t nonrecoverableMessageLength, byte *signature) const; +}; + +//! interface for public-key signature verifiers +/*! The Recover* functions throw NotImplemented if the signature scheme does not support + message recovery. + The Verify* functions throw InvalidDataFormat if the scheme does support message + recovery and the signature contains a non-empty recoverable message part. The + Recovery* functions should be used in that case. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE PK_Verifier : public PK_SignatureScheme, public PublicKeyAlgorithm +{ +public: + //! create a new HashTransformation to accumulate the message to be verified + virtual PK_MessageAccumulator * NewVerificationAccumulator() const =0; + + //! input signature into a message accumulator + virtual void InputSignature(PK_MessageAccumulator &messageAccumulator, const byte *signature, size_t signatureLength) const =0; + + //! check whether messageAccumulator contains a valid signature and message, and delete messageAccumulator (even in case of exception thrown) + virtual bool Verify(PK_MessageAccumulator *messageAccumulator) const; + + //! check whether messageAccumulator contains a valid signature and message, and restart messageAccumulator + virtual bool VerifyAndRestart(PK_MessageAccumulator &messageAccumulator) const =0; + + //! check whether input signature is a valid signature for input message + virtual bool VerifyMessage(const byte *message, size_t messageLen, + const byte *signature, size_t signatureLength) const; + + //! recover a message from its signature + /*! \pre size of recoveredMessage == MaxRecoverableLengthFromSignatureLength(signatureLength) + */ + virtual DecodingResult Recover(byte *recoveredMessage, PK_MessageAccumulator *messageAccumulator) const; + + //! recover a message from its signature + /*! \pre size of recoveredMessage == MaxRecoverableLengthFromSignatureLength(signatureLength) + */ + virtual DecodingResult RecoverAndRestart(byte *recoveredMessage, PK_MessageAccumulator &messageAccumulator) const =0; + + //! recover a message from its signature + /*! \pre size of recoveredMessage == MaxRecoverableLengthFromSignatureLength(signatureLength) + */ + virtual DecodingResult RecoverMessage(byte *recoveredMessage, + const byte *nonrecoverableMessage, size_t nonrecoverableMessageLength, + const byte *signature, size_t signatureLength) const; +}; + +//! interface for domains of simple key agreement protocols + +/*! A key agreement domain is a set of parameters that must be shared + by two parties in a key agreement protocol, along with the algorithms + for generating key pairs and deriving agreed values. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE SimpleKeyAgreementDomain : public KeyAgreementAlgorithm +{ +public: + //! return length of agreed value produced + virtual unsigned int AgreedValueLength() const =0; + //! return length of private keys in this domain + virtual unsigned int PrivateKeyLength() const =0; + //! return length of public keys in this domain + virtual unsigned int PublicKeyLength() const =0; + //! generate private key + /*! \pre size of privateKey == PrivateKeyLength() */ + virtual void GeneratePrivateKey(RandomNumberGenerator &rng, byte *privateKey) const =0; + //! generate public key + /*! \pre size of publicKey == PublicKeyLength() */ + virtual void GeneratePublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const =0; + //! generate private/public key pair + /*! \note equivalent to calling GeneratePrivateKey() and then GeneratePublicKey() */ + virtual void GenerateKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const; + //! derive agreed value from your private key and couterparty's public key, return false in case of failure + /*! \note If you have previously validated the public key, use validateOtherPublicKey=false to save time. + \pre size of agreedValue == AgreedValueLength() + \pre length of privateKey == PrivateKeyLength() + \pre length of otherPublicKey == PublicKeyLength() + */ + virtual bool Agree(byte *agreedValue, const byte *privateKey, const byte *otherPublicKey, bool validateOtherPublicKey=true) const =0; + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + bool ValidateDomainParameters(RandomNumberGenerator &rng) const + {return GetCryptoParameters().Validate(rng, 2);} +#endif +}; + +//! interface for domains of authenticated key agreement protocols + +/*! In an authenticated key agreement protocol, each party has two + key pairs. The long-lived key pair is called the static key pair, + and the short-lived key pair is called the ephemeral key pair. +*/ +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AuthenticatedKeyAgreementDomain : public KeyAgreementAlgorithm +{ +public: + //! return length of agreed value produced + virtual unsigned int AgreedValueLength() const =0; + + //! return length of static private keys in this domain + virtual unsigned int StaticPrivateKeyLength() const =0; + //! return length of static public keys in this domain + virtual unsigned int StaticPublicKeyLength() const =0; + //! generate static private key + /*! \pre size of privateKey == PrivateStaticKeyLength() */ + virtual void GenerateStaticPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const =0; + //! generate static public key + /*! \pre size of publicKey == PublicStaticKeyLength() */ + virtual void GenerateStaticPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const =0; + //! generate private/public key pair + /*! \note equivalent to calling GenerateStaticPrivateKey() and then GenerateStaticPublicKey() */ + virtual void GenerateStaticKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const; + + //! return length of ephemeral private keys in this domain + virtual unsigned int EphemeralPrivateKeyLength() const =0; + //! return length of ephemeral public keys in this domain + virtual unsigned int EphemeralPublicKeyLength() const =0; + //! generate ephemeral private key + /*! \pre size of privateKey == PrivateEphemeralKeyLength() */ + virtual void GenerateEphemeralPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const =0; + //! generate ephemeral public key + /*! \pre size of publicKey == PublicEphemeralKeyLength() */ + virtual void GenerateEphemeralPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const =0; + //! generate private/public key pair + /*! \note equivalent to calling GenerateEphemeralPrivateKey() and then GenerateEphemeralPublicKey() */ + virtual void GenerateEphemeralKeyPair(RandomNumberGenerator &rng, byte *privateKey, byte *publicKey) const; + + //! derive agreed value from your private keys and couterparty's public keys, return false in case of failure + /*! \note The ephemeral public key will always be validated. + If you have previously validated the static public key, use validateStaticOtherPublicKey=false to save time. + \pre size of agreedValue == AgreedValueLength() + \pre length of staticPrivateKey == StaticPrivateKeyLength() + \pre length of ephemeralPrivateKey == EphemeralPrivateKeyLength() + \pre length of staticOtherPublicKey == StaticPublicKeyLength() + \pre length of ephemeralOtherPublicKey == EphemeralPublicKeyLength() + */ + virtual bool Agree(byte *agreedValue, + const byte *staticPrivateKey, const byte *ephemeralPrivateKey, + const byte *staticOtherPublicKey, const byte *ephemeralOtherPublicKey, + bool validateStaticOtherPublicKey=true) const =0; + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY + bool ValidateDomainParameters(RandomNumberGenerator &rng) const + {return GetCryptoParameters().Validate(rng, 2);} +#endif +}; + +// interface for password authenticated key agreement protocols, not implemented yet +#if 0 +//! interface for protocol sessions +/*! The methods should be called in the following order: + + InitializeSession(rng, parameters); // or call initialize method in derived class + while (true) + { + if (OutgoingMessageAvailable()) + { + length = GetOutgoingMessageLength(); + GetOutgoingMessage(message); + ; // send outgoing message + } + + if (LastMessageProcessed()) + break; + + ; // receive incoming message + ProcessIncomingMessage(message); + } + ; // call methods in derived class to obtain result of protocol session +*/ +class ProtocolSession +{ +public: + //! exception thrown when an invalid protocol message is processed + class ProtocolError : public Exception + { + public: + ProtocolError(ErrorType errorType, const std::string &s) : Exception(errorType, s) {} + }; + + //! exception thrown when a function is called unexpectedly + /*! for example calling ProcessIncomingMessage() when ProcessedLastMessage() == true */ + class UnexpectedMethodCall : public Exception + { + public: + UnexpectedMethodCall(const std::string &s) : Exception(OTHER_ERROR, s) {} + }; + + ProtocolSession() : m_rng(NULL), m_throwOnProtocolError(true), m_validState(false) {} + virtual ~ProtocolSession() {} + + virtual void InitializeSession(RandomNumberGenerator &rng, const NameValuePairs ¶meters) =0; + + bool GetThrowOnProtocolError() const {return m_throwOnProtocolError;} + void SetThrowOnProtocolError(bool throwOnProtocolError) {m_throwOnProtocolError = throwOnProtocolError;} + + bool HasValidState() const {return m_validState;} + + virtual bool OutgoingMessageAvailable() const =0; + virtual unsigned int GetOutgoingMessageLength() const =0; + virtual void GetOutgoingMessage(byte *message) =0; + + virtual bool LastMessageProcessed() const =0; + virtual void ProcessIncomingMessage(const byte *message, unsigned int messageLength) =0; + +protected: + void HandleProtocolError(Exception::ErrorType errorType, const std::string &s) const; + void CheckAndHandleInvalidState() const; + void SetValidState(bool valid) {m_validState = valid;} + + RandomNumberGenerator *m_rng; + +private: + bool m_throwOnProtocolError, m_validState; +}; + +class KeyAgreementSession : public ProtocolSession +{ +public: + virtual unsigned int GetAgreedValueLength() const =0; + virtual void GetAgreedValue(byte *agreedValue) const =0; +}; + +class PasswordAuthenticatedKeyAgreementSession : public KeyAgreementSession +{ +public: + void InitializePasswordAuthenticatedKeyAgreementSession(RandomNumberGenerator &rng, + const byte *myId, unsigned int myIdLength, + const byte *counterPartyId, unsigned int counterPartyIdLength, + const byte *passwordOrVerifier, unsigned int passwordOrVerifierLength); +}; + +class PasswordAuthenticatedKeyAgreementDomain : public KeyAgreementAlgorithm +{ +public: + //! return whether the domain parameters stored in this object are valid + virtual bool ValidateDomainParameters(RandomNumberGenerator &rng) const + {return GetCryptoParameters().Validate(rng, 2);} + + virtual unsigned int GetPasswordVerifierLength(const byte *password, unsigned int passwordLength) const =0; + virtual void GeneratePasswordVerifier(RandomNumberGenerator &rng, const byte *userId, unsigned int userIdLength, const byte *password, unsigned int passwordLength, byte *verifier) const =0; + + enum RoleFlags {CLIENT=1, SERVER=2, INITIATOR=4, RESPONDER=8}; + + virtual bool IsValidRole(unsigned int role) =0; + virtual PasswordAuthenticatedKeyAgreementSession * CreateProtocolSession(unsigned int role) const =0; +}; +#endif + +//! BER Decode Exception Class, may be thrown during an ASN1 BER decode operation +class CRYPTOPP_DLL BERDecodeErr : public InvalidArgument +{ +public: + BERDecodeErr() : InvalidArgument("BER decode error") {} + BERDecodeErr(const std::string &s) : InvalidArgument(s) {} +}; + +//! interface for encoding and decoding ASN1 objects +class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE ASN1Object +{ +public: + virtual ~ASN1Object() {} + //! decode this object from a BufferedTransformation, using BER (Basic Encoding Rules) + virtual void BERDecode(BufferedTransformation &bt) =0; + //! encode this object into a BufferedTransformation, using DER (Distinguished Encoding Rules) + virtual void DEREncode(BufferedTransformation &bt) const =0; + //! encode this object into a BufferedTransformation, using BER + /*! this may be useful if DEREncode() would be too inefficient */ + virtual void BEREncode(BufferedTransformation &bt) const {DEREncode(bt);} +}; + +#ifdef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY +typedef PK_SignatureScheme PK_SignatureSystem; +typedef SimpleKeyAgreementDomain PK_SimpleKeyAgreementDomain; +typedef AuthenticatedKeyAgreementDomain PK_AuthenticatedKeyAgreementDomain; +#endif + +NAMESPACE_END + +#endif diff --git a/src/cryptopp/iterhash.h b/src/cryptopp/iterhash.h new file mode 100644 index 0000000000..0e2a147728 --- /dev/null +++ b/src/cryptopp/iterhash.h @@ -0,0 +1,29 @@ +#ifndef CRYPTOPP_ITERHASH_H +#define CRYPTOPP_ITERHASH_H + +#include "cryptopp/secblock.h" + +NAMESPACE_BEGIN(CryptoPP) + +// *** trimmed down dependency from iterhash.h *** +template +class CRYPTOPP_NO_VTABLE IteratedHashWithStaticTransform +{ +public: + CRYPTOPP_CONSTANT(DIGESTSIZE = T_DigestSize ? T_DigestSize : T_StateSize) + unsigned int DigestSize() const {return DIGESTSIZE;}; + typedef T_HashWordType HashWordType; + CRYPTOPP_CONSTANT(BLOCKSIZE = T_BlockSize) + +protected: + IteratedHashWithStaticTransform() {this->Init();} + void HashEndianCorrectedBlock(const T_HashWordType *data) {T_Transform::Transform(this->m_state, data);} + void Init() {T_Transform::InitState(this->m_state);} + + T_HashWordType* StateBuf() {return this->m_state;} + FixedSizeAlignedSecBlock m_state; +}; + +NAMESPACE_END + +#endif diff --git a/src/cryptopp/misc.h b/src/cryptopp/misc.h new file mode 100644 index 0000000000..5258e2abca --- /dev/null +++ b/src/cryptopp/misc.h @@ -0,0 +1,1134 @@ +#ifndef CRYPTOPP_MISC_H +#define CRYPTOPP_MISC_H + +#include "cryptopp/cryptlib.h" +#include "cryptopp/smartptr.h" +#include // for memcpy and memmove + +#ifdef _MSC_VER + #include + #if _MSC_VER >= 1400 + // VC2005 workaround: disable declarations that conflict with winnt.h + #define _interlockedbittestandset CRYPTOPP_DISABLED_INTRINSIC_1 + #define _interlockedbittestandreset CRYPTOPP_DISABLED_INTRINSIC_2 + #define _interlockedbittestandset64 CRYPTOPP_DISABLED_INTRINSIC_3 + #define _interlockedbittestandreset64 CRYPTOPP_DISABLED_INTRINSIC_4 + #include + #undef _interlockedbittestandset + #undef _interlockedbittestandreset + #undef _interlockedbittestandset64 + #undef _interlockedbittestandreset64 + #define CRYPTOPP_FAST_ROTATE(x) 1 + #elif _MSC_VER >= 1300 + #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32 | (x) == 64) + #else + #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32) + #endif +#elif (defined(__MWERKS__) && TARGET_CPU_PPC) || \ + (defined(__GNUC__) && (defined(_ARCH_PWR2) || defined(_ARCH_PWR) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || defined(_ARCH_COM))) + #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32) +#elif defined(__GNUC__) && (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X86) // depend on GCC's peephole optimization to generate rotate instructions + #define CRYPTOPP_FAST_ROTATE(x) 1 +#else + #define CRYPTOPP_FAST_ROTATE(x) 0 +#endif + +#ifdef __BORLANDC__ +#include +#endif + +#if defined(__GNUC__) && defined(__linux__) +#define CRYPTOPP_BYTESWAP_AVAILABLE +#include +#endif + +NAMESPACE_BEGIN(CryptoPP) + +// ************** compile-time assertion *************** + +template +struct CompileAssert +{ + static char dummy[2*b-1]; +}; + +#define CRYPTOPP_COMPILE_ASSERT(assertion) CRYPTOPP_COMPILE_ASSERT_INSTANCE(assertion, __LINE__) +#if defined(CRYPTOPP_EXPORTS) || defined(CRYPTOPP_IMPORTS) +#define CRYPTOPP_COMPILE_ASSERT_INSTANCE(assertion, instance) +#else +#define CRYPTOPP_COMPILE_ASSERT_INSTANCE(assertion, instance) static CompileAssert<(assertion)> CRYPTOPP_ASSERT_JOIN(cryptopp_assert_, instance) +#endif +#define CRYPTOPP_ASSERT_JOIN(X, Y) CRYPTOPP_DO_ASSERT_JOIN(X, Y) +#define CRYPTOPP_DO_ASSERT_JOIN(X, Y) X##Y + +// ************** misc classes *************** + +class CRYPTOPP_DLL Empty +{ +}; + +//! _ +template +class CRYPTOPP_NO_VTABLE TwoBases : public BASE1, public BASE2 +{ +}; + +//! _ +template +class CRYPTOPP_NO_VTABLE ThreeBases : public BASE1, public BASE2, public BASE3 +{ +}; + +template +class ObjectHolder +{ +protected: + T m_object; +}; + +class NotCopyable +{ +public: + NotCopyable() {} +private: + NotCopyable(const NotCopyable &); + void operator=(const NotCopyable &); +}; + +template +struct NewObject +{ + T* operator()() const {return new T;} +}; + +/*! This function safely initializes a static object in a multithreaded environment without using locks. + It may leak memory when two threads try to initialize the static object at the same time + but this should be acceptable since each static object is only initialized once per session. +*/ +template , int instance=0> +class Singleton +{ +public: + Singleton(F objectFactory = F()) : m_objectFactory(objectFactory) {} + + // prevent this function from being inlined + CRYPTOPP_NOINLINE const T & Ref(CRYPTOPP_NOINLINE_DOTDOTDOT) const; + +private: + F m_objectFactory; +}; + +template +const T & Singleton::Ref(CRYPTOPP_NOINLINE_DOTDOTDOT) const +{ + static simple_ptr s_pObject; + static char s_objectState = 0; + +retry: + switch (s_objectState) + { + case 0: + s_objectState = 1; + try + { + s_pObject.m_p = m_objectFactory(); + } + catch(...) + { + s_objectState = 0; + throw; + } + s_objectState = 2; + break; + case 1: + goto retry; + default: + break; + } + return *s_pObject.m_p; +} + +// ************** misc functions *************** + +#if (!__STDC_WANT_SECURE_LIB__) +inline void memcpy_s(void *dest, size_t sizeInBytes, const void *src, size_t count) +{ + if (count > sizeInBytes) + throw InvalidArgument("memcpy_s: buffer overflow"); + memcpy(dest, src, count); +} + +inline void memmove_s(void *dest, size_t sizeInBytes, const void *src, size_t count) +{ + if (count > sizeInBytes) + throw InvalidArgument("memmove_s: buffer overflow"); + memmove(dest, src, count); +} +#endif + +inline void * memset_z(void *ptr, int value, size_t num) +{ +// avoid extranous warning on GCC 4.3.2 Ubuntu 8.10 +#if CRYPTOPP_GCC_VERSION >= 30001 + if (__builtin_constant_p(num) && num==0) + return ptr; +#endif + return memset(ptr, value, num); +} + +// can't use std::min or std::max in MSVC60 or Cygwin 1.1.0 +template inline const T& STDMIN(const T& a, const T& b) +{ + return b < a ? b : a; +} + +template inline const T1 UnsignedMin(const T1& a, const T2& b) +{ + CRYPTOPP_COMPILE_ASSERT((sizeof(T1)<=sizeof(T2) && T2(-1)>0) || (sizeof(T1)>sizeof(T2) && T1(-1)>0)); + assert(a==0 || a>0); // GCC workaround: get rid of the warning "comparison is always true due to limited range of data type" + assert(b>=0); + + if (sizeof(T1)<=sizeof(T2)) + return b < (T2)a ? (T1)b : a; + else + return (T1)b < a ? (T1)b : a; +} + +template inline const T& STDMAX(const T& a, const T& b) +{ + return a < b ? b : a; +} + +#define RETURN_IF_NONZERO(x) size_t returnedValue = x; if (returnedValue) return returnedValue + +// this version of the macro is fastest on Pentium 3 and Pentium 4 with MSVC 6 SP5 w/ Processor Pack +#define GETBYTE(x, y) (unsigned int)byte((x)>>(8*(y))) +// these may be faster on other CPUs/compilers +// #define GETBYTE(x, y) (unsigned int)(((x)>>(8*(y)))&255) +// #define GETBYTE(x, y) (((byte *)&(x))[y]) + +#define CRYPTOPP_GET_BYTE_AS_BYTE(x, y) byte((x)>>(8*(y))) + +template +unsigned int Parity(T value) +{ + for (unsigned int i=8*sizeof(value)/2; i>0; i/=2) + value ^= value >> i; + return (unsigned int)value&1; +} + +template +unsigned int BytePrecision(const T &value) +{ + if (!value) + return 0; + + unsigned int l=0, h=8*sizeof(value); + + while (h-l > 8) + { + unsigned int t = (l+h)/2; + if (value >> t) + l = t; + else + h = t; + } + + return h/8; +} + +template +unsigned int BitPrecision(const T &value) +{ + if (!value) + return 0; + + unsigned int l=0, h=8*sizeof(value); + + while (h-l > 1) + { + unsigned int t = (l+h)/2; + if (value >> t) + l = t; + else + h = t; + } + + return h; +} + +template +inline T Crop(T value, size_t size) +{ + if (size < 8*sizeof(value)) + return T(value & ((T(1) << size) - 1)); + else + return value; +} + +template +inline bool SafeConvert(T1 from, T2 &to) +{ + to = (T2)from; + if (from != to || (from > 0) != (to > 0)) + return false; + return true; +} + +inline size_t BitsToBytes(size_t bitCount) +{ + return ((bitCount+7)/(8)); +} + +inline size_t BytesToWords(size_t byteCount) +{ + return ((byteCount+WORD_SIZE-1)/WORD_SIZE); +} + +inline size_t BitsToWords(size_t bitCount) +{ + return ((bitCount+WORD_BITS-1)/(WORD_BITS)); +} + +inline size_t BitsToDwords(size_t bitCount) +{ + return ((bitCount+2*WORD_BITS-1)/(2*WORD_BITS)); +} + +CRYPTOPP_DLL void CRYPTOPP_API xorbuf(byte *buf, const byte *mask, size_t count); +CRYPTOPP_DLL void CRYPTOPP_API xorbuf(byte *output, const byte *input, const byte *mask, size_t count); + +CRYPTOPP_DLL bool CRYPTOPP_API VerifyBufsEqual(const byte *buf1, const byte *buf2, size_t count); + +template +inline bool IsPowerOf2(const T &n) +{ + return n > 0 && (n & (n-1)) == 0; +} + +template +inline T2 ModPowerOf2(const T1 &a, const T2 &b) +{ + assert(IsPowerOf2(b)); + return T2(a) & (b-1); +} + +template +inline T1 RoundDownToMultipleOf(const T1 &n, const T2 &m) +{ + if (IsPowerOf2(m)) + return n - ModPowerOf2(n, m); + else + return n - n%m; +} + +template +inline T1 RoundUpToMultipleOf(const T1 &n, const T2 &m) +{ + if (n+m-1 < n) + throw InvalidArgument("RoundUpToMultipleOf: integer overflow"); + return RoundDownToMultipleOf(n+m-1, m); +} + +template +inline unsigned int GetAlignmentOf(T *dummy=NULL) // VC60 workaround +{ +#ifdef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS + if (sizeof(T) < 16) + return 1; +#endif + +#if (_MSC_VER >= 1300) + return __alignof(T); +#elif defined(__GNUC__) + return __alignof__(T); +#elif CRYPTOPP_BOOL_SLOW_WORD64 + return UnsignedMin(4U, sizeof(T)); +#else + return sizeof(T); +#endif +} + +inline bool IsAlignedOn(const void *p, unsigned int alignment) +{ + return alignment==1 || (IsPowerOf2(alignment) ? ModPowerOf2((size_t)p, alignment) == 0 : (size_t)p % alignment == 0); +} + +template +inline bool IsAligned(const void *p, T *dummy=NULL) // VC60 workaround +{ + return IsAlignedOn(p, GetAlignmentOf()); +} + +#ifdef IS_LITTLE_ENDIAN + typedef LittleEndian NativeByteOrder; +#else + typedef BigEndian NativeByteOrder; +#endif + +inline ByteOrder GetNativeByteOrder() +{ + return NativeByteOrder::ToEnum(); +} + +inline bool NativeByteOrderIs(ByteOrder order) +{ + return order == GetNativeByteOrder(); +} + +template +std::string IntToString(T a, unsigned int base = 10) +{ + if (a == 0) + return "0"; + bool negate = false; + if (a < 0) + { + negate = true; + a = 0-a; // VC .NET does not like -a + } + std::string result; + while (a > 0) + { + T digit = a % base; + result = char((digit < 10 ? '0' : ('a' - 10)) + digit) + result; + a /= base; + } + if (negate) + result = "-" + result; + return result; +} + +template +inline T1 SaturatingSubtract(const T1 &a, const T2 &b) +{ + return T1((a > b) ? (a - b) : 0); +} + +template +inline CipherDir GetCipherDir(const T &obj) +{ + return obj.IsForwardTransformation() ? ENCRYPTION : DECRYPTION; +} + +CRYPTOPP_DLL void CRYPTOPP_API CallNewHandler(); + +inline void IncrementCounterByOne(byte *inout, unsigned int s) +{ + for (int i=s-1, carry=1; i>=0 && carry; i--) + carry = !++inout[i]; +} + +inline void IncrementCounterByOne(byte *output, const byte *input, unsigned int s) +{ + int i, carry; + for (i=s-1, carry=1; i>=0 && carry; i--) + carry = ((output[i] = input[i]+1) == 0); + memcpy_s(output, s, input, i+1); +} + +// ************** rotate functions *************** + +template inline T rotlFixed(T x, unsigned int y) +{ + assert(y < sizeof(T)*8); + return T((x<>(sizeof(T)*8-y))); +} + +template inline T rotrFixed(T x, unsigned int y) +{ + assert(y < sizeof(T)*8); + return T((x>>y) | (x<<(sizeof(T)*8-y))); +} + +template inline T rotlVariable(T x, unsigned int y) +{ + assert(y < sizeof(T)*8); + return T((x<>(sizeof(T)*8-y))); +} + +template inline T rotrVariable(T x, unsigned int y) +{ + assert(y < sizeof(T)*8); + return T((x>>y) | (x<<(sizeof(T)*8-y))); +} + +template inline T rotlMod(T x, unsigned int y) +{ + y %= sizeof(T)*8; + return T((x<>(sizeof(T)*8-y))); +} + +template inline T rotrMod(T x, unsigned int y) +{ + y %= sizeof(T)*8; + return T((x>>y) | (x<<(sizeof(T)*8-y))); +} + +#ifdef _MSC_VER + +template<> inline word32 rotlFixed(word32 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _lrotl(x, y) : x; +} + +template<> inline word32 rotrFixed(word32 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _lrotr(x, y) : x; +} + +template<> inline word32 rotlVariable(word32 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _lrotl(x, y); +} + +template<> inline word32 rotrVariable(word32 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _lrotr(x, y); +} + +template<> inline word32 rotlMod(word32 x, unsigned int y) +{ + return _lrotl(x, y); +} + +template<> inline word32 rotrMod(word32 x, unsigned int y) +{ + return _lrotr(x, y); +} + +#endif // #ifdef _MSC_VER + +#if _MSC_VER >= 1300 && !defined(__INTEL_COMPILER) +// Intel C++ Compiler 10.0 calls a function instead of using the rotate instruction when using these instructions + +template<> inline word64 rotlFixed(word64 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _rotl64(x, y) : x; +} + +template<> inline word64 rotrFixed(word64 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _rotr64(x, y) : x; +} + +template<> inline word64 rotlVariable(word64 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _rotl64(x, y); +} + +template<> inline word64 rotrVariable(word64 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _rotr64(x, y); +} + +template<> inline word64 rotlMod(word64 x, unsigned int y) +{ + return _rotl64(x, y); +} + +template<> inline word64 rotrMod(word64 x, unsigned int y) +{ + return _rotr64(x, y); +} + +#endif // #if _MSC_VER >= 1310 + +#if _MSC_VER >= 1400 && !defined(__INTEL_COMPILER) +// Intel C++ Compiler 10.0 gives undefined externals with these + +template<> inline word16 rotlFixed(word16 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _rotl16(x, y) : x; +} + +template<> inline word16 rotrFixed(word16 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _rotr16(x, y) : x; +} + +template<> inline word16 rotlVariable(word16 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _rotl16(x, y); +} + +template<> inline word16 rotrVariable(word16 x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _rotr16(x, y); +} + +template<> inline word16 rotlMod(word16 x, unsigned int y) +{ + return _rotl16(x, y); +} + +template<> inline word16 rotrMod(word16 x, unsigned int y) +{ + return _rotr16(x, y); +} + +template<> inline byte rotlFixed(byte x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _rotl8(x, y) : x; +} + +template<> inline byte rotrFixed(byte x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return y ? _rotr8(x, y) : x; +} + +template<> inline byte rotlVariable(byte x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _rotl8(x, y); +} + +template<> inline byte rotrVariable(byte x, unsigned int y) +{ + assert(y < 8*sizeof(x)); + return _rotr8(x, y); +} + +template<> inline byte rotlMod(byte x, unsigned int y) +{ + return _rotl8(x, y); +} + +template<> inline byte rotrMod(byte x, unsigned int y) +{ + return _rotr8(x, y); +} + +#endif // #if _MSC_VER >= 1400 + +#if (defined(__MWERKS__) && TARGET_CPU_PPC) + +template<> inline word32 rotlFixed(word32 x, unsigned int y) +{ + assert(y < 32); + return y ? __rlwinm(x,y,0,31) : x; +} + +template<> inline word32 rotrFixed(word32 x, unsigned int y) +{ + assert(y < 32); + return y ? __rlwinm(x,32-y,0,31) : x; +} + +template<> inline word32 rotlVariable(word32 x, unsigned int y) +{ + assert(y < 32); + return (__rlwnm(x,y,0,31)); +} + +template<> inline word32 rotrVariable(word32 x, unsigned int y) +{ + assert(y < 32); + return (__rlwnm(x,32-y,0,31)); +} + +template<> inline word32 rotlMod(word32 x, unsigned int y) +{ + return (__rlwnm(x,y,0,31)); +} + +template<> inline word32 rotrMod(word32 x, unsigned int y) +{ + return (__rlwnm(x,32-y,0,31)); +} + +#endif // #if (defined(__MWERKS__) && TARGET_CPU_PPC) + +// ************** endian reversal *************** + +template +inline unsigned int GetByte(ByteOrder order, T value, unsigned int index) +{ + if (order == LITTLE_ENDIAN_ORDER) + return GETBYTE(value, index); + else + return GETBYTE(value, sizeof(T)-index-1); +} + +inline byte ByteReverse(byte value) +{ + return value; +} + +inline word16 ByteReverse(word16 value) +{ +#ifdef CRYPTOPP_BYTESWAP_AVAILABLE + return bswap_16(value); +#elif defined(_MSC_VER) && _MSC_VER >= 1300 + return _byteswap_ushort(value); +#else + return rotlFixed(value, 8U); +#endif +} + +inline word32 ByteReverse(word32 value) +{ +#if defined(__GNUC__) && defined(CRYPTOPP_X86_ASM_AVAILABLE) + __asm__ ("bswap %0" : "=r" (value) : "0" (value)); + return value; +#elif defined(CRYPTOPP_BYTESWAP_AVAILABLE) + return bswap_32(value); +#elif defined(__MWERKS__) && TARGET_CPU_PPC + return (word32)__lwbrx(&value,0); +#elif _MSC_VER >= 1400 || (_MSC_VER >= 1300 && !defined(_DLL)) + return _byteswap_ulong(value); +#elif CRYPTOPP_FAST_ROTATE(32) + // 5 instructions with rotate instruction, 9 without + return (rotrFixed(value, 8U) & 0xff00ff00) | (rotlFixed(value, 8U) & 0x00ff00ff); +#else + // 6 instructions with rotate instruction, 8 without + value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); + return rotlFixed(value, 16U); +#endif +} + +inline word64 ByteReverse(word64 value) +{ +#if defined(__GNUC__) && defined(CRYPTOPP_X86_ASM_AVAILABLE) && defined(__x86_64__) + __asm__ ("bswap %0" : "=r" (value) : "0" (value)); + return value; +#elif defined(CRYPTOPP_BYTESWAP_AVAILABLE) + return bswap_64(value); +#elif defined(_MSC_VER) && _MSC_VER >= 1300 + return _byteswap_uint64(value); +#elif CRYPTOPP_BOOL_SLOW_WORD64 + return (word64(ByteReverse(word32(value))) << 32) | ByteReverse(word32(value>>32)); +#else + value = ((value & W64LIT(0xFF00FF00FF00FF00)) >> 8) | ((value & W64LIT(0x00FF00FF00FF00FF)) << 8); + value = ((value & W64LIT(0xFFFF0000FFFF0000)) >> 16) | ((value & W64LIT(0x0000FFFF0000FFFF)) << 16); + return rotlFixed(value, 32U); +#endif +} + +inline byte BitReverse(byte value) +{ + value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); + value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); + return rotlFixed(value, 4U); +} + +inline word16 BitReverse(word16 value) +{ + value = ((value & 0xAAAA) >> 1) | ((value & 0x5555) << 1); + value = ((value & 0xCCCC) >> 2) | ((value & 0x3333) << 2); + value = ((value & 0xF0F0) >> 4) | ((value & 0x0F0F) << 4); + return ByteReverse(value); +} + +inline word32 BitReverse(word32 value) +{ + value = ((value & 0xAAAAAAAA) >> 1) | ((value & 0x55555555) << 1); + value = ((value & 0xCCCCCCCC) >> 2) | ((value & 0x33333333) << 2); + value = ((value & 0xF0F0F0F0) >> 4) | ((value & 0x0F0F0F0F) << 4); + return ByteReverse(value); +} + +inline word64 BitReverse(word64 value) +{ +#if CRYPTOPP_BOOL_SLOW_WORD64 + return (word64(BitReverse(word32(value))) << 32) | BitReverse(word32(value>>32)); +#else + value = ((value & W64LIT(0xAAAAAAAAAAAAAAAA)) >> 1) | ((value & W64LIT(0x5555555555555555)) << 1); + value = ((value & W64LIT(0xCCCCCCCCCCCCCCCC)) >> 2) | ((value & W64LIT(0x3333333333333333)) << 2); + value = ((value & W64LIT(0xF0F0F0F0F0F0F0F0)) >> 4) | ((value & W64LIT(0x0F0F0F0F0F0F0F0F)) << 4); + return ByteReverse(value); +#endif +} + +template +inline T BitReverse(T value) +{ + if (sizeof(T) == 1) + return (T)BitReverse((byte)value); + else if (sizeof(T) == 2) + return (T)BitReverse((word16)value); + else if (sizeof(T) == 4) + return (T)BitReverse((word32)value); + else + { + assert(sizeof(T) == 8); + return (T)BitReverse((word64)value); + } +} + +template +inline T ConditionalByteReverse(ByteOrder order, T value) +{ + return NativeByteOrderIs(order) ? value : ByteReverse(value); +} + +template +void ByteReverse(T *out, const T *in, size_t byteCount) +{ + assert(byteCount % sizeof(T) == 0); + size_t count = byteCount/sizeof(T); + for (size_t i=0; i +inline void ConditionalByteReverse(ByteOrder order, T *out, const T *in, size_t byteCount) +{ + if (!NativeByteOrderIs(order)) + ByteReverse(out, in, byteCount); + else if (in != out) + memcpy_s(out, byteCount, in, byteCount); +} + +template +inline void GetUserKey(ByteOrder order, T *out, size_t outlen, const byte *in, size_t inlen) +{ + const size_t U = sizeof(T); + assert(inlen <= outlen*U); + memcpy_s(out, outlen*U, in, inlen); + memset_z((byte *)out+inlen, 0, outlen*U-inlen); + ConditionalByteReverse(order, out, out, RoundUpToMultipleOf(inlen, U)); +} + +#ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS +inline byte UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const byte *) +{ + return block[0]; +} + +inline word16 UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const word16 *) +{ + return (order == BIG_ENDIAN_ORDER) + ? block[1] | (block[0] << 8) + : block[0] | (block[1] << 8); +} + +inline word32 UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const word32 *) +{ + return (order == BIG_ENDIAN_ORDER) + ? word32(block[3]) | (word32(block[2]) << 8) | (word32(block[1]) << 16) | (word32(block[0]) << 24) + : word32(block[0]) | (word32(block[1]) << 8) | (word32(block[2]) << 16) | (word32(block[3]) << 24); +} + +inline word64 UnalignedGetWordNonTemplate(ByteOrder order, const byte *block, const word64 *) +{ + return (order == BIG_ENDIAN_ORDER) + ? + (word64(block[7]) | + (word64(block[6]) << 8) | + (word64(block[5]) << 16) | + (word64(block[4]) << 24) | + (word64(block[3]) << 32) | + (word64(block[2]) << 40) | + (word64(block[1]) << 48) | + (word64(block[0]) << 56)) + : + (word64(block[0]) | + (word64(block[1]) << 8) | + (word64(block[2]) << 16) | + (word64(block[3]) << 24) | + (word64(block[4]) << 32) | + (word64(block[5]) << 40) | + (word64(block[6]) << 48) | + (word64(block[7]) << 56)); +} + +inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, byte value, const byte *xorBlock) +{ + block[0] = xorBlock ? (value ^ xorBlock[0]) : value; +} + +inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, word16 value, const byte *xorBlock) +{ + if (order == BIG_ENDIAN_ORDER) + { + if (xorBlock) + { + block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + } + else + { + block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + } + } + else + { + if (xorBlock) + { + block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + } + else + { + block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + } + } +} + +inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, word32 value, const byte *xorBlock) +{ + if (order == BIG_ENDIAN_ORDER) + { + if (xorBlock) + { + block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + } + else + { + block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + } + } + else + { + if (xorBlock) + { + block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + } + else + { + block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + } + } +} + +inline void UnalignedPutWordNonTemplate(ByteOrder order, byte *block, word64 value, const byte *xorBlock) +{ + if (order == BIG_ENDIAN_ORDER) + { + if (xorBlock) + { + block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); + block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); + block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); + block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); + block[4] = xorBlock[4] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + block[5] = xorBlock[5] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[6] = xorBlock[6] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[7] = xorBlock[7] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + } + else + { + block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); + block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); + block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); + block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); + block[4] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + block[5] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[6] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[7] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + } + } + else + { + if (xorBlock) + { + block[0] = xorBlock[0] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + block[1] = xorBlock[1] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[2] = xorBlock[2] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[3] = xorBlock[3] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + block[4] = xorBlock[4] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); + block[5] = xorBlock[5] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); + block[6] = xorBlock[6] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); + block[7] = xorBlock[7] ^ CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); + } + else + { + block[0] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 0); + block[1] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 1); + block[2] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 2); + block[3] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 3); + block[4] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 4); + block[5] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 5); + block[6] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 6); + block[7] = CRYPTOPP_GET_BYTE_AS_BYTE(value, 7); + } + } +} +#endif // #ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS + +template +inline T GetWord(bool assumeAligned, ByteOrder order, const byte *block) +{ +#ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS + if (!assumeAligned) + return UnalignedGetWordNonTemplate(order, block, (T*)NULL); + assert(IsAligned(block)); +#endif + return ConditionalByteReverse(order, *reinterpret_cast(block)); +} + +template +inline void GetWord(bool assumeAligned, ByteOrder order, T &result, const byte *block) +{ + result = GetWord(assumeAligned, order, block); +} + +template +inline void PutWord(bool assumeAligned, ByteOrder order, byte *block, T value, const byte *xorBlock = NULL) +{ +#ifndef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS + if (!assumeAligned) + return UnalignedPutWordNonTemplate(order, block, value, xorBlock); + assert(IsAligned(block)); + assert(IsAligned(xorBlock)); +#endif + *reinterpret_cast(block) = ConditionalByteReverse(order, value) ^ (xorBlock ? *reinterpret_cast(xorBlock) : 0); +} + +template +class GetBlock +{ +public: + GetBlock(const void *block) + : m_block((const byte *)block) {} + + template + inline GetBlock & operator()(U &x) + { + CRYPTOPP_COMPILE_ASSERT(sizeof(U) >= sizeof(T)); + x = GetWord(A, B::ToEnum(), m_block); + m_block += sizeof(T); + return *this; + } + +private: + const byte *m_block; +}; + +template +class PutBlock +{ +public: + PutBlock(const void *xorBlock, void *block) + : m_xorBlock((const byte *)xorBlock), m_block((byte *)block) {} + + template + inline PutBlock & operator()(U x) + { + PutWord(A, B::ToEnum(), m_block, (T)x, m_xorBlock); + m_block += sizeof(T); + if (m_xorBlock) + m_xorBlock += sizeof(T); + return *this; + } + +private: + const byte *m_xorBlock; + byte *m_block; +}; + +template +struct BlockGetAndPut +{ + // function needed because of C++ grammatical ambiguity between expression-statements and declarations + static inline GetBlock Get(const void *block) {return GetBlock(block);} + typedef PutBlock Put; +}; + +template +std::string WordToString(T value, ByteOrder order = BIG_ENDIAN_ORDER) +{ + if (!NativeByteOrderIs(order)) + value = ByteReverse(value); + + return std::string((char *)&value, sizeof(value)); +} + +template +T StringToWord(const std::string &str, ByteOrder order = BIG_ENDIAN_ORDER) +{ + T value = 0; + memcpy_s(&value, sizeof(value), str.data(), UnsignedMin(str.size(), sizeof(value))); + return NativeByteOrderIs(order) ? value : ByteReverse(value); +} + +// ************** help remove warning on g++ *************** + +template struct SafeShifter; + +template<> struct SafeShifter +{ + template + static inline T RightShift(T value, unsigned int bits) + { + return 0; + } + + template + static inline T LeftShift(T value, unsigned int bits) + { + return 0; + } +}; + +template<> struct SafeShifter +{ + template + static inline T RightShift(T value, unsigned int bits) + { + return value >> bits; + } + + template + static inline T LeftShift(T value, unsigned int bits) + { + return value << bits; + } +}; + +template +inline T SafeRightShift(T value) +{ + return SafeShifter<(bits>=(8*sizeof(T)))>::RightShift(value, bits); +} + +template +inline T SafeLeftShift(T value) +{ + return SafeShifter<(bits>=(8*sizeof(T)))>::LeftShift(value, bits); +} + +// ************** use one buffer for multiple data members *************** + +#define CRYPTOPP_BLOCK_1(n, t, s) t* m_##n() {return (t *)(m_aggregate+0);} size_t SS1() {return sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_2(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS1());} size_t SS2() {return SS1()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_3(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS2());} size_t SS3() {return SS2()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_4(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS3());} size_t SS4() {return SS3()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_5(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS4());} size_t SS5() {return SS4()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_6(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS5());} size_t SS6() {return SS5()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_7(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS6());} size_t SS7() {return SS6()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCK_8(n, t, s) t* m_##n() {return (t *)(m_aggregate+SS7());} size_t SS8() {return SS7()+sizeof(t)*(s);} size_t m_##n##Size() {return (s);} +#define CRYPTOPP_BLOCKS_END(i) size_t SST() {return SS##i();} void AllocateBlocks() {m_aggregate.New(SST());} AlignedSecByteBlock m_aggregate; + +NAMESPACE_END + +#endif diff --git a/src/cryptopp/pch.h b/src/cryptopp/pch.h new file mode 100644 index 0000000000..418c39076d --- /dev/null +++ b/src/cryptopp/pch.h @@ -0,0 +1,21 @@ +#ifndef CRYPTOPP_PCH_H +#define CRYPTOPP_PCH_H + +#ifdef CRYPTOPP_GENERATE_X64_MASM + + #include "cpu.h" + +#else + + #include "config.h" + + #ifdef USE_PRECOMPILED_HEADERS + #include "simple.h" + #include "secblock.h" + #include "misc.h" + #include "smartptr.h" + #endif + +#endif + +#endif diff --git a/src/cryptopp/secblock.h b/src/cryptopp/secblock.h new file mode 100644 index 0000000000..6433f97349 --- /dev/null +++ b/src/cryptopp/secblock.h @@ -0,0 +1,501 @@ +// secblock.h - written and placed in the public domain by Wei Dai + +#ifndef CRYPTOPP_SECBLOCK_H +#define CRYPTOPP_SECBLOCK_H + +#include "cryptopp/config.h" +#include "cryptopp/misc.h" +#include + +#if defined(CRYPTOPP_MEMALIGN_AVAILABLE) || defined(CRYPTOPP_MM_MALLOC_AVAILABLE) || defined(QNX) + #include +#else + #include +#endif + +NAMESPACE_BEGIN(CryptoPP) + +// ************** secure memory allocation *************** + +template +class AllocatorBase +{ +public: + typedef T value_type; + typedef size_t size_type; +#ifdef CRYPTOPP_MSVCRT6 + typedef ptrdiff_t difference_type; +#else + typedef std::ptrdiff_t difference_type; +#endif + typedef T * pointer; + typedef const T * const_pointer; + typedef T & reference; + typedef const T & const_reference; + + pointer address(reference r) const {return (&r);} + const_pointer address(const_reference r) const {return (&r); } + void construct(pointer p, const T& val) {new (p) T(val);} + void destroy(pointer p) {p->~T();} + size_type max_size() const {return ~size_type(0)/sizeof(T);} // switch to std::numeric_limits::max later + +protected: + static void CheckSize(size_t n) + { + if (n > ~size_t(0) / sizeof(T)) + throw InvalidArgument("AllocatorBase: requested size would cause integer overflow"); + } +}; + +#define CRYPTOPP_INHERIT_ALLOCATOR_TYPES \ +typedef typename AllocatorBase::value_type value_type;\ +typedef typename AllocatorBase::size_type size_type;\ +typedef typename AllocatorBase::difference_type difference_type;\ +typedef typename AllocatorBase::pointer pointer;\ +typedef typename AllocatorBase::const_pointer const_pointer;\ +typedef typename AllocatorBase::reference reference;\ +typedef typename AllocatorBase::const_reference const_reference; + +#if defined(_MSC_VER) && (_MSC_VER < 1300) +// this pragma causes an internal compiler error if placed immediately before std::swap(a, b) +#pragma warning(push) +#pragma warning(disable: 4700) // VC60 workaround: don't know how to get rid of this warning +#endif + +template +typename A::pointer StandardReallocate(A& a, T *p, typename A::size_type oldSize, typename A::size_type newSize, bool preserve) +{ + if (oldSize == newSize) + return p; + + if (preserve) + { + typename A::pointer newPointer = a.allocate(newSize, NULL); + memcpy_s(newPointer, sizeof(T)*newSize, p, sizeof(T)*STDMIN(oldSize, newSize)); + a.deallocate(p, oldSize); + return newPointer; + } + else + { + a.deallocate(p, oldSize); + return a.allocate(newSize, NULL); + } +} + +#if defined(_MSC_VER) && (_MSC_VER < 1300) +#pragma warning(pop) +#endif + +template +class AllocatorWithCleanup : public AllocatorBase +{ +public: + CRYPTOPP_INHERIT_ALLOCATOR_TYPES + + pointer allocate(size_type n, const void * = NULL) + { + CheckSize(n); + if (n == 0) + return NULL; + + if (CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16 && n*sizeof(T) >= 16) + { + byte *p; + #ifdef CRYPTOPP_MM_MALLOC_AVAILABLE + while (!(p = (byte *)_mm_malloc(sizeof(T)*n, 16))) + #elif defined(CRYPTOPP_MEMALIGN_AVAILABLE) + while (!(p = (byte *)memalign(16, sizeof(T)*n))) + #elif defined(CRYPTOPP_MALLOC_ALIGNMENT_IS_16) + while (!(p = (byte *)malloc(sizeof(T)*n))) + #else + while (!(p = (byte *)malloc(sizeof(T)*n + 16))) + #endif + CallNewHandler(); + + #ifdef CRYPTOPP_NO_ALIGNED_ALLOC + size_t adjustment = 16-((size_t)p%16); + p += adjustment; + p[-1] = (byte)adjustment; + #endif + + assert(IsAlignedOn(p, 16)); + return (pointer)p; + } + + pointer p; + while (!(p = (pointer)malloc(sizeof(T)*n))) + CallNewHandler(); + return p; + } + + void deallocate(void *p, size_type n) + { + memset_z(p, 0, n*sizeof(T)); + + if (CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16 && n*sizeof(T) >= 16) + { + #ifdef CRYPTOPP_MM_MALLOC_AVAILABLE + _mm_free(p); + #elif defined(CRYPTOPP_NO_ALIGNED_ALLOC) + p = (byte *)p - ((byte *)p)[-1]; + free(p); + #else + free(p); + #endif + return; + } + + free(p); + } + + pointer reallocate(T *p, size_type oldSize, size_type newSize, bool preserve) + { + return StandardReallocate(*this, p, oldSize, newSize, preserve); + } + + // VS.NET STL enforces the policy of "All STL-compliant allocators have to provide a + // template class member called rebind". + template struct rebind { typedef AllocatorWithCleanup other; }; +#if _MSC_VER >= 1500 + AllocatorWithCleanup() {} + template AllocatorWithCleanup(const AllocatorWithCleanup &) {} +#endif +}; + +CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; +CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; +CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; +CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; +#if CRYPTOPP_BOOL_X86 +CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; // for Integer +#endif + +template +class NullAllocator : public AllocatorBase +{ +public: + CRYPTOPP_INHERIT_ALLOCATOR_TYPES + + pointer allocate(size_type n, const void * = NULL) + { + assert(false); + return NULL; + } + + void deallocate(void *p, size_type n) + { + //// Bitcoin: don't know why this trips, probably a false alarm, depends on the compiler used. + //assert(false); + } + + size_type max_size() const {return 0;} +}; + +// This allocator can't be used with standard collections because +// they require that all objects of the same allocator type are equivalent. +// So this is for use with SecBlock only. +template , bool T_Align16 = false> +class FixedSizeAllocatorWithCleanup : public AllocatorBase +{ +public: + CRYPTOPP_INHERIT_ALLOCATOR_TYPES + + FixedSizeAllocatorWithCleanup() : m_allocated(false) {} + + pointer allocate(size_type n) + { + assert(IsAlignedOn(m_array, 8)); + + if (n <= S && !m_allocated) + { + m_allocated = true; + return GetAlignedArray(); + } + else + return m_fallbackAllocator.allocate(n); + } + + pointer allocate(size_type n, const void *hint) + { + if (n <= S && !m_allocated) + { + m_allocated = true; + return GetAlignedArray(); + } + else + return m_fallbackAllocator.allocate(n, hint); + } + + void deallocate(void *p, size_type n) + { + if (p == GetAlignedArray()) + { + assert(n <= S); + assert(m_allocated); + m_allocated = false; + memset(p, 0, n*sizeof(T)); + } + else + m_fallbackAllocator.deallocate(p, n); + } + + pointer reallocate(pointer p, size_type oldSize, size_type newSize, bool preserve) + { + if (p == GetAlignedArray() && newSize <= S) + { + assert(oldSize <= S); + if (oldSize > newSize) + memset(p + newSize, 0, (oldSize-newSize)*sizeof(T)); + return p; + } + + pointer newPointer = allocate(newSize, NULL); + if (preserve) + memcpy(newPointer, p, sizeof(T)*STDMIN(oldSize, newSize)); + deallocate(p, oldSize); + return newPointer; + } + + size_type max_size() const {return STDMAX(m_fallbackAllocator.max_size(), S);} + +private: +#ifdef __BORLANDC__ + T* GetAlignedArray() {return m_array;} + T m_array[S]; +#else + T* GetAlignedArray() {return (CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16) ? (T*)(((byte *)m_array) + (0-(size_t)m_array)%16) : m_array;} + CRYPTOPP_ALIGN_DATA(8) T m_array[(CRYPTOPP_BOOL_ALIGN16_ENABLED && T_Align16) ? S+8/sizeof(T) : S]; +#endif + A m_fallbackAllocator; + bool m_allocated; +}; + +//! a block of memory allocated using A +template > +class SecBlock +{ +public: + typedef typename A::value_type value_type; + typedef typename A::pointer iterator; + typedef typename A::const_pointer const_iterator; + typedef typename A::size_type size_type; + + explicit SecBlock(size_type size=0) + : m_size(size) {m_ptr = m_alloc.allocate(size, NULL);} + SecBlock(const SecBlock &t) + : m_size(t.m_size) {m_ptr = m_alloc.allocate(m_size, NULL); memcpy_s(m_ptr, m_size*sizeof(T), t.m_ptr, m_size*sizeof(T));} + SecBlock(const T *t, size_type len) + : m_size(len) + { + m_ptr = m_alloc.allocate(len, NULL); + if (t == NULL) + memset_z(m_ptr, 0, len*sizeof(T)); + else + memcpy(m_ptr, t, len*sizeof(T)); + } + + ~SecBlock() + {m_alloc.deallocate(m_ptr, m_size);} + +#ifdef __BORLANDC__ + operator T *() const + {return (T*)m_ptr;} +#else + operator const void *() const + {return m_ptr;} + operator void *() + {return m_ptr;} + + operator const T *() const + {return m_ptr;} + operator T *() + {return m_ptr;} +#endif + +// T *operator +(size_type offset) +// {return m_ptr+offset;} + +// const T *operator +(size_type offset) const +// {return m_ptr+offset;} + +// T& operator[](size_type index) +// {assert(index >= 0 && index < m_size); return m_ptr[index];} + +// const T& operator[](size_type index) const +// {assert(index >= 0 && index < m_size); return m_ptr[index];} + + iterator begin() + {return m_ptr;} + const_iterator begin() const + {return m_ptr;} + iterator end() + {return m_ptr+m_size;} + const_iterator end() const + {return m_ptr+m_size;} + + typename A::pointer data() {return m_ptr;} + typename A::const_pointer data() const {return m_ptr;} + + size_type size() const {return m_size;} + bool empty() const {return m_size == 0;} + + byte * BytePtr() {return (byte *)m_ptr;} + const byte * BytePtr() const {return (const byte *)m_ptr;} + size_type SizeInBytes() const {return m_size*sizeof(T);} + + //! set contents and size + void Assign(const T *t, size_type len) + { + New(len); + memcpy_s(m_ptr, m_size*sizeof(T), t, len*sizeof(T)); + } + + //! copy contents and size from another SecBlock + void Assign(const SecBlock &t) + { + New(t.m_size); + memcpy_s(m_ptr, m_size*sizeof(T), t.m_ptr, m_size*sizeof(T)); + } + + SecBlock& operator=(const SecBlock &t) + { + Assign(t); + return *this; + } + + // append to this object + SecBlock& operator+=(const SecBlock &t) + { + size_type oldSize = m_size; + Grow(m_size+t.m_size); + memcpy_s(m_ptr+oldSize, m_size*sizeof(T), t.m_ptr, t.m_size*sizeof(T)); + return *this; + } + + // append operator + SecBlock operator+(const SecBlock &t) + { + SecBlock result(m_size+t.m_size); + memcpy_s(result.m_ptr, result.m_size*sizeof(T), m_ptr, m_size*sizeof(T)); + memcpy_s(result.m_ptr+m_size, t.m_size*sizeof(T), t.m_ptr, t.m_size*sizeof(T)); + return result; + } + + bool operator==(const SecBlock &t) const + { + return m_size == t.m_size && VerifyBufsEqual(m_ptr, t.m_ptr, m_size*sizeof(T)); + } + + bool operator!=(const SecBlock &t) const + { + return !operator==(t); + } + + //! change size, without preserving contents + void New(size_type newSize) + { + m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, false); + m_size = newSize; + } + + //! change size and set contents to 0 + void CleanNew(size_type newSize) + { + New(newSize); + memset_z(m_ptr, 0, m_size*sizeof(T)); + } + + //! change size only if newSize > current size. contents are preserved + void Grow(size_type newSize) + { + if (newSize > m_size) + { + m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); + m_size = newSize; + } + } + + //! change size only if newSize > current size. contents are preserved and additional area is set to 0 + void CleanGrow(size_type newSize) + { + if (newSize > m_size) + { + m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); + memset(m_ptr+m_size, 0, (newSize-m_size)*sizeof(T)); + m_size = newSize; + } + } + + //! change size and preserve contents + void resize(size_type newSize) + { + m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); + m_size = newSize; + } + + //! swap contents and size with another SecBlock + void swap(SecBlock &b) + { + std::swap(m_alloc, b.m_alloc); + std::swap(m_size, b.m_size); + std::swap(m_ptr, b.m_ptr); + } + +//private: + A m_alloc; + size_type m_size; + T *m_ptr; +}; + +typedef SecBlock SecByteBlock; +typedef SecBlock > AlignedSecByteBlock; +typedef SecBlock SecWordBlock; + +//! a SecBlock with fixed size, allocated statically +template > +class FixedSizeSecBlock : public SecBlock +{ +public: + explicit FixedSizeSecBlock() : SecBlock(S) {} +}; + +template +class FixedSizeAlignedSecBlock : public FixedSizeSecBlock, T_Align16> > +{ +}; + +//! a SecBlock that preallocates size S statically, and uses the heap when this size is exceeded +template > > +class SecBlockWithHint : public SecBlock +{ +public: + explicit SecBlockWithHint(size_t size) : SecBlock(size) {} +}; + +template +inline bool operator==(const CryptoPP::AllocatorWithCleanup&, const CryptoPP::AllocatorWithCleanup&) {return (true);} +template +inline bool operator!=(const CryptoPP::AllocatorWithCleanup&, const CryptoPP::AllocatorWithCleanup&) {return (false);} + +NAMESPACE_END + +NAMESPACE_BEGIN(std) +template +inline void swap(CryptoPP::SecBlock &a, CryptoPP::SecBlock &b) +{ + a.swap(b); +} + +#if defined(_STLP_DONT_SUPPORT_REBIND_MEMBER_TEMPLATE) || (defined(_STLPORT_VERSION) && !defined(_STLP_MEMBER_TEMPLATE_CLASSES)) +// working for STLport 5.1.3 and MSVC 6 SP5 +template +inline CryptoPP::AllocatorWithCleanup<_Tp2>& +__stl_alloc_rebind(CryptoPP::AllocatorWithCleanup<_Tp1>& __a, const _Tp2*) +{ + return (CryptoPP::AllocatorWithCleanup<_Tp2>&)(__a); +} +#endif + +NAMESPACE_END + +#endif diff --git a/src/cryptopp/sha.cpp b/src/cryptopp/sha.cpp new file mode 100644 index 0000000000..1ff6c0b45c --- /dev/null +++ b/src/cryptopp/sha.cpp @@ -0,0 +1,899 @@ +// sha.cpp - modified by Wei Dai from Steve Reid's public domain sha1.c + +// Steve Reid implemented SHA-1. Wei Dai implemented SHA-2. +// Both are in the public domain. + +// use "cl /EP /P /DCRYPTOPP_GENERATE_X64_MASM sha.cpp" to generate MASM code + +#include "cryptopp/pch.h" + +#ifndef CRYPTOPP_IMPORTS +#ifndef CRYPTOPP_GENERATE_X64_MASM + +#include "cryptopp/sha.h" +#include "cryptopp/misc.h" +#include "cryptopp/cpu.h" + +NAMESPACE_BEGIN(CryptoPP) + +// start of Steve Reid's code + +#define blk0(i) (W[i] = data[i]) +#define blk1(i) (W[i&15] = rotlFixed(W[(i+13)&15]^W[(i+8)&15]^W[(i+2)&15]^W[i&15],1)) + +void SHA1::InitState(HashWordType *state) +{ + state[0] = 0x67452301L; + state[1] = 0xEFCDAB89L; + state[2] = 0x98BADCFEL; + state[3] = 0x10325476L; + state[4] = 0xC3D2E1F0L; +} + +#define f1(x,y,z) (z^(x&(y^z))) +#define f2(x,y,z) (x^y^z) +#define f3(x,y,z) ((x&y)|(z&(x|y))) +#define f4(x,y,z) (x^y^z) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=f1(w,x,y)+blk0(i)+0x5A827999+rotlFixed(v,5);w=rotlFixed(w,30); +#define R1(v,w,x,y,z,i) z+=f1(w,x,y)+blk1(i)+0x5A827999+rotlFixed(v,5);w=rotlFixed(w,30); +#define R2(v,w,x,y,z,i) z+=f2(w,x,y)+blk1(i)+0x6ED9EBA1+rotlFixed(v,5);w=rotlFixed(w,30); +#define R3(v,w,x,y,z,i) z+=f3(w,x,y)+blk1(i)+0x8F1BBCDC+rotlFixed(v,5);w=rotlFixed(w,30); +#define R4(v,w,x,y,z,i) z+=f4(w,x,y)+blk1(i)+0xCA62C1D6+rotlFixed(v,5);w=rotlFixed(w,30); + +void SHA1::Transform(word32 *state, const word32 *data) +{ + word32 W[16]; + /* Copy context->state[] to working vars */ + word32 a = state[0]; + word32 b = state[1]; + word32 c = state[2]; + word32 d = state[3]; + word32 e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; +} + +// end of Steve Reid's code + +// ************************************************************* + +void SHA224::InitState(HashWordType *state) +{ + static const word32 s[8] = {0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4}; + memcpy(state, s, sizeof(s)); +} + +void SHA256::InitState(HashWordType *state) +{ + static const word32 s[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + memcpy(state, s, sizeof(s)); +} + +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE +CRYPTOPP_ALIGN_DATA(16) extern const word32 SHA256_K[64] CRYPTOPP_SECTION_ALIGN16 = { +#else +extern const word32 SHA256_K[64] = { +#endif + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +#endif // #ifndef CRYPTOPP_GENERATE_X64_MASM + +#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_GENERATE_X64_MASM) + +#pragma warning(disable: 4731) // frame pointer register 'ebp' modified by inline assembly code + +static void CRYPTOPP_FASTCALL X86_SHA256_HashBlocks(word32 *state, const word32 *data, size_t len +#if defined(_MSC_VER) && (_MSC_VER == 1200) + , ... // VC60 workaround: prevent VC 6 from inlining this function +#endif + ) +{ +#if defined(_MSC_VER) && (_MSC_VER == 1200) + AS2(mov ecx, [state]) + AS2(mov edx, [data]) +#endif + + #define LOCALS_SIZE 8*4 + 16*4 + 4*WORD_SZ + #define H(i) [BASE+ASM_MOD(1024+7-(i),8)*4] + #define G(i) H(i+1) + #define F(i) H(i+2) + #define E(i) H(i+3) + #define D(i) H(i+4) + #define C(i) H(i+5) + #define B(i) H(i+6) + #define A(i) H(i+7) + #define Wt(i) BASE+8*4+ASM_MOD(1024+15-(i),16)*4 + #define Wt_2(i) Wt((i)-2) + #define Wt_15(i) Wt((i)-15) + #define Wt_7(i) Wt((i)-7) + #define K_END [BASE+8*4+16*4+0*WORD_SZ] + #define STATE_SAVE [BASE+8*4+16*4+1*WORD_SZ] + #define DATA_SAVE [BASE+8*4+16*4+2*WORD_SZ] + #define DATA_END [BASE+8*4+16*4+3*WORD_SZ] + #define Kt(i) WORD_REG(si)+(i)*4 +#if CRYPTOPP_BOOL_X86 + #define BASE esp+4 +#elif defined(__GNUC__) + #define BASE r8 +#else + #define BASE rsp +#endif + +#define RA0(i, edx, edi) \ + AS2( add edx, [Kt(i)] )\ + AS2( add edx, [Wt(i)] )\ + AS2( add edx, H(i) )\ + +#define RA1(i, edx, edi) + +#define RB0(i, edx, edi) + +#define RB1(i, edx, edi) \ + AS2( mov AS_REG_7d, [Wt_2(i)] )\ + AS2( mov edi, [Wt_15(i)])\ + AS2( mov ebx, AS_REG_7d )\ + AS2( shr AS_REG_7d, 10 )\ + AS2( ror ebx, 17 )\ + AS2( xor AS_REG_7d, ebx )\ + AS2( ror ebx, 2 )\ + AS2( xor ebx, AS_REG_7d )/* s1(W_t-2) */\ + AS2( add ebx, [Wt_7(i)])\ + AS2( mov AS_REG_7d, edi )\ + AS2( shr AS_REG_7d, 3 )\ + AS2( ror edi, 7 )\ + AS2( add ebx, [Wt(i)])/* s1(W_t-2) + W_t-7 + W_t-16 */\ + AS2( xor AS_REG_7d, edi )\ + AS2( add edx, [Kt(i)])\ + AS2( ror edi, 11 )\ + AS2( add edx, H(i) )\ + AS2( xor AS_REG_7d, edi )/* s0(W_t-15) */\ + AS2( add AS_REG_7d, ebx )/* W_t = s1(W_t-2) + W_t-7 + s0(W_t-15) W_t-16*/\ + AS2( mov [Wt(i)], AS_REG_7d)\ + AS2( add edx, AS_REG_7d )\ + +#define ROUND(i, r, eax, ecx, edi, edx)\ + /* in: edi = E */\ + /* unused: eax, ecx, temp: ebx, AS_REG_7d, out: edx = T1 */\ + AS2( mov edx, F(i) )\ + AS2( xor edx, G(i) )\ + AS2( and edx, edi )\ + AS2( xor edx, G(i) )/* Ch(E,F,G) = (G^(E&(F^G))) */\ + AS2( mov AS_REG_7d, edi )\ + AS2( ror edi, 6 )\ + AS2( ror AS_REG_7d, 25 )\ + RA##r(i, edx, edi )/* H + Wt + Kt + Ch(E,F,G) */\ + AS2( xor AS_REG_7d, edi )\ + AS2( ror edi, 5 )\ + AS2( xor AS_REG_7d, edi )/* S1(E) */\ + AS2( add edx, AS_REG_7d )/* T1 = S1(E) + Ch(E,F,G) + H + Wt + Kt */\ + RB##r(i, edx, edi )/* H + Wt + Kt + Ch(E,F,G) */\ + /* in: ecx = A, eax = B^C, edx = T1 */\ + /* unused: edx, temp: ebx, AS_REG_7d, out: eax = A, ecx = B^C, edx = E */\ + AS2( mov ebx, ecx )\ + AS2( xor ecx, B(i) )/* A^B */\ + AS2( and eax, ecx )\ + AS2( xor eax, B(i) )/* Maj(A,B,C) = B^((A^B)&(B^C) */\ + AS2( mov AS_REG_7d, ebx )\ + AS2( ror ebx, 2 )\ + AS2( add eax, edx )/* T1 + Maj(A,B,C) */\ + AS2( add edx, D(i) )\ + AS2( mov D(i), edx )\ + AS2( ror AS_REG_7d, 22 )\ + AS2( xor AS_REG_7d, ebx )\ + AS2( ror ebx, 11 )\ + AS2( xor AS_REG_7d, ebx )\ + AS2( add eax, AS_REG_7d )/* T1 + S0(A) + Maj(A,B,C) */\ + AS2( mov H(i), eax )\ + +#define SWAP_COPY(i) \ + AS2( mov WORD_REG(bx), [WORD_REG(dx)+i*WORD_SZ])\ + AS1( bswap WORD_REG(bx))\ + AS2( mov [Wt(i*(1+CRYPTOPP_BOOL_X64)+CRYPTOPP_BOOL_X64)], WORD_REG(bx)) + +#if defined(__GNUC__) + #if CRYPTOPP_BOOL_X64 + FixedSizeAlignedSecBlock workspace; + #endif + __asm__ __volatile__ + ( + #if CRYPTOPP_BOOL_X64 + "lea %4, %%r8;" + #endif + ".intel_syntax noprefix;" +#elif defined(CRYPTOPP_GENERATE_X64_MASM) + ALIGN 8 + X86_SHA256_HashBlocks PROC FRAME + rex_push_reg rsi + push_reg rdi + push_reg rbx + push_reg rbp + alloc_stack(LOCALS_SIZE+8) + .endprolog + mov rdi, r8 + lea rsi, [?SHA256_K@CryptoPP@@3QBIB + 48*4] +#endif + +#if CRYPTOPP_BOOL_X86 + #ifndef __GNUC__ + AS2( mov edi, [len]) + AS2( lea WORD_REG(si), [SHA256_K+48*4]) + #endif + #if !defined(_MSC_VER) || (_MSC_VER < 1400) + AS_PUSH_IF86(bx) + #endif + + AS_PUSH_IF86(bp) + AS2( mov ebx, esp) + AS2( and esp, -16) + AS2( sub WORD_REG(sp), LOCALS_SIZE) + AS_PUSH_IF86(bx) +#endif + AS2( mov STATE_SAVE, WORD_REG(cx)) + AS2( mov DATA_SAVE, WORD_REG(dx)) + AS2( add WORD_REG(di), WORD_REG(dx)) + AS2( mov DATA_END, WORD_REG(di)) + AS2( mov K_END, WORD_REG(si)) + +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE +#if CRYPTOPP_BOOL_X86 + AS2( test edi, 1) + ASJ( jnz, 2, f) +#endif + AS2( movdqa xmm0, XMMWORD_PTR [WORD_REG(cx)+0*16]) + AS2( movdqa xmm1, XMMWORD_PTR [WORD_REG(cx)+1*16]) +#endif + +#if CRYPTOPP_BOOL_X86 +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + ASJ( jmp, 0, f) +#endif + ASL(2) // non-SSE2 + AS2( mov esi, ecx) + AS2( lea edi, A(0)) + AS2( mov ecx, 8) + AS1( rep movsd) + AS2( mov esi, K_END) + ASJ( jmp, 3, f) +#endif + +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + ASL(0) + AS2( movdqa E(0), xmm1) + AS2( movdqa A(0), xmm0) +#endif +#if CRYPTOPP_BOOL_X86 + ASL(3) +#endif + AS2( sub WORD_REG(si), 48*4) + SWAP_COPY(0) SWAP_COPY(1) SWAP_COPY(2) SWAP_COPY(3) + SWAP_COPY(4) SWAP_COPY(5) SWAP_COPY(6) SWAP_COPY(7) +#if CRYPTOPP_BOOL_X86 + SWAP_COPY(8) SWAP_COPY(9) SWAP_COPY(10) SWAP_COPY(11) + SWAP_COPY(12) SWAP_COPY(13) SWAP_COPY(14) SWAP_COPY(15) +#endif + AS2( mov edi, E(0)) // E + AS2( mov eax, B(0)) // B + AS2( xor eax, C(0)) // B^C + AS2( mov ecx, A(0)) // A + + ROUND(0, 0, eax, ecx, edi, edx) + ROUND(1, 0, ecx, eax, edx, edi) + ROUND(2, 0, eax, ecx, edi, edx) + ROUND(3, 0, ecx, eax, edx, edi) + ROUND(4, 0, eax, ecx, edi, edx) + ROUND(5, 0, ecx, eax, edx, edi) + ROUND(6, 0, eax, ecx, edi, edx) + ROUND(7, 0, ecx, eax, edx, edi) + ROUND(8, 0, eax, ecx, edi, edx) + ROUND(9, 0, ecx, eax, edx, edi) + ROUND(10, 0, eax, ecx, edi, edx) + ROUND(11, 0, ecx, eax, edx, edi) + ROUND(12, 0, eax, ecx, edi, edx) + ROUND(13, 0, ecx, eax, edx, edi) + ROUND(14, 0, eax, ecx, edi, edx) + ROUND(15, 0, ecx, eax, edx, edi) + + ASL(1) + AS2(add WORD_REG(si), 4*16) + ROUND(0, 1, eax, ecx, edi, edx) + ROUND(1, 1, ecx, eax, edx, edi) + ROUND(2, 1, eax, ecx, edi, edx) + ROUND(3, 1, ecx, eax, edx, edi) + ROUND(4, 1, eax, ecx, edi, edx) + ROUND(5, 1, ecx, eax, edx, edi) + ROUND(6, 1, eax, ecx, edi, edx) + ROUND(7, 1, ecx, eax, edx, edi) + ROUND(8, 1, eax, ecx, edi, edx) + ROUND(9, 1, ecx, eax, edx, edi) + ROUND(10, 1, eax, ecx, edi, edx) + ROUND(11, 1, ecx, eax, edx, edi) + ROUND(12, 1, eax, ecx, edi, edx) + ROUND(13, 1, ecx, eax, edx, edi) + ROUND(14, 1, eax, ecx, edi, edx) + ROUND(15, 1, ecx, eax, edx, edi) + AS2( cmp WORD_REG(si), K_END) + ASJ( jne, 1, b) + + AS2( mov WORD_REG(dx), DATA_SAVE) + AS2( add WORD_REG(dx), 64) + AS2( mov AS_REG_7, STATE_SAVE) + AS2( mov DATA_SAVE, WORD_REG(dx)) + +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE +#if CRYPTOPP_BOOL_X86 + AS2( test DWORD PTR DATA_END, 1) + ASJ( jnz, 4, f) +#endif + AS2( movdqa xmm1, XMMWORD_PTR [AS_REG_7+1*16]) + AS2( movdqa xmm0, XMMWORD_PTR [AS_REG_7+0*16]) + AS2( paddd xmm1, E(0)) + AS2( paddd xmm0, A(0)) + AS2( movdqa [AS_REG_7+1*16], xmm1) + AS2( movdqa [AS_REG_7+0*16], xmm0) + AS2( cmp WORD_REG(dx), DATA_END) + ASJ( jl, 0, b) +#endif + +#if CRYPTOPP_BOOL_X86 +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + ASJ( jmp, 5, f) + ASL(4) // non-SSE2 +#endif + AS2( add [AS_REG_7+0*4], ecx) // A + AS2( add [AS_REG_7+4*4], edi) // E + AS2( mov eax, B(0)) + AS2( mov ebx, C(0)) + AS2( mov ecx, D(0)) + AS2( add [AS_REG_7+1*4], eax) + AS2( add [AS_REG_7+2*4], ebx) + AS2( add [AS_REG_7+3*4], ecx) + AS2( mov eax, F(0)) + AS2( mov ebx, G(0)) + AS2( mov ecx, H(0)) + AS2( add [AS_REG_7+5*4], eax) + AS2( add [AS_REG_7+6*4], ebx) + AS2( add [AS_REG_7+7*4], ecx) + AS2( mov ecx, AS_REG_7d) + AS2( cmp WORD_REG(dx), DATA_END) + ASJ( jl, 2, b) +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + ASL(5) +#endif +#endif + + AS_POP_IF86(sp) + AS_POP_IF86(bp) + #if !defined(_MSC_VER) || (_MSC_VER < 1400) + AS_POP_IF86(bx) + #endif + +#ifdef CRYPTOPP_GENERATE_X64_MASM + add rsp, LOCALS_SIZE+8 + pop rbp + pop rbx + pop rdi + pop rsi + ret + X86_SHA256_HashBlocks ENDP +#endif + +#ifdef __GNUC__ + ".att_syntax prefix;" + : + : "c" (state), "d" (data), "S" (SHA256_K+48), "D" (len) + #if CRYPTOPP_BOOL_X64 + , "m" (workspace[0]) + #endif + : "memory", "cc", "%eax" + #if CRYPTOPP_BOOL_X64 + , "%rbx", "%r8" + #endif + ); +#endif +} + +#endif // #if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_GENERATE_X64_MASM) + +#ifndef CRYPTOPP_GENERATE_X64_MASM + +#ifdef CRYPTOPP_X64_MASM_AVAILABLE +extern "C" { +void CRYPTOPP_FASTCALL X86_SHA256_HashBlocks(word32 *state, const word32 *data, size_t len); +} +#endif + +#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) + +size_t SHA256::HashMultipleBlocks(const word32 *input, size_t length) +{ + X86_SHA256_HashBlocks(m_state, input, (length&(size_t(0)-BLOCKSIZE)) - !HasSSE2()); + return length % BLOCKSIZE; +} + +size_t SHA224::HashMultipleBlocks(const word32 *input, size_t length) +{ + X86_SHA256_HashBlocks(m_state, input, (length&(size_t(0)-BLOCKSIZE)) - !HasSSE2()); + return length % BLOCKSIZE; +} + +#endif + +#define blk2(i) (W[i&15]+=s1(W[(i-2)&15])+W[(i-7)&15]+s0(W[(i-15)&15])) + +#define Ch(x,y,z) (z^(x&(y^z))) +#define Maj(x,y,z) (y^((x^y)&(y^z))) + +#define a(i) T[(0-i)&7] +#define b(i) T[(1-i)&7] +#define c(i) T[(2-i)&7] +#define d(i) T[(3-i)&7] +#define e(i) T[(4-i)&7] +#define f(i) T[(5-i)&7] +#define g(i) T[(6-i)&7] +#define h(i) T[(7-i)&7] + +#define R(i) h(i)+=S1(e(i))+Ch(e(i),f(i),g(i))+SHA256_K[i+j]+(j?blk2(i):blk0(i));\ + d(i)+=h(i);h(i)+=S0(a(i))+Maj(a(i),b(i),c(i)) + +// for SHA256 +#define S0(x) (rotrFixed(x,2)^rotrFixed(x,13)^rotrFixed(x,22)) +#define S1(x) (rotrFixed(x,6)^rotrFixed(x,11)^rotrFixed(x,25)) +#define s0(x) (rotrFixed(x,7)^rotrFixed(x,18)^(x>>3)) +#define s1(x) (rotrFixed(x,17)^rotrFixed(x,19)^(x>>10)) + +void SHA256::Transform(word32 *state, const word32 *data) +{ + word32 W[16]; +#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) + // this byte reverse is a waste of time, but this function is only called by MDC + ByteReverse(W, data, BLOCKSIZE); + X86_SHA256_HashBlocks(state, W, BLOCKSIZE - !HasSSE2()); +#else + word32 T[8]; + /* Copy context->state[] to working vars */ + memcpy(T, state, sizeof(T)); + /* 64 operations, partially loop unrolled */ + for (unsigned int j=0; j<64; j+=16) + { + R( 0); R( 1); R( 2); R( 3); + R( 4); R( 5); R( 6); R( 7); + R( 8); R( 9); R(10); R(11); + R(12); R(13); R(14); R(15); + } + /* Add the working vars back into context.state[] */ + state[0] += a(0); + state[1] += b(0); + state[2] += c(0); + state[3] += d(0); + state[4] += e(0); + state[5] += f(0); + state[6] += g(0); + state[7] += h(0); +#endif +} + +/* +// smaller but slower +void SHA256::Transform(word32 *state, const word32 *data) +{ + word32 T[20]; + word32 W[32]; + unsigned int i = 0, j = 0; + word32 *t = T+8; + + memcpy(t, state, 8*4); + word32 e = t[4], a = t[0]; + + do + { + word32 w = data[j]; + W[j] = w; + w += SHA256_K[j]; + w += t[7]; + w += S1(e); + w += Ch(e, t[5], t[6]); + e = t[3] + w; + t[3] = t[3+8] = e; + w += S0(t[0]); + a = w + Maj(a, t[1], t[2]); + t[-1] = t[7] = a; + --t; + ++j; + if (j%8 == 0) + t += 8; + } while (j<16); + + do + { + i = j&0xf; + word32 w = s1(W[i+16-2]) + s0(W[i+16-15]) + W[i] + W[i+16-7]; + W[i+16] = W[i] = w; + w += SHA256_K[j]; + w += t[7]; + w += S1(e); + w += Ch(e, t[5], t[6]); + e = t[3] + w; + t[3] = t[3+8] = e; + w += S0(t[0]); + a = w + Maj(a, t[1], t[2]); + t[-1] = t[7] = a; + + w = s1(W[(i+1)+16-2]) + s0(W[(i+1)+16-15]) + W[(i+1)] + W[(i+1)+16-7]; + W[(i+1)+16] = W[(i+1)] = w; + w += SHA256_K[j+1]; + w += (t-1)[7]; + w += S1(e); + w += Ch(e, (t-1)[5], (t-1)[6]); + e = (t-1)[3] + w; + (t-1)[3] = (t-1)[3+8] = e; + w += S0((t-1)[0]); + a = w + Maj(a, (t-1)[1], (t-1)[2]); + (t-1)[-1] = (t-1)[7] = a; + + t-=2; + j+=2; + if (j%8 == 0) + t += 8; + } while (j<64); + + state[0] += a; + state[1] += t[1]; + state[2] += t[2]; + state[3] += t[3]; + state[4] += e; + state[5] += t[5]; + state[6] += t[6]; + state[7] += t[7]; +} +*/ + +#undef S0 +#undef S1 +#undef s0 +#undef s1 +#undef R + +// ************************************************************* + +void SHA384::InitState(HashWordType *state) +{ + static const word64 s[8] = { + W64LIT(0xcbbb9d5dc1059ed8), W64LIT(0x629a292a367cd507), + W64LIT(0x9159015a3070dd17), W64LIT(0x152fecd8f70e5939), + W64LIT(0x67332667ffc00b31), W64LIT(0x8eb44a8768581511), + W64LIT(0xdb0c2e0d64f98fa7), W64LIT(0x47b5481dbefa4fa4)}; + memcpy(state, s, sizeof(s)); +} + +void SHA512::InitState(HashWordType *state) +{ + static const word64 s[8] = { + W64LIT(0x6a09e667f3bcc908), W64LIT(0xbb67ae8584caa73b), + W64LIT(0x3c6ef372fe94f82b), W64LIT(0xa54ff53a5f1d36f1), + W64LIT(0x510e527fade682d1), W64LIT(0x9b05688c2b3e6c1f), + W64LIT(0x1f83d9abfb41bd6b), W64LIT(0x5be0cd19137e2179)}; + memcpy(state, s, sizeof(s)); +} + +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 +CRYPTOPP_ALIGN_DATA(16) static const word64 SHA512_K[80] CRYPTOPP_SECTION_ALIGN16 = { +#else +static const word64 SHA512_K[80] = { +#endif + W64LIT(0x428a2f98d728ae22), W64LIT(0x7137449123ef65cd), + W64LIT(0xb5c0fbcfec4d3b2f), W64LIT(0xe9b5dba58189dbbc), + W64LIT(0x3956c25bf348b538), W64LIT(0x59f111f1b605d019), + W64LIT(0x923f82a4af194f9b), W64LIT(0xab1c5ed5da6d8118), + W64LIT(0xd807aa98a3030242), W64LIT(0x12835b0145706fbe), + W64LIT(0x243185be4ee4b28c), W64LIT(0x550c7dc3d5ffb4e2), + W64LIT(0x72be5d74f27b896f), W64LIT(0x80deb1fe3b1696b1), + W64LIT(0x9bdc06a725c71235), W64LIT(0xc19bf174cf692694), + W64LIT(0xe49b69c19ef14ad2), W64LIT(0xefbe4786384f25e3), + W64LIT(0x0fc19dc68b8cd5b5), W64LIT(0x240ca1cc77ac9c65), + W64LIT(0x2de92c6f592b0275), W64LIT(0x4a7484aa6ea6e483), + W64LIT(0x5cb0a9dcbd41fbd4), W64LIT(0x76f988da831153b5), + W64LIT(0x983e5152ee66dfab), W64LIT(0xa831c66d2db43210), + W64LIT(0xb00327c898fb213f), W64LIT(0xbf597fc7beef0ee4), + W64LIT(0xc6e00bf33da88fc2), W64LIT(0xd5a79147930aa725), + W64LIT(0x06ca6351e003826f), W64LIT(0x142929670a0e6e70), + W64LIT(0x27b70a8546d22ffc), W64LIT(0x2e1b21385c26c926), + W64LIT(0x4d2c6dfc5ac42aed), W64LIT(0x53380d139d95b3df), + W64LIT(0x650a73548baf63de), W64LIT(0x766a0abb3c77b2a8), + W64LIT(0x81c2c92e47edaee6), W64LIT(0x92722c851482353b), + W64LIT(0xa2bfe8a14cf10364), W64LIT(0xa81a664bbc423001), + W64LIT(0xc24b8b70d0f89791), W64LIT(0xc76c51a30654be30), + W64LIT(0xd192e819d6ef5218), W64LIT(0xd69906245565a910), + W64LIT(0xf40e35855771202a), W64LIT(0x106aa07032bbd1b8), + W64LIT(0x19a4c116b8d2d0c8), W64LIT(0x1e376c085141ab53), + W64LIT(0x2748774cdf8eeb99), W64LIT(0x34b0bcb5e19b48a8), + W64LIT(0x391c0cb3c5c95a63), W64LIT(0x4ed8aa4ae3418acb), + W64LIT(0x5b9cca4f7763e373), W64LIT(0x682e6ff3d6b2b8a3), + W64LIT(0x748f82ee5defb2fc), W64LIT(0x78a5636f43172f60), + W64LIT(0x84c87814a1f0ab72), W64LIT(0x8cc702081a6439ec), + W64LIT(0x90befffa23631e28), W64LIT(0xa4506cebde82bde9), + W64LIT(0xbef9a3f7b2c67915), W64LIT(0xc67178f2e372532b), + W64LIT(0xca273eceea26619c), W64LIT(0xd186b8c721c0c207), + W64LIT(0xeada7dd6cde0eb1e), W64LIT(0xf57d4f7fee6ed178), + W64LIT(0x06f067aa72176fba), W64LIT(0x0a637dc5a2c898a6), + W64LIT(0x113f9804bef90dae), W64LIT(0x1b710b35131c471b), + W64LIT(0x28db77f523047d84), W64LIT(0x32caab7b40c72493), + W64LIT(0x3c9ebe0a15c9bebc), W64LIT(0x431d67c49c100d4c), + W64LIT(0x4cc5d4becb3e42b6), W64LIT(0x597f299cfc657e2a), + W64LIT(0x5fcb6fab3ad6faec), W64LIT(0x6c44198c4a475817) +}; + +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 +// put assembly version in separate function, otherwise MSVC 2005 SP1 doesn't generate correct code for the non-assembly version +CRYPTOPP_NAKED static void CRYPTOPP_FASTCALL SHA512_SSE2_Transform(word64 *state, const word64 *data) +{ +#ifdef __GNUC__ + __asm__ __volatile__ + ( + ".intel_syntax noprefix;" + AS1( push ebx) + AS2( mov ebx, eax) +#else + AS1( push ebx) + AS1( push esi) + AS1( push edi) + AS2( lea ebx, SHA512_K) +#endif + + AS2( mov eax, esp) + AS2( and esp, 0xfffffff0) + AS2( sub esp, 27*16) // 17*16 for expanded data, 20*8 for state + AS1( push eax) + AS2( xor eax, eax) + AS2( lea edi, [esp+4+8*8]) // start at middle of state buffer. will decrement pointer each round to avoid copying + AS2( lea esi, [esp+4+20*8+8]) // 16-byte alignment, then add 8 + + AS2( movdqa xmm0, [ecx+0*16]) + AS2( movdq2q mm4, xmm0) + AS2( movdqa [edi+0*16], xmm0) + AS2( movdqa xmm0, [ecx+1*16]) + AS2( movdqa [edi+1*16], xmm0) + AS2( movdqa xmm0, [ecx+2*16]) + AS2( movdq2q mm5, xmm0) + AS2( movdqa [edi+2*16], xmm0) + AS2( movdqa xmm0, [ecx+3*16]) + AS2( movdqa [edi+3*16], xmm0) + ASJ( jmp, 0, f) + +#define SSE2_S0_S1(r, a, b, c) \ + AS2( movq mm6, r)\ + AS2( psrlq r, a)\ + AS2( movq mm7, r)\ + AS2( psllq mm6, 64-c)\ + AS2( pxor mm7, mm6)\ + AS2( psrlq r, b-a)\ + AS2( pxor mm7, r)\ + AS2( psllq mm6, c-b)\ + AS2( pxor mm7, mm6)\ + AS2( psrlq r, c-b)\ + AS2( pxor r, mm7)\ + AS2( psllq mm6, b-a)\ + AS2( pxor r, mm6) + +#define SSE2_s0(r, a, b, c) \ + AS2( movdqa xmm6, r)\ + AS2( psrlq r, a)\ + AS2( movdqa xmm7, r)\ + AS2( psllq xmm6, 64-c)\ + AS2( pxor xmm7, xmm6)\ + AS2( psrlq r, b-a)\ + AS2( pxor xmm7, r)\ + AS2( psrlq r, c-b)\ + AS2( pxor r, xmm7)\ + AS2( psllq xmm6, c-a)\ + AS2( pxor r, xmm6) + +#define SSE2_s1(r, a, b, c) \ + AS2( movdqa xmm6, r)\ + AS2( psrlq r, a)\ + AS2( movdqa xmm7, r)\ + AS2( psllq xmm6, 64-c)\ + AS2( pxor xmm7, xmm6)\ + AS2( psrlq r, b-a)\ + AS2( pxor xmm7, r)\ + AS2( psllq xmm6, c-b)\ + AS2( pxor xmm7, xmm6)\ + AS2( psrlq r, c-b)\ + AS2( pxor r, xmm7) + + ASL(SHA512_Round) + // k + w is in mm0, a is in mm4, e is in mm5 + AS2( paddq mm0, [edi+7*8]) // h + AS2( movq mm2, [edi+5*8]) // f + AS2( movq mm3, [edi+6*8]) // g + AS2( pxor mm2, mm3) + AS2( pand mm2, mm5) + SSE2_S0_S1(mm5,14,18,41) + AS2( pxor mm2, mm3) + AS2( paddq mm0, mm2) // h += Ch(e,f,g) + AS2( paddq mm5, mm0) // h += S1(e) + AS2( movq mm2, [edi+1*8]) // b + AS2( movq mm1, mm2) + AS2( por mm2, mm4) + AS2( pand mm2, [edi+2*8]) // c + AS2( pand mm1, mm4) + AS2( por mm1, mm2) + AS2( paddq mm1, mm5) // temp = h + Maj(a,b,c) + AS2( paddq mm5, [edi+3*8]) // e = d + h + AS2( movq [edi+3*8], mm5) + AS2( movq [edi+11*8], mm5) + SSE2_S0_S1(mm4,28,34,39) // S0(a) + AS2( paddq mm4, mm1) // a = temp + S0(a) + AS2( movq [edi-8], mm4) + AS2( movq [edi+7*8], mm4) + AS1( ret) + + // first 16 rounds + ASL(0) + AS2( movq mm0, [edx+eax*8]) + AS2( movq [esi+eax*8], mm0) + AS2( movq [esi+eax*8+16*8], mm0) + AS2( paddq mm0, [ebx+eax*8]) + ASC( call, SHA512_Round) + AS1( inc eax) + AS2( sub edi, 8) + AS2( test eax, 7) + ASJ( jnz, 0, b) + AS2( add edi, 8*8) + AS2( cmp eax, 16) + ASJ( jne, 0, b) + + // rest of the rounds + AS2( movdqu xmm0, [esi+(16-2)*8]) + ASL(1) + // data expansion, W[i-2] already in xmm0 + AS2( movdqu xmm3, [esi]) + AS2( paddq xmm3, [esi+(16-7)*8]) + AS2( movdqa xmm2, [esi+(16-15)*8]) + SSE2_s1(xmm0, 6, 19, 61) + AS2( paddq xmm0, xmm3) + SSE2_s0(xmm2, 1, 7, 8) + AS2( paddq xmm0, xmm2) + AS2( movdq2q mm0, xmm0) + AS2( movhlps xmm1, xmm0) + AS2( paddq mm0, [ebx+eax*8]) + AS2( movlps [esi], xmm0) + AS2( movlps [esi+8], xmm1) + AS2( movlps [esi+8*16], xmm0) + AS2( movlps [esi+8*17], xmm1) + // 2 rounds + ASC( call, SHA512_Round) + AS2( sub edi, 8) + AS2( movdq2q mm0, xmm1) + AS2( paddq mm0, [ebx+eax*8+8]) + ASC( call, SHA512_Round) + // update indices and loop + AS2( add esi, 16) + AS2( add eax, 2) + AS2( sub edi, 8) + AS2( test eax, 7) + ASJ( jnz, 1, b) + // do housekeeping every 8 rounds + AS2( mov esi, 0xf) + AS2( and esi, eax) + AS2( lea esi, [esp+4+20*8+8+esi*8]) + AS2( add edi, 8*8) + AS2( cmp eax, 80) + ASJ( jne, 1, b) + +#define SSE2_CombineState(i) \ + AS2( movdqa xmm0, [edi+i*16])\ + AS2( paddq xmm0, [ecx+i*16])\ + AS2( movdqa [ecx+i*16], xmm0) + + SSE2_CombineState(0) + SSE2_CombineState(1) + SSE2_CombineState(2) + SSE2_CombineState(3) + + AS1( pop esp) + AS1( emms) + +#if defined(__GNUC__) + AS1( pop ebx) + ".att_syntax prefix;" + : + : "a" (SHA512_K), "c" (state), "d" (data) + : "%esi", "%edi", "memory", "cc" + ); +#else + AS1( pop edi) + AS1( pop esi) + AS1( pop ebx) + AS1( ret) +#endif +} +#endif // #if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + +void SHA512::Transform(word64 *state, const word64 *data) +{ +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 + if (HasSSE2()) + { + SHA512_SSE2_Transform(state, data); + return; + } +#endif + +#define S0(x) (rotrFixed(x,28)^rotrFixed(x,34)^rotrFixed(x,39)) +#define S1(x) (rotrFixed(x,14)^rotrFixed(x,18)^rotrFixed(x,41)) +#define s0(x) (rotrFixed(x,1)^rotrFixed(x,8)^(x>>7)) +#define s1(x) (rotrFixed(x,19)^rotrFixed(x,61)^(x>>6)) + +#define R(i) h(i)+=S1(e(i))+Ch(e(i),f(i),g(i))+SHA512_K[i+j]+(j?blk2(i):blk0(i));\ + d(i)+=h(i);h(i)+=S0(a(i))+Maj(a(i),b(i),c(i)) + + word64 W[16]; + word64 T[8]; + /* Copy context->state[] to working vars */ + memcpy(T, state, sizeof(T)); + /* 80 operations, partially loop unrolled */ + for (unsigned int j=0; j<80; j+=16) + { + R( 0); R( 1); R( 2); R( 3); + R( 4); R( 5); R( 6); R( 7); + R( 8); R( 9); R(10); R(11); + R(12); R(13); R(14); R(15); + } + /* Add the working vars back into context.state[] */ + state[0] += a(0); + state[1] += b(0); + state[2] += c(0); + state[3] += d(0); + state[4] += e(0); + state[5] += f(0); + state[6] += g(0); + state[7] += h(0); +} + +NAMESPACE_END + +#endif // #ifndef CRYPTOPP_GENERATE_X64_MASM +#endif // #ifndef CRYPTOPP_IMPORTS diff --git a/src/cryptopp/sha.h b/src/cryptopp/sha.h new file mode 100644 index 0000000000..8d0dbfcac7 --- /dev/null +++ b/src/cryptopp/sha.h @@ -0,0 +1,63 @@ +#ifndef CRYPTOPP_SHA_H +#define CRYPTOPP_SHA_H + +#include "cryptopp/iterhash.h" + +NAMESPACE_BEGIN(CryptoPP) + +/// SHA-1 +class CRYPTOPP_DLL SHA1 : public IteratedHashWithStaticTransform +{ +public: + static void CRYPTOPP_API InitState(HashWordType *state); + static void CRYPTOPP_API Transform(word32 *digest, const word32 *data); + static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-1";} +}; + +typedef SHA1 SHA; // for backwards compatibility + +//! implements the SHA-256 standard +class CRYPTOPP_DLL SHA256 : public IteratedHashWithStaticTransform +{ +public: +#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) + size_t HashMultipleBlocks(const word32 *input, size_t length); +#endif + static void CRYPTOPP_API InitState(HashWordType *state); + static void CRYPTOPP_API Transform(word32 *digest, const word32 *data); + static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-256";} +}; + +//! implements the SHA-224 standard +class CRYPTOPP_DLL SHA224 : public IteratedHashWithStaticTransform +{ +public: +#if defined(CRYPTOPP_X86_ASM_AVAILABLE) || defined(CRYPTOPP_X64_MASM_AVAILABLE) + size_t HashMultipleBlocks(const word32 *input, size_t length); +#endif + static void CRYPTOPP_API InitState(HashWordType *state); + static void CRYPTOPP_API Transform(word32 *digest, const word32 *data) {SHA256::Transform(digest, data);} + static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-224";} +}; + +//! implements the SHA-512 standard +class CRYPTOPP_DLL SHA512 : public IteratedHashWithStaticTransform +{ +public: + static void CRYPTOPP_API InitState(HashWordType *state); + static void CRYPTOPP_API Transform(word64 *digest, const word64 *data); + static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-512";} +}; + +//! implements the SHA-384 standard +class CRYPTOPP_DLL SHA384 : public IteratedHashWithStaticTransform +{ +public: + static void CRYPTOPP_API InitState(HashWordType *state); + static void CRYPTOPP_API Transform(word64 *digest, const word64 *data) {SHA512::Transform(digest, data);} + static const char * CRYPTOPP_API StaticAlgorithmName() {return "SHA-384";} +}; + +NAMESPACE_END + +#endif diff --git a/src/cryptopp/simple.h b/src/cryptopp/simple.h new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/cryptopp/simple.h @@ -0,0 +1 @@ + diff --git a/src/cryptopp/smartptr.h b/src/cryptopp/smartptr.h new file mode 100644 index 0000000000..fa0daec3df --- /dev/null +++ b/src/cryptopp/smartptr.h @@ -0,0 +1,223 @@ +#ifndef CRYPTOPP_SMARTPTR_H +#define CRYPTOPP_SMARTPTR_H + +#include "cryptopp/config.h" +#include + +NAMESPACE_BEGIN(CryptoPP) + +template class simple_ptr +{ +public: + simple_ptr() : m_p(NULL) {} + ~simple_ptr() {delete m_p;} + T *m_p; +}; + +template class member_ptr +{ +public: + explicit member_ptr(T *p = NULL) : m_p(p) {} + + ~member_ptr(); + + const T& operator*() const { return *m_p; } + T& operator*() { return *m_p; } + + const T* operator->() const { return m_p; } + T* operator->() { return m_p; } + + const T* get() const { return m_p; } + T* get() { return m_p; } + + T* release() + { + T *old_p = m_p; + m_p = 0; + return old_p; + } + + void reset(T *p = 0); + +protected: + member_ptr(const member_ptr& rhs); // copy not allowed + void operator=(const member_ptr& rhs); // assignment not allowed + + T *m_p; +}; + +template member_ptr::~member_ptr() {delete m_p;} +template void member_ptr::reset(T *p) {delete m_p; m_p = p;} + +// ******************************************************** + +template class value_ptr : public member_ptr +{ +public: + value_ptr(const T &obj) : member_ptr(new T(obj)) {} + value_ptr(T *p = NULL) : member_ptr(p) {} + value_ptr(const value_ptr& rhs) + : member_ptr(rhs.m_p ? new T(*rhs.m_p) : NULL) {} + + value_ptr& operator=(const value_ptr& rhs); + bool operator==(const value_ptr& rhs) + { + return (!this->m_p && !rhs.m_p) || (this->m_p && rhs.m_p && *this->m_p == *rhs.m_p); + } +}; + +template value_ptr& value_ptr::operator=(const value_ptr& rhs) +{ + T *old_p = this->m_p; + this->m_p = rhs.m_p ? new T(*rhs.m_p) : NULL; + delete old_p; + return *this; +} + +// ******************************************************** + +template class clonable_ptr : public member_ptr +{ +public: + clonable_ptr(const T &obj) : member_ptr(obj.Clone()) {} + clonable_ptr(T *p = NULL) : member_ptr(p) {} + clonable_ptr(const clonable_ptr& rhs) + : member_ptr(rhs.m_p ? rhs.m_p->Clone() : NULL) {} + + clonable_ptr& operator=(const clonable_ptr& rhs); +}; + +template clonable_ptr& clonable_ptr::operator=(const clonable_ptr& rhs) +{ + T *old_p = this->m_p; + this->m_p = rhs.m_p ? rhs.m_p->Clone() : NULL; + delete old_p; + return *this; +} + +// ******************************************************** + +template class counted_ptr +{ +public: + explicit counted_ptr(T *p = 0); + counted_ptr(const T &r) : m_p(0) {attach(r);} + counted_ptr(const counted_ptr& rhs); + + ~counted_ptr(); + + const T& operator*() const { return *m_p; } + T& operator*() { return *m_p; } + + const T* operator->() const { return m_p; } + T* operator->() { return get(); } + + const T* get() const { return m_p; } + T* get(); + + void attach(const T &p); + + counted_ptr & operator=(const counted_ptr& rhs); + +private: + T *m_p; +}; + +template counted_ptr::counted_ptr(T *p) + : m_p(p) +{ + if (m_p) + m_p->m_referenceCount = 1; +} + +template counted_ptr::counted_ptr(const counted_ptr& rhs) + : m_p(rhs.m_p) +{ + if (m_p) + m_p->m_referenceCount++; +} + +template counted_ptr::~counted_ptr() +{ + if (m_p && --m_p->m_referenceCount == 0) + delete m_p; +} + +template void counted_ptr::attach(const T &r) +{ + if (m_p && --m_p->m_referenceCount == 0) + delete m_p; + if (r.m_referenceCount == 0) + { + m_p = r.clone(); + m_p->m_referenceCount = 1; + } + else + { + m_p = const_cast(&r); + m_p->m_referenceCount++; + } +} + +template T* counted_ptr::get() +{ + if (m_p && m_p->m_referenceCount > 1) + { + T *temp = m_p->clone(); + m_p->m_referenceCount--; + m_p = temp; + m_p->m_referenceCount = 1; + } + return m_p; +} + +template counted_ptr & counted_ptr::operator=(const counted_ptr& rhs) +{ + if (m_p != rhs.m_p) + { + if (m_p && --m_p->m_referenceCount == 0) + delete m_p; + m_p = rhs.m_p; + if (m_p) + m_p->m_referenceCount++; + } + return *this; +} + +// ******************************************************** + +template class vector_member_ptrs +{ +public: + vector_member_ptrs(size_t size=0) + : m_size(size), m_ptr(new member_ptr[size]) {} + ~vector_member_ptrs() + {delete [] this->m_ptr;} + + member_ptr& operator[](size_t index) + {assert(indexm_size); return this->m_ptr[index];} + const member_ptr& operator[](size_t index) const + {assert(indexm_size); return this->m_ptr[index];} + + size_t size() const {return this->m_size;} + void resize(size_t newSize) + { + member_ptr *newPtr = new member_ptr[newSize]; + for (size_t i=0; im_size && im_ptr[i].release()); + delete [] this->m_ptr; + this->m_size = newSize; + this->m_ptr = newPtr; + } + +private: + vector_member_ptrs(const vector_member_ptrs &c); // copy not allowed + void operator=(const vector_member_ptrs &x); // assignment not allowed + + size_t m_size; + member_ptr *m_ptr; +}; + +NAMESPACE_END + +#endif diff --git a/src/cryptopp/stdcpp.h b/src/cryptopp/stdcpp.h new file mode 100644 index 0000000000..9a468ab61e --- /dev/null +++ b/src/cryptopp/stdcpp.h @@ -0,0 +1,27 @@ +#ifndef CRYPTOPP_STDCPP_H +#define CRYPTOPP_STDCPP_H + +#include +#include +#include +#include +#include +#include +#include + + +#ifdef _MSC_VER +#include // CodeWarrior doesn't have memory.h +#include +#include +#include + +// re-disable this +#pragma warning(disable: 4231) +#endif + +#if defined(_MSC_VER) && defined(_CRTAPI1) +#define CRYPTOPP_MSVCRT6 +#endif + +#endif diff --git a/src/db.cpp b/src/db.cpp new file mode 100644 index 0000000000..52c0f5b4c3 --- /dev/null +++ b/src/db.cpp @@ -0,0 +1,1026 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" + +using namespace std; +using namespace boost; + +void ThreadFlushWalletDB(void* parg); + + +unsigned int nWalletDBUpdated; +uint64 nAccountingEntryNumber = 0; + + + +// +// CDB +// + +static CCriticalSection cs_db; +static bool fDbEnvInit = false; +DbEnv dbenv(0); +static map mapFileUseCount; +static map mapDb; + +class CDBInit +{ +public: + CDBInit() + { + } + ~CDBInit() + { + if (fDbEnvInit) + { + dbenv.close(0); + fDbEnvInit = false; + } + } +} +instance_of_cdbinit; + + +CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) +{ + int ret; + if (pszFile == NULL) + return; + + fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); + bool fCreate = strchr(pszMode, 'c'); + unsigned int nFlags = DB_THREAD; + if (fCreate) + nFlags |= DB_CREATE; + + CRITICAL_BLOCK(cs_db) + { + if (!fDbEnvInit) + { + if (fShutdown) + return; + string strDataDir = GetDataDir(); + string strLogDir = strDataDir + "/database"; + filesystem::create_directory(strLogDir.c_str()); + string strErrorFile = strDataDir + "/db.log"; + printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str()); + + dbenv.set_lg_dir(strLogDir.c_str()); + dbenv.set_lg_max(10000000); + dbenv.set_lk_max_locks(10000); + dbenv.set_lk_max_objects(10000); + dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug + dbenv.set_flags(DB_AUTO_COMMIT, 1); + ret = dbenv.open(strDataDir.c_str(), + DB_CREATE | + DB_INIT_LOCK | + DB_INIT_LOG | + DB_INIT_MPOOL | + DB_INIT_TXN | + DB_THREAD | + DB_RECOVER, + S_IRUSR | S_IWUSR); + if (ret > 0) + throw runtime_error(strprintf("CDB() : error %d opening database environment", ret)); + fDbEnvInit = true; + } + + strFile = pszFile; + ++mapFileUseCount[strFile]; + pdb = mapDb[strFile]; + if (pdb == NULL) + { + pdb = new Db(&dbenv, 0); + + ret = pdb->open(NULL, // Txn pointer + pszFile, // Filename + "main", // Logical db name + DB_BTREE, // Database type + nFlags, // Flags + 0); + + if (ret > 0) + { + delete pdb; + pdb = NULL; + CRITICAL_BLOCK(cs_db) + --mapFileUseCount[strFile]; + strFile = ""; + throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret)); + } + + if (fCreate && !Exists(string("version"))) + { + bool fTmp = fReadOnly; + fReadOnly = false; + WriteVersion(VERSION); + fReadOnly = fTmp; + } + + mapDb[strFile] = pdb; + } + } +} + +void CDB::Close() +{ + if (!pdb) + return; + if (!vTxn.empty()) + vTxn.front()->abort(); + vTxn.clear(); + pdb = NULL; + + // Flush database activity from memory pool to disk log + unsigned int nMinutes = 0; + if (fReadOnly) + nMinutes = 1; + if (strFile == "addr.dat") + nMinutes = 2; + if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 500 != 0) + nMinutes = 1; + dbenv.txn_checkpoint(0, nMinutes, 0); + + CRITICAL_BLOCK(cs_db) + --mapFileUseCount[strFile]; +} + +void CloseDb(const string& strFile) +{ + CRITICAL_BLOCK(cs_db) + { + if (mapDb[strFile] != NULL) + { + // Close the database handle + Db* pdb = mapDb[strFile]; + pdb->close(0); + delete pdb; + mapDb[strFile] = NULL; + } + } +} + +void DBFlush(bool fShutdown) +{ + // Flush log data to the actual data file + // on all files that are not in use + printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started"); + if (!fDbEnvInit) + return; + CRITICAL_BLOCK(cs_db) + { + map::iterator mi = mapFileUseCount.begin(); + while (mi != mapFileUseCount.end()) + { + string strFile = (*mi).first; + int nRefCount = (*mi).second; + printf("%s refcount=%d\n", strFile.c_str(), nRefCount); + if (nRefCount == 0) + { + // Move log data to the dat file + CloseDb(strFile); + dbenv.txn_checkpoint(0, 0, 0); + printf("%s flush\n", strFile.c_str()); + dbenv.lsn_reset(strFile.c_str(), 0); + mapFileUseCount.erase(mi++); + } + else + mi++; + } + if (fShutdown) + { + char** listp; + if (mapFileUseCount.empty()) + dbenv.log_archive(&listp, DB_ARCH_REMOVE); + dbenv.close(0); + fDbEnvInit = false; + } + } +} + + + + + + +// +// CTxDB +// + +bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex) +{ + assert(!fClient); + txindex.SetNull(); + return Read(make_pair(string("tx"), hash), txindex); +} + +bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex) +{ + assert(!fClient); + return Write(make_pair(string("tx"), hash), txindex); +} + +bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight) +{ + assert(!fClient); + + // Add to tx index + uint256 hash = tx.GetHash(); + CTxIndex txindex(pos, tx.vout.size()); + return Write(make_pair(string("tx"), hash), txindex); +} + +bool CTxDB::EraseTxIndex(const CTransaction& tx) +{ + assert(!fClient); + uint256 hash = tx.GetHash(); + + return Erase(make_pair(string("tx"), hash)); +} + +bool CTxDB::ContainsTx(uint256 hash) +{ + assert(!fClient); + return Exists(make_pair(string("tx"), hash)); +} + +bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector& vtx) +{ + assert(!fClient); + vtx.clear(); + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + unsigned int fFlags = DB_SET_RANGE; + loop + { + // Read next record + CDataStream ssKey; + if (fFlags == DB_SET_RANGE) + ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0); + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); + fFlags = DB_NEXT; + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + { + pcursor->close(); + return false; + } + + // Unserialize + string strType; + uint160 hashItem; + CDiskTxPos pos; + ssKey >> strType >> hashItem >> pos; + int nItemHeight; + ssValue >> nItemHeight; + + // Read transaction + if (strType != "owner" || hashItem != hash160) + break; + if (nItemHeight >= nMinHeight) + { + vtx.resize(vtx.size()+1); + if (!vtx.back().ReadFromDisk(pos)) + { + pcursor->close(); + return false; + } + } + } + + pcursor->close(); + return true; +} + +bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex) +{ + assert(!fClient); + tx.SetNull(); + if (!ReadTxIndex(hash, txindex)) + return false; + return (tx.ReadFromDisk(txindex.pos)); +} + +bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx) +{ + CTxIndex txindex; + return ReadDiskTx(hash, tx, txindex); +} + +bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex) +{ + return ReadDiskTx(outpoint.hash, tx, txindex); +} + +bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx) +{ + CTxIndex txindex; + return ReadDiskTx(outpoint.hash, tx, txindex); +} + +bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) +{ + return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex); +} + +bool CTxDB::EraseBlockIndex(uint256 hash) +{ + return Erase(make_pair(string("blockindex"), hash)); +} + +bool CTxDB::ReadHashBestChain(uint256& hashBestChain) +{ + return Read(string("hashBestChain"), hashBestChain); +} + +bool CTxDB::WriteHashBestChain(uint256 hashBestChain) +{ + return Write(string("hashBestChain"), hashBestChain); +} + +bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork) +{ + return Read(string("bnBestInvalidWork"), bnBestInvalidWork); +} + +bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork) +{ + return Write(string("bnBestInvalidWork"), bnBestInvalidWork); +} + +CBlockIndex* InsertBlockIndex(uint256 hash) +{ + if (hash == 0) + return NULL; + + // Return existing + map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + return (*mi).second; + + // Create new + CBlockIndex* pindexNew = new CBlockIndex(); + if (!pindexNew) + throw runtime_error("LoadBlockIndex() : new CBlockIndex failed"); + mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + pindexNew->phashBlock = &((*mi).first); + + return pindexNew; +} + +bool CTxDB::LoadBlockIndex() +{ + // Get database cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + // Load mapBlockIndex + unsigned int fFlags = DB_SET_RANGE; + loop + { + // Read next record + CDataStream ssKey; + if (fFlags == DB_SET_RANGE) + ssKey << make_pair(string("blockindex"), uint256(0)); + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); + fFlags = DB_NEXT; + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + return false; + + // Unserialize + string strType; + ssKey >> strType; + if (strType == "blockindex") + { + CDiskBlockIndex diskindex; + ssValue >> diskindex; + + // Construct block index object + CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); + pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); + pindexNew->pnext = InsertBlockIndex(diskindex.hashNext); + pindexNew->nFile = diskindex.nFile; + pindexNew->nBlockPos = diskindex.nBlockPos; + pindexNew->nHeight = diskindex.nHeight; + pindexNew->nVersion = diskindex.nVersion; + pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; + pindexNew->nTime = diskindex.nTime; + pindexNew->nBits = diskindex.nBits; + pindexNew->nNonce = diskindex.nNonce; + + // Watch for genesis block + if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock) + pindexGenesisBlock = pindexNew; + + if (!pindexNew->CheckIndex()) + return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight); + } + else + { + break; + } + } + pcursor->close(); + + // Calculate bnChainWork + vector > vSortedByHeight; + vSortedByHeight.reserve(mapBlockIndex.size()); + BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) + { + CBlockIndex* pindex = item.second; + vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); + } + sort(vSortedByHeight.begin(), vSortedByHeight.end()); + BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) + { + CBlockIndex* pindex = item.second; + pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork(); + } + + // Load hashBestChain pointer to end of best chain + if (!ReadHashBestChain(hashBestChain)) + { + if (pindexGenesisBlock == NULL) + return true; + return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded"); + } + if (!mapBlockIndex.count(hashBestChain)) + return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index"); + pindexBest = mapBlockIndex[hashBestChain]; + nBestHeight = pindexBest->nHeight; + bnBestChainWork = pindexBest->bnChainWork; + printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight); + + // Load bnBestInvalidWork, OK if it doesn't exist + ReadBestInvalidWork(bnBestInvalidWork); + + // Verify blocks in the best chain + CBlockIndex* pindexFork = NULL; + for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) + { + if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks")) + break; + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("LoadBlockIndex() : block.ReadFromDisk failed"); + if (!block.CheckBlock()) + { + printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + pindexFork = pindex->pprev; + } + } + if (pindexFork) + { + // Reorg back to the fork + printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight); + CBlock block; + if (!block.ReadFromDisk(pindexFork)) + return error("LoadBlockIndex() : block.ReadFromDisk failed"); + CTxDB txdb; + block.SetBestChain(txdb, pindexFork); + } + + return true; +} + + + + + +// +// CAddrDB +// + +bool CAddrDB::WriteAddress(const CAddress& addr) +{ + return Write(make_pair(string("addr"), addr.GetKey()), addr); +} + +bool CAddrDB::EraseAddress(const CAddress& addr) +{ + return Erase(make_pair(string("addr"), addr.GetKey())); +} + +bool CAddrDB::LoadAddresses() +{ + CRITICAL_BLOCK(cs_mapAddresses) + { + // Load user provided addresses + CAutoFile filein = fopen((GetDataDir() + "/addr.txt").c_str(), "rt"); + if (filein) + { + try + { + char psz[1000]; + while (fgets(psz, sizeof(psz), filein)) + { + CAddress addr(psz, NODE_NETWORK); + addr.nTime = 0; // so it won't relay unless successfully connected + if (addr.IsValid()) + AddAddress(addr); + } + } + catch (...) { } + } + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + loop + { + // Read next record + CDataStream ssKey; + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + return false; + + // Unserialize + string strType; + ssKey >> strType; + if (strType == "addr") + { + CAddress addr; + ssValue >> addr; + mapAddresses.insert(make_pair(addr.GetKey(), addr)); + } + } + pcursor->close(); + + printf("Loaded %d addresses\n", mapAddresses.size()); + } + + return true; +} + +bool LoadAddresses() +{ + return CAddrDB("cr+").LoadAddresses(); +} + + + + +// +// CWalletDB +// + +static set setKeyPool; +static CCriticalSection cs_setKeyPool; + +bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) +{ + account.SetNull(); + return Read(make_pair(string("acc"), strAccount), account); +} + +bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) +{ + return Write(make_pair(string("acc"), strAccount), account); +} + +bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) +{ + return Write(make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry); +} + +int64 CWalletDB::GetAccountCreditDebit(const string& strAccount) +{ + list entries; + ListAccountCreditDebit(strAccount, entries); + + int64 nCreditDebit = 0; + BOOST_FOREACH (const CAccountingEntry& entry, entries) + nCreditDebit += entry.nCreditDebit; + + return nCreditDebit; +} + +void CWalletDB::ListAccountCreditDebit(const string& strAccount, list& entries) +{ + int64 nCreditDebit = 0; + + bool fAllAccounts = (strAccount == "*"); + + Dbc* pcursor = GetCursor(); + if (!pcursor) + throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor"); + unsigned int fFlags = DB_SET_RANGE; + loop + { + // Read next record + CDataStream ssKey; + if (fFlags == DB_SET_RANGE) + ssKey << make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0)); + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); + fFlags = DB_NEXT; + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + { + pcursor->close(); + throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB"); + } + + // Unserialize + string strType; + ssKey >> strType; + if (strType != "acentry") + break; + CAccountingEntry acentry; + ssKey >> acentry.strAccount; + if (!fAllAccounts && acentry.strAccount != strAccount) + break; + + ssValue >> acentry; + entries.push_back(acentry); + } + + pcursor->close(); +} + + +bool CWalletDB::LoadWallet() +{ + vchDefaultKey.clear(); + int nFileVersion = 0; + vector vWalletUpgrade; + + // Modify defaults +#ifndef __WXMSW__ + // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program + fMinimizeToTray = false; + fMinimizeOnClose = false; +#endif + + //// todo: shouldn't we catch exceptions and try to recover and continue? + CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_mapKeys) + { + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + loop + { + // Read next record + CDataStream ssKey; + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + return false; + + // Unserialize + // Taking advantage of the fact that pair serialization + // is just the two items serialized one after the other + string strType; + ssKey >> strType; + if (strType == "name") + { + string strAddress; + ssKey >> strAddress; + ssValue >> mapAddressBook[strAddress]; + } + else if (strType == "tx") + { + uint256 hash; + ssKey >> hash; + CWalletTx& wtx = mapWallet[hash]; + ssValue >> wtx; + + if (wtx.GetHash() != hash) + printf("Error in wallet.dat, hash mismatch\n"); + + // Undo serialize changes in 31600 + if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) + { + if (!ssValue.empty()) + { + char fTmp; + char fUnused; + ssValue >> fTmp >> fUnused >> wtx.strFromAccount; + printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str()); + wtx.fTimeReceivedIsTxTime = fTmp; + } + else + { + printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str()); + wtx.fTimeReceivedIsTxTime = 0; + } + vWalletUpgrade.push_back(hash); + } + + //// debug print + //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); + //printf(" %12I64d %s %s %s\n", + // wtx.vout[0].nValue, + // DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(), + // wtx.hashBlock.ToString().substr(0,20).c_str(), + // wtx.mapValue["message"].c_str()); + } + else if (strType == "acentry") + { + string strAccount; + ssKey >> strAccount; + uint64 nNumber; + ssKey >> nNumber; + if (nNumber > nAccountingEntryNumber) + nAccountingEntryNumber = nNumber; + } + else if (strType == "key" || strType == "wkey") + { + vector vchPubKey; + ssKey >> vchPubKey; + CWalletKey wkey; + if (strType == "key") + ssValue >> wkey.vchPrivKey; + else + ssValue >> wkey; + + mapKeys[vchPubKey] = wkey.vchPrivKey; + mapPubKeys[Hash160(vchPubKey)] = vchPubKey; + } + else if (strType == "defaultkey") + { + ssValue >> vchDefaultKey; + } + else if (strType == "pool") + { + int64 nIndex; + ssKey >> nIndex; + setKeyPool.insert(nIndex); + } + else if (strType == "version") + { + ssValue >> nFileVersion; + if (nFileVersion == 10300) + nFileVersion = 300; + } + else if (strType == "setting") + { + string strKey; + ssKey >> strKey; + + // Options +#ifndef GUI + if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins; +#endif + if (strKey == "nTransactionFee") ssValue >> nTransactionFee; + if (strKey == "addrIncoming") ssValue >> addrIncoming; + if (strKey == "fLimitProcessors") ssValue >> fLimitProcessors; + if (strKey == "nLimitProcessors") ssValue >> nLimitProcessors; + if (strKey == "fMinimizeToTray") ssValue >> fMinimizeToTray; + if (strKey == "fMinimizeOnClose") ssValue >> fMinimizeOnClose; + if (strKey == "fUseProxy") ssValue >> fUseProxy; + if (strKey == "addrProxy") ssValue >> addrProxy; + if (fHaveUPnP && strKey == "fUseUPnP") ssValue >> fUseUPnP; + } + } + pcursor->close(); + } + + BOOST_FOREACH(uint256 hash, vWalletUpgrade) + WriteTx(hash, mapWallet[hash]); + + printf("nFileVersion = %d\n", nFileVersion); + printf("fGenerateBitcoins = %d\n", fGenerateBitcoins); + printf("nTransactionFee = %"PRI64d"\n", nTransactionFee); + printf("addrIncoming = %s\n", addrIncoming.ToString().c_str()); + printf("fMinimizeToTray = %d\n", fMinimizeToTray); + printf("fMinimizeOnClose = %d\n", fMinimizeOnClose); + printf("fUseProxy = %d\n", fUseProxy); + printf("addrProxy = %s\n", addrProxy.ToString().c_str()); + if (fHaveUPnP) + printf("fUseUPnP = %d\n", fUseUPnP); + + + // Upgrade + if (nFileVersion < VERSION) + { + // Get rid of old debug.log file in current directory + if (nFileVersion <= 105 && !pszSetDataDir[0]) + unlink("debug.log"); + + WriteVersion(VERSION); + } + + + return true; +} + +bool LoadWallet(bool& fFirstRunRet) +{ + fFirstRunRet = false; + if (!CWalletDB("cr+").LoadWallet()) + return false; + fFirstRunRet = vchDefaultKey.empty(); + + if (mapKeys.count(vchDefaultKey)) + { + // Set keyUser + keyUser.SetPubKey(vchDefaultKey); + keyUser.SetPrivKey(mapKeys[vchDefaultKey]); + } + else + { + // Create new keyUser and set as default key + RandAddSeedPerfmon(); + keyUser.MakeNewKey(); + if (!AddKey(keyUser)) + return false; + if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "")) + return false; + CWalletDB().WriteDefaultKey(keyUser.GetPubKey()); + } + + CreateThread(ThreadFlushWalletDB, NULL); + return true; +} + +void ThreadFlushWalletDB(void* parg) +{ + static bool fOneThread; + if (fOneThread) + return; + fOneThread = true; + if (mapArgs.count("-noflushwallet")) + return; + + unsigned int nLastSeen = nWalletDBUpdated; + unsigned int nLastFlushed = nWalletDBUpdated; + int64 nLastWalletUpdate = GetTime(); + while (!fShutdown) + { + Sleep(500); + + if (nLastSeen != nWalletDBUpdated) + { + nLastSeen = nWalletDBUpdated; + nLastWalletUpdate = GetTime(); + } + + if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) + { + TRY_CRITICAL_BLOCK(cs_db) + { + // Don't do this if any databases are in use + int nRefCount = 0; + map::iterator mi = mapFileUseCount.begin(); + while (mi != mapFileUseCount.end()) + { + nRefCount += (*mi).second; + mi++; + } + + if (nRefCount == 0 && !fShutdown) + { + string strFile = "wallet.dat"; + map::iterator mi = mapFileUseCount.find(strFile); + if (mi != mapFileUseCount.end()) + { + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); + printf("Flushing wallet.dat\n"); + nLastFlushed = nWalletDBUpdated; + int64 nStart = GetTimeMillis(); + + // Flush wallet.dat so it's self contained + CloseDb(strFile); + dbenv.txn_checkpoint(0, 0, 0); + dbenv.lsn_reset(strFile.c_str(), 0); + + mapFileUseCount.erase(mi++); + printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart); + } + } + } + } + } +} + +void BackupWallet(const string& strDest) +{ + while (!fShutdown) + { + CRITICAL_BLOCK(cs_db) + { + const string strFile = "wallet.dat"; + if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0) + { + // Flush log data to the dat file + CloseDb(strFile); + dbenv.txn_checkpoint(0, 0, 0); + dbenv.lsn_reset(strFile.c_str(), 0); + mapFileUseCount.erase(strFile); + + // Copy wallet.dat + filesystem::path pathSrc(GetDataDir() + "/" + strFile); + filesystem::path pathDest(strDest); + if (filesystem::is_directory(pathDest)) + pathDest = pathDest / strFile; +#if BOOST_VERSION >= 104000 + filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists); +#else + filesystem::copy_file(pathSrc, pathDest); +#endif + printf("copied wallet.dat to %s\n", pathDest.string().c_str()); + + return; + } + } + Sleep(100); + } +} + + +void CWalletDB::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) +{ + nIndex = -1; + keypool.vchPubKey.clear(); + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_setKeyPool) + { + // Top up key pool + int64 nTargetSize = max(GetArg("-keypool", 100), (int64)0); + while (setKeyPool.size() < nTargetSize+1) + { + int64 nEnd = 1; + if (!setKeyPool.empty()) + nEnd = *(--setKeyPool.end()) + 1; + if (!Write(make_pair(string("pool"), nEnd), CKeyPool(GenerateNewKey()))) + throw runtime_error("ReserveKeyFromKeyPool() : writing generated key failed"); + setKeyPool.insert(nEnd); + printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size()); + } + + // Get the oldest key + assert(!setKeyPool.empty()); + nIndex = *(setKeyPool.begin()); + setKeyPool.erase(setKeyPool.begin()); + if (!Read(make_pair(string("pool"), nIndex), keypool)) + throw runtime_error("ReserveKeyFromKeyPool() : read failed"); + if (!mapKeys.count(keypool.vchPubKey)) + throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); + assert(!keypool.vchPubKey.empty()); + printf("keypool reserve %"PRI64d"\n", nIndex); + } +} + +void CWalletDB::KeepKey(int64 nIndex) +{ + // Remove from key pool + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + { + Erase(make_pair(string("pool"), nIndex)); + } + printf("keypool keep %"PRI64d"\n", nIndex); +} + +void CWalletDB::ReturnKey(int64 nIndex) +{ + // Return to key pool + CRITICAL_BLOCK(cs_setKeyPool) + setKeyPool.insert(nIndex); + printf("keypool return %"PRI64d"\n", nIndex); +} + +vector GetKeyFromKeyPool() +{ + CWalletDB walletdb; + int64 nIndex = 0; + CKeyPool keypool; + walletdb.ReserveKeyFromKeyPool(nIndex, keypool); + walletdb.KeepKey(nIndex); + return keypool.vchPubKey; +} + +int64 GetOldestKeyPoolTime() +{ + CWalletDB walletdb; + int64 nIndex = 0; + CKeyPool keypool; + walletdb.ReserveKeyFromKeyPool(nIndex, keypool); + walletdb.ReturnKey(nIndex); + return keypool.nTime; +} diff --git a/src/db.h b/src/db.h new file mode 100644 index 0000000000..9826194ed0 --- /dev/null +++ b/src/db.h @@ -0,0 +1,526 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_DB_H +#define BITCOIN_DB_H + +#include "key.h" + +#include +#include +#include + +#include + +class CTransaction; +class CTxIndex; +class CDiskBlockIndex; +class CDiskTxPos; +class COutPoint; +class CUser; +class CReview; +class CAddress; +class CWalletTx; +class CAccount; +class CAccountingEntry; +class CBlockLocator; + +extern std::map mapAddressBook; +extern CCriticalSection cs_mapAddressBook; +extern std::vector vchDefaultKey; +extern bool fClient; +extern int nBestHeight; + + +extern unsigned int nWalletDBUpdated; +extern DbEnv dbenv; + + +extern void DBFlush(bool fShutdown); +extern std::vector GetKeyFromKeyPool(); +extern int64 GetOldestKeyPoolTime(); + + + + +class CDB +{ +protected: + Db* pdb; + std::string strFile; + std::vector vTxn; + bool fReadOnly; + + explicit CDB(const char* pszFile, const char* pszMode="r+"); + ~CDB() { Close(); } +public: + void Close(); +private: + CDB(const CDB&); + void operator=(const CDB&); + +protected: + template + bool Read(const K& key, T& value) + { + if (!pdb) + return false; + + // Key + CDataStream ssKey(SER_DISK); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Read + Dbt datValue; + datValue.set_flags(DB_DBT_MALLOC); + int ret = pdb->get(GetTxn(), &datKey, &datValue, 0); + memset(datKey.get_data(), 0, datKey.get_size()); + if (datValue.get_data() == NULL) + return false; + + // Unserialize value + CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK); + ssValue >> value; + + // Clear and free memory + memset(datValue.get_data(), 0, datValue.get_size()); + free(datValue.get_data()); + return (ret == 0); + } + + template + bool Write(const K& key, const T& value, bool fOverwrite=true) + { + if (!pdb) + return false; + if (fReadOnly) + assert(("Write called on database in read-only mode", false)); + + // Key + CDataStream ssKey(SER_DISK); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Value + CDataStream ssValue(SER_DISK); + ssValue.reserve(10000); + ssValue << value; + Dbt datValue(&ssValue[0], ssValue.size()); + + // Write + int ret = pdb->put(GetTxn(), &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); + + // Clear memory in case it was a private key + memset(datKey.get_data(), 0, datKey.get_size()); + memset(datValue.get_data(), 0, datValue.get_size()); + return (ret == 0); + } + + template + bool Erase(const K& key) + { + if (!pdb) + return false; + if (fReadOnly) + assert(("Erase called on database in read-only mode", false)); + + // Key + CDataStream ssKey(SER_DISK); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Erase + int ret = pdb->del(GetTxn(), &datKey, 0); + + // Clear memory + memset(datKey.get_data(), 0, datKey.get_size()); + return (ret == 0 || ret == DB_NOTFOUND); + } + + template + bool Exists(const K& key) + { + if (!pdb) + return false; + + // Key + CDataStream ssKey(SER_DISK); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Exists + int ret = pdb->exists(GetTxn(), &datKey, 0); + + // Clear memory + memset(datKey.get_data(), 0, datKey.get_size()); + return (ret == 0); + } + + Dbc* GetCursor() + { + if (!pdb) + return NULL; + Dbc* pcursor = NULL; + int ret = pdb->cursor(NULL, &pcursor, 0); + if (ret != 0) + return NULL; + return pcursor; + } + + int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags=DB_NEXT) + { + // Read at cursor + Dbt datKey; + if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) + { + datKey.set_data(&ssKey[0]); + datKey.set_size(ssKey.size()); + } + Dbt datValue; + if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) + { + datValue.set_data(&ssValue[0]); + datValue.set_size(ssValue.size()); + } + datKey.set_flags(DB_DBT_MALLOC); + datValue.set_flags(DB_DBT_MALLOC); + int ret = pcursor->get(&datKey, &datValue, fFlags); + if (ret != 0) + return ret; + else if (datKey.get_data() == NULL || datValue.get_data() == NULL) + return 99999; + + // Convert to streams + ssKey.SetType(SER_DISK); + ssKey.clear(); + ssKey.write((char*)datKey.get_data(), datKey.get_size()); + ssValue.SetType(SER_DISK); + ssValue.clear(); + ssValue.write((char*)datValue.get_data(), datValue.get_size()); + + // Clear and free memory + memset(datKey.get_data(), 0, datKey.get_size()); + memset(datValue.get_data(), 0, datValue.get_size()); + free(datKey.get_data()); + free(datValue.get_data()); + return 0; + } + + DbTxn* GetTxn() + { + if (!vTxn.empty()) + return vTxn.back(); + else + return NULL; + } + +public: + bool TxnBegin() + { + if (!pdb) + return false; + DbTxn* ptxn = NULL; + int ret = dbenv.txn_begin(GetTxn(), &ptxn, DB_TXN_NOSYNC); + if (!ptxn || ret != 0) + return false; + vTxn.push_back(ptxn); + return true; + } + + bool TxnCommit() + { + if (!pdb) + return false; + if (vTxn.empty()) + return false; + int ret = vTxn.back()->commit(0); + vTxn.pop_back(); + return (ret == 0); + } + + bool TxnAbort() + { + if (!pdb) + return false; + if (vTxn.empty()) + return false; + int ret = vTxn.back()->abort(); + vTxn.pop_back(); + return (ret == 0); + } + + bool ReadVersion(int& nVersion) + { + nVersion = 0; + return Read(std::string("version"), nVersion); + } + + bool WriteVersion(int nVersion) + { + return Write(std::string("version"), nVersion); + } +}; + + + + + + + + +class CTxDB : public CDB +{ +public: + CTxDB(const char* pszMode="r+") : CDB("blkindex.dat", pszMode) { } +private: + CTxDB(const CTxDB&); + void operator=(const CTxDB&); +public: + bool ReadTxIndex(uint256 hash, CTxIndex& txindex); + bool UpdateTxIndex(uint256 hash, const CTxIndex& txindex); + bool AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight); + bool EraseTxIndex(const CTransaction& tx); + bool ContainsTx(uint256 hash); + bool ReadOwnerTxes(uint160 hash160, int nHeight, std::vector& vtx); + bool ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex); + bool ReadDiskTx(uint256 hash, CTransaction& tx); + bool ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex); + bool ReadDiskTx(COutPoint outpoint, CTransaction& tx); + bool WriteBlockIndex(const CDiskBlockIndex& blockindex); + bool EraseBlockIndex(uint256 hash); + bool ReadHashBestChain(uint256& hashBestChain); + bool WriteHashBestChain(uint256 hashBestChain); + bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork); + bool WriteBestInvalidWork(CBigNum bnBestInvalidWork); + bool LoadBlockIndex(); +}; + + + + + +class CAddrDB : public CDB +{ +public: + CAddrDB(const char* pszMode="r+") : CDB("addr.dat", pszMode) { } +private: + CAddrDB(const CAddrDB&); + void operator=(const CAddrDB&); +public: + bool WriteAddress(const CAddress& addr); + bool EraseAddress(const CAddress& addr); + bool LoadAddresses(); +}; + +bool LoadAddresses(); + + + + + + +class CKeyPool +{ +public: + int64 nTime; + std::vector vchPubKey; + + CKeyPool() + { + nTime = GetTime(); + } + + CKeyPool(const std::vector& vchPubKeyIn) + { + nTime = GetTime(); + vchPubKey = vchPubKeyIn; + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(nTime); + READWRITE(vchPubKey); + ) +}; + + + + +class CWalletDB : public CDB +{ +public: + CWalletDB(const char* pszMode="r+") : CDB("wallet.dat", pszMode) + { + } +private: + CWalletDB(const CWalletDB&); + void operator=(const CWalletDB&); +public: + bool ReadName(const std::string& strAddress, std::string& strName) + { + strName = ""; + return Read(std::make_pair(std::string("name"), strAddress), strName); + } + + bool WriteName(const std::string& strAddress, const std::string& strName) + { + CRITICAL_BLOCK(cs_mapAddressBook) + mapAddressBook[strAddress] = strName; + nWalletDBUpdated++; + return Write(std::make_pair(std::string("name"), strAddress), strName); + } + + bool EraseName(const std::string& strAddress) + { + // This should only be used for sending addresses, never for receiving addresses, + // receiving addresses must always have an address book entry if they're not change return. + CRITICAL_BLOCK(cs_mapAddressBook) + mapAddressBook.erase(strAddress); + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("name"), strAddress)); + } + + bool ReadTx(uint256 hash, CWalletTx& wtx) + { + return Read(std::make_pair(std::string("tx"), hash), wtx); + } + + bool WriteTx(uint256 hash, const CWalletTx& wtx) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("tx"), hash), wtx); + } + + bool EraseTx(uint256 hash) + { + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("tx"), hash)); + } + + bool ReadKey(const std::vector& vchPubKey, CPrivKey& vchPrivKey) + { + vchPrivKey.clear(); + return Read(std::make_pair(std::string("key"), vchPubKey), vchPrivKey); + } + + bool WriteKey(const std::vector& vchPubKey, const CPrivKey& vchPrivKey) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false); + } + + bool WriteBestBlock(const CBlockLocator& locator) + { + nWalletDBUpdated++; + return Write(std::string("bestblock"), locator); + } + + bool ReadBestBlock(CBlockLocator& locator) + { + return Read(std::string("bestblock"), locator); + } + + bool ReadDefaultKey(std::vector& vchPubKey) + { + vchPubKey.clear(); + return Read(std::string("defaultkey"), vchPubKey); + } + + bool WriteDefaultKey(const std::vector& vchPubKey) + { + vchDefaultKey = vchPubKey; + nWalletDBUpdated++; + return Write(std::string("defaultkey"), vchPubKey); + } + + template + bool ReadSetting(const std::string& strKey, T& value) + { + return Read(std::make_pair(std::string("setting"), strKey), value); + } + + template + bool WriteSetting(const std::string& strKey, const T& value) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("setting"), strKey), value); + } + + bool ReadAccount(const std::string& strAccount, CAccount& account); + bool WriteAccount(const std::string& strAccount, const CAccount& account); + bool WriteAccountingEntry(const CAccountingEntry& acentry); + int64 GetAccountCreditDebit(const std::string& strAccount); + void ListAccountCreditDebit(const std::string& strAccount, std::list& acentries); + + bool LoadWallet(); +protected: + void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool); + void KeepKey(int64 nIndex); + static void ReturnKey(int64 nIndex); + friend class CReserveKey; + friend std::vector GetKeyFromKeyPool(); + friend int64 GetOldestKeyPoolTime(); +}; + +bool LoadWallet(bool& fFirstRunRet); +void BackupWallet(const std::string& strDest); + +inline bool SetAddressBookName(const std::string& strAddress, const std::string& strName) +{ + return CWalletDB().WriteName(strAddress, strName); +} + +class CReserveKey +{ +protected: + int64 nIndex; + std::vector vchPubKey; +public: + CReserveKey() + { + nIndex = -1; + } + + ~CReserveKey() + { + if (!fShutdown) + ReturnKey(); + } + + std::vector GetReservedKey() + { + if (nIndex == -1) + { + CKeyPool keypool; + CWalletDB().ReserveKeyFromKeyPool(nIndex, keypool); + vchPubKey = keypool.vchPubKey; + } + assert(!vchPubKey.empty()); + return vchPubKey; + } + + void KeepKey() + { + if (nIndex != -1) + CWalletDB().KeepKey(nIndex); + nIndex = -1; + vchPubKey.clear(); + } + + void ReturnKey() + { + if (nIndex != -1) + CWalletDB::ReturnKey(nIndex); + nIndex = -1; + vchPubKey.clear(); + } +}; + +#endif diff --git a/src/externui.h b/src/externui.h new file mode 100644 index 0000000000..e58ccc2242 --- /dev/null +++ b/src/externui.h @@ -0,0 +1,45 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_EXTERNUI_H +#define BITCOIN_EXTERNUI_H + +#include + +typedef void wxWindow; +#define wxYES 0x00000002 +#define wxOK 0x00000004 +#define wxNO 0x00000008 +#define wxYES_NO (wxYES|wxNO) +#define wxCANCEL 0x00000010 +#define wxAPPLY 0x00000020 +#define wxCLOSE 0x00000040 +#define wxOK_DEFAULT 0x00000000 +#define wxYES_DEFAULT 0x00000000 +#define wxNO_DEFAULT 0x00000080 +#define wxCANCEL_DEFAULT 0x80000000 +#define wxICON_EXCLAMATION 0x00000100 +#define wxICON_HAND 0x00000200 +#define wxICON_WARNING wxICON_EXCLAMATION +#define wxICON_ERROR wxICON_HAND +#define wxICON_QUESTION 0x00000400 +#define wxICON_INFORMATION 0x00000800 +#define wxICON_STOP wxICON_HAND +#define wxICON_ASTERISK wxICON_INFORMATION +#define wxICON_MASK (0x00000100|0x00000200|0x00000400|0x00000800) +#define wxFORWARD 0x00001000 +#define wxBACKWARD 0x00002000 +#define wxRESET 0x00004000 +#define wxHELP 0x00008000 +#define wxMORE 0x00010000 +#define wxSETUP 0x00020000 + +extern int MyMessageBox(const std::string& message, const std::string& caption="Message", int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1); +#define wxMessageBox MyMessageBox +extern int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1); +extern bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent); +extern void CalledSetStatusBar(const std::string& strText, int nField); +extern void UIThreadCall(boost::function0 fn); +extern void MainFrameRepaint(); + +#endif diff --git a/src/headers.h b/src/headers.h new file mode 100644 index 0000000000..33aeef3383 --- /dev/null +++ b/src/headers.h @@ -0,0 +1,147 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#ifdef _MSC_VER +#pragma warning(disable:4786) +#pragma warning(disable:4804) +#pragma warning(disable:4805) +#pragma warning(disable:4717) +#endif +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0500 +#ifdef _WIN32_IE +#undef _WIN32_IE +#endif +#define _WIN32_IE 0x0400 +#define WIN32_LEAN_AND_MEAN 1 +#define __STDC_LIMIT_MACROS // to enable UINT64_MAX from stdint.h +#if (defined(__unix__) || defined(unix)) && !defined(USG) +#include // to get BSD define +#endif +#ifdef __WXMAC_OSX__ +#ifndef BSD +#define BSD 1 +#endif +#endif +#ifdef GUI +#include +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __WXMSW__ +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#ifdef BSD +#include +#endif + + +#pragma hdrstop + +#include "strlcpy.h" +#include "serialize.h" +#include "uint256.h" +#include "util.h" +#include "key.h" +#include "bignum.h" +#include "base58.h" +#include "script.h" +#include "db.h" +#include "net.h" +#include "irc.h" +#include "main.h" +#include "rpc.h" +#ifdef GUI +#include "uibase.h" +#include "ui.h" +#else +#include "externui.h" +#endif +#include "init.h" + +#ifdef GUI +#include "xpm/addressbook16.xpm" +#include "xpm/addressbook20.xpm" +#include "xpm/bitcoin16.xpm" +#include "xpm/bitcoin20.xpm" +#include "xpm/bitcoin32.xpm" +#include "xpm/bitcoin48.xpm" +#include "xpm/bitcoin80.xpm" +#include "xpm/check.xpm" +#include "xpm/send16.xpm" +#include "xpm/send16noshadow.xpm" +#include "xpm/send20.xpm" +#include "xpm/about.xpm" +#endif diff --git a/src/init.cpp b/src/init.cpp new file mode 100644 index 0000000000..f7a1ce9ef1 --- /dev/null +++ b/src/init.cpp @@ -0,0 +1,525 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#include "headers.h" + +using namespace std; +using namespace boost; + +////////////////////////////////////////////////////////////////////////////// +// +// Shutdown +// + +void ExitTimeout(void* parg) +{ +#ifdef __WXMSW__ + Sleep(5000); + ExitProcess(0); +#endif +} + +void Shutdown(void* parg) +{ + static CCriticalSection cs_Shutdown; + static bool fTaken; + bool fFirstThread; + CRITICAL_BLOCK(cs_Shutdown) + { + fFirstThread = !fTaken; + fTaken = true; + } + static bool fExit; + if (fFirstThread) + { + fShutdown = true; + nTransactionsUpdated++; + DBFlush(false); + StopNode(); + DBFlush(true); + boost::filesystem::remove(GetPidFile()); + CreateThread(ExitTimeout, NULL); + Sleep(50); + printf("Bitcoin exiting\n\n"); + fExit = true; + exit(0); + } + else + { + while (!fExit) + Sleep(500); + Sleep(100); + ExitThread(0); + } +} + +void HandleSIGTERM(int) +{ + fRequestShutdown = true; +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Start +// +#if 0 +#ifndef GUI +int main(int argc, char* argv[]) +{ + bool fRet = false; + fRet = AppInit(argc, argv); + + if (fRet && fDaemon) + return 0; + + return 1; +} +#endif +#endif + +bool AppInit(int argc, char* argv[]) +{ + bool fRet = false; + try + { + fRet = AppInit2(argc, argv); + } + catch (std::exception& e) { + PrintException(&e, "AppInit()"); + } catch (...) { + PrintException(NULL, "AppInit()"); + } + if (!fRet) + Shutdown(NULL); + return fRet; +} + +bool AppInit2(int argc, char* argv[]) +{ +#ifdef _MSC_VER + // Turn off microsoft heap dump noise + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); +#endif +#if _MSC_VER >= 1400 + // Disable confusing "helpful" text message on abort, ctrl-c + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); +#endif +#ifndef __WXMSW__ + umask(077); +#endif +#ifndef __WXMSW__ + // Clean shutdown on SIGTERM + struct sigaction sa; + sa.sa_handler = HandleSIGTERM; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); +#endif + + // + // Parameters + // + ParseParameters(argc, argv); + + if (mapArgs.count("-datadir")) + { + filesystem::path pathDataDir = filesystem::system_complete(mapArgs["-datadir"]); + strlcpy(pszSetDataDir, pathDataDir.string().c_str(), sizeof(pszSetDataDir)); + } + + ReadConfigFile(mapArgs, mapMultiArgs); // Must be done after processing datadir + + if (mapArgs.count("-?") || mapArgs.count("--help")) + { + string beta = VERSION_IS_BETA ? _(" beta") : ""; + string strUsage = string() + + _("Bitcoin version") + " " + FormatFullVersion() + "\n\n" + + _("Usage:") + "\t\t\t\t\t\t\t\t\t\t\n" + + " bitcoin [options] \t " + "\n" + + " bitcoin [options] [params]\t " + _("Send command to -server or bitcoind\n") + + " bitcoin [options] help \t\t " + _("List commands\n") + + " bitcoin [options] help \t\t " + _("Get help for a command\n") + + _("Options:\n") + + " -conf= \t\t " + _("Specify configuration file (default: bitcoin.conf)\n") + + " -pid= \t\t " + _("Specify pid file (default: bitcoind.pid)\n") + + " -gen \t\t " + _("Generate coins\n") + + " -gen=0 \t\t " + _("Don't generate coins\n") + + " -min \t\t " + _("Start minimized\n") + + " -datadir=

\t\t " + _("Specify data directory\n") + + " -proxy= \t " + _("Connect through socks4 proxy\n") + + " -dns \t " + _("Allow DNS lookups for addnode and connect\n") + + " -addnode= \t " + _("Add a node to connect to\n") + + " -connect= \t\t " + _("Connect only to the specified node\n") + + " -nolisten \t " + _("Don't accept connections from outside\n") + +#ifdef USE_UPNP +#if USE_UPNP + " -noupnp \t " + _("Don't attempt to use UPnP to map the listening port\n") + +#else + " -upnp \t " + _("Attempt to use UPnP to map the listening port\n") + +#endif +#endif + " -paytxfee= \t " + _("Fee per KB to add to transactions you send\n") + +#ifdef GUI + " -server \t\t " + _("Accept command line and JSON-RPC commands\n") + +#endif +#ifndef __WXMSW__ + " -daemon \t\t " + _("Run in the background as a daemon and accept commands\n") + +#endif + " -testnet \t\t " + _("Use the test network\n") + + " -rpcuser= \t " + _("Username for JSON-RPC connections\n") + + " -rpcpassword=\t " + _("Password for JSON-RPC connections\n") + + " -rpcport= \t\t " + _("Listen for JSON-RPC connections on (default: 8332)\n") + + " -rpcallowip= \t\t " + _("Allow JSON-RPC connections from specified IP address\n") + + " -rpcconnect= \t " + _("Send commands to node running on (default: 127.0.0.1)\n") + + " -keypool= \t " + _("Set key pool size to (default: 100)\n") + + " -rescan \t " + _("Rescan the block chain for missing wallet transactions\n"); + +#ifdef USE_SSL + strUsage += string() + + _("\nSSL options: (see the Bitcoin Wiki for SSL setup instructions)\n") + + " -rpcssl \t " + _("Use OpenSSL (https) for JSON-RPC connections\n") + + " -rpcsslcertificatechainfile=\t " + _("Server certificate file (default: server.cert)\n") + + " -rpcsslprivatekeyfile= \t " + _("Server private key (default: server.pem)\n") + + " -rpcsslciphers= \t " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)\n"); +#endif + + strUsage += string() + + " -? \t\t " + _("This help message\n"); + +#if defined(__WXMSW__) && defined(GUI) + // Tabs make the columns line up in the message box + wxMessageBox(strUsage, "Bitcoin", wxOK); +#else + // Remove tabs + strUsage.erase(std::remove(strUsage.begin(), strUsage.end(), '\t'), strUsage.end()); + fprintf(stderr, "%s", strUsage.c_str()); +#endif + return false; + } + + fDebug = GetBoolArg("-debug"); + fAllowDNS = GetBoolArg("-dns"); + +#ifndef __WXMSW__ + fDaemon = GetBoolArg("-daemon"); +#else + fDaemon = false; +#endif + + if (fDaemon) + fServer = true; + else + fServer = GetBoolArg("-server"); + + /* force fServer when running without GUI */ +#if 0 +#ifndef GUI + fServer = true; +#endif +#endif + fPrintToConsole = GetBoolArg("-printtoconsole"); + fPrintToDebugger = GetBoolArg("-printtodebugger"); + + fTestNet = GetBoolArg("-testnet"); + fNoListen = GetBoolArg("-nolisten"); + fLogTimestamps = GetBoolArg("-logtimestamps"); + + for (int i = 1; i < argc; i++) + if (!IsSwitchChar(argv[i][0])) + fCommandLine = true; + + if (fCommandLine) + { + int ret = CommandLineRPC(argc, argv); + exit(ret); + } + +#ifndef __WXMSW__ + if (fDaemon) + { + // Daemonize + pid_t pid = fork(); + if (pid < 0) + { + fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno); + return false; + } + if (pid > 0) + { + CreatePidFile(GetPidFile(), pid); + return true; + } + + pid_t sid = setsid(); + if (sid < 0) + fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno); + } +#endif + + if (!fDebug && !pszSetDataDir[0]) + ShrinkDebugFile(); + printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); + printf("Bitcoin version %s\n", FormatFullVersion().c_str()); +#ifdef GUI + printf("OS version %s\n", ((string)wxGetOsDescription()).c_str()); + printf("System default language is %d %s\n", g_locale.GetSystemLanguage(), ((string)g_locale.GetSysName()).c_str()); + printf("Language file %s (%s)\n", (string("locale/") + (string)g_locale.GetCanonicalName() + "/LC_MESSAGES/bitcoin.mo").c_str(), ((string)g_locale.GetLocale()).c_str()); +#endif + printf("Default data directory %s\n", GetDefaultDataDir().c_str()); + + if (GetBoolArg("-loadblockindextest")) + { + CTxDB txdb("r"); + txdb.LoadBlockIndex(); + PrintBlockTree(); + return false; + } + + // + // Limit to single instance per user + // Required to protect the database files if we're going to keep deleting log.* + // +#if defined(__WXMSW__) && defined(GUI) + // wxSingleInstanceChecker doesn't work on Linux + wxString strMutexName = wxString("bitcoin_running.") + getenv("HOMEPATH"); + for (int i = 0; i < strMutexName.size(); i++) + if (!isalnum(strMutexName[i])) + strMutexName[i] = '.'; + wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName); + if (psingleinstancechecker->IsAnotherRunning()) + { + printf("Existing instance found\n"); + unsigned int nStart = GetTime(); + loop + { + // Show the previous instance and exit + HWND hwndPrev = FindWindowA("wxWindowClassNR", "Bitcoin"); + if (hwndPrev) + { + if (IsIconic(hwndPrev)) + ShowWindow(hwndPrev, SW_RESTORE); + SetForegroundWindow(hwndPrev); + return false; + } + + if (GetTime() > nStart + 60) + return false; + + // Resume this instance if the other exits + delete psingleinstancechecker; + Sleep(1000); + psingleinstancechecker = new wxSingleInstanceChecker(strMutexName); + if (!psingleinstancechecker->IsAnotherRunning()) + break; + } + } +#endif + + // Make sure only a single bitcoin process is using the data directory. + string strLockFile = GetDataDir() + "/.lock"; + FILE* file = fopen(strLockFile.c_str(), "a"); // empty lock file; created if it doesn't exist. + if (file) fclose(file); + static boost::interprocess::file_lock lock(strLockFile.c_str()); + if (!lock.try_lock()) + { + wxMessageBox(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), GetDataDir().c_str()), "Bitcoin"); + return false; + } + + // Bind to the port early so we can tell if another instance is already running. + string strErrors; + if (!fNoListen) + { + if (!BindListenPort(strErrors)) + { + wxMessageBox(strErrors, "Bitcoin"); + return false; + } + } + + // + // Load data files + // + if (fDaemon) + fprintf(stdout, "bitcoin server starting\n"); + strErrors = ""; + int64 nStart; + + printf("Loading addresses...\n"); + nStart = GetTimeMillis(); + if (!LoadAddresses()) + strErrors += _("Error loading addr.dat \n"); + printf(" addresses %15"PRI64d"ms\n", GetTimeMillis() - nStart); + + printf("Loading block index...\n"); + nStart = GetTimeMillis(); + if (!LoadBlockIndex()) + strErrors += _("Error loading blkindex.dat \n"); + printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart); + + printf("Loading wallet...\n"); + nStart = GetTimeMillis(); + bool fFirstRun; + if (!LoadWallet(fFirstRun)) + strErrors += _("Error loading wallet.dat \n"); + printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart); + + CBlockIndex *pindexRescan = pindexBest; + if (GetBoolArg("-rescan")) + pindexRescan = pindexGenesisBlock; + else + { + CWalletDB walletdb; + CBlockLocator locator; + if (walletdb.ReadBestBlock(locator)) + pindexRescan = locator.GetBlockIndex(); + } + if (pindexBest != pindexRescan) + { + printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); + nStart = GetTimeMillis(); + ScanForWalletTransactions(pindexRescan, true); + printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); + } + + printf("Done loading\n"); + + //// debug print + printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size()); + printf("nBestHeight = %d\n", nBestHeight); + printf("mapKeys.size() = %d\n", mapKeys.size()); + printf("mapPubKeys.size() = %d\n", mapPubKeys.size()); + printf("mapWallet.size() = %d\n", mapWallet.size()); + printf("mapAddressBook.size() = %d\n", mapAddressBook.size()); + + if (!strErrors.empty()) + { + wxMessageBox(strErrors, "Bitcoin", wxOK | wxICON_ERROR); + return false; + } + + // Add wallet transactions that aren't already in a block to mapTransactions + ReacceptWalletTransactions(); + + // + // Parameters + // + if (GetBoolArg("-printblockindex") || GetBoolArg("-printblocktree")) + { + PrintBlockTree(); + return false; + } + + if (mapArgs.count("-printblock")) + { + string strMatch = mapArgs["-printblock"]; + int nFound = 0; + for (map::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) + { + uint256 hash = (*mi).first; + if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0) + { + CBlockIndex* pindex = (*mi).second; + CBlock block; + block.ReadFromDisk(pindex); + block.BuildMerkleTree(); + block.print(); + printf("\n"); + nFound++; + } + } + if (nFound == 0) + printf("No blocks matching %s were found\n", strMatch.c_str()); + return false; + } + + fGenerateBitcoins = GetBoolArg("-gen"); + + if (mapArgs.count("-proxy")) + { + fUseProxy = true; + addrProxy = CAddress(mapArgs["-proxy"]); + if (!addrProxy.IsValid()) + { + wxMessageBox(_("Invalid -proxy address"), "Bitcoin"); + return false; + } + } + + if (mapArgs.count("-addnode")) + { + BOOST_FOREACH(string strAddr, mapMultiArgs["-addnode"]) + { + CAddress addr(strAddr, fAllowDNS); + addr.nTime = 0; // so it won't relay unless successfully connected + if (addr.IsValid()) + AddAddress(addr); + } + } + + if (mapArgs.count("-dnsseed")) + DNSAddressSeed(); + + if (mapArgs.count("-paytxfee")) + { + if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee)) + { + wxMessageBox(_("Invalid amount for -paytxfee="), "Bitcoin"); + return false; + } + if (nTransactionFee > 0.25 * COIN) + wxMessageBox(_("Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction."), "Bitcoin", wxOK | wxICON_EXCLAMATION); + } + + if (fHaveUPnP) + { +#if USE_UPNP + if (GetBoolArg("-noupnp")) + fUseUPnP = false; +#else + if (GetBoolArg("-upnp")) + fUseUPnP = true; +#endif + } + + // + // Create the main window and start the node + // +#ifdef GUI + if (!fDaemon) + CreateMainWindow(); +#endif + + if (!CheckDiskSpace()) + return false; + + RandAddSeedPerfmon(); + + if (!CreateThread(StartNode, NULL)) + wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin"); + + if (fServer) + CreateThread(ThreadRPCServer, NULL); + +#if defined(__WXMSW__) && defined(GUI) + if (fFirstRun) + SetStartOnSystemStartup(true); +#endif + +#if 0 +#ifndef GUI + while (1) + Sleep(5000); +#endif +#endif + + return true; +} diff --git a/src/init.h b/src/init.h new file mode 100644 index 0000000000..61b2728576 --- /dev/null +++ b/src/init.h @@ -0,0 +1,11 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_INIT_H +#define BITCOIN_INIT_H + +void Shutdown(void* parg); +bool AppInit(int argc, char* argv[]); +bool AppInit2(int argc, char* argv[]); + +#endif diff --git a/src/irc.cpp b/src/irc.cpp new file mode 100644 index 0000000000..a76374d143 --- /dev/null +++ b/src/irc.cpp @@ -0,0 +1,445 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" + +using namespace std; +using namespace boost; + +int nGotIRCAddresses = 0; +bool fGotExternalIP = false; + +void ThreadIRCSeed2(void* parg); + + + + +#pragma pack(push, 1) +struct ircaddr +{ + int ip; + short port; +}; +#pragma pack(pop) + +string EncodeAddress(const CAddress& addr) +{ + struct ircaddr tmp; + tmp.ip = addr.ip; + tmp.port = addr.port; + + vector vch(UBEGIN(tmp), UEND(tmp)); + return string("u") + EncodeBase58Check(vch); +} + +bool DecodeAddress(string str, CAddress& addr) +{ + vector vch; + if (!DecodeBase58Check(str.substr(1), vch)) + return false; + + struct ircaddr tmp; + if (vch.size() != sizeof(tmp)) + return false; + memcpy(&tmp, &vch[0], sizeof(tmp)); + + addr = CAddress(tmp.ip, ntohs(tmp.port), NODE_NETWORK); + return true; +} + + + + + + +static bool Send(SOCKET hSocket, const char* pszSend) +{ + if (strstr(pszSend, "PONG") != pszSend) + printf("IRC SENDING: %s\n", pszSend); + const char* psz = pszSend; + const char* pszEnd = psz + strlen(psz); + while (psz < pszEnd) + { + int ret = send(hSocket, psz, pszEnd - psz, MSG_NOSIGNAL); + if (ret < 0) + return false; + psz += ret; + } + return true; +} + +bool RecvLine(SOCKET hSocket, string& strLine) +{ + strLine = ""; + loop + { + char c; + int nBytes = recv(hSocket, &c, 1, 0); + if (nBytes > 0) + { + if (c == '\n') + continue; + if (c == '\r') + return true; + strLine += c; + if (strLine.size() >= 9000) + return true; + } + else if (nBytes <= 0) + { + if (fShutdown) + return false; + if (nBytes < 0) + { + int nErr = WSAGetLastError(); + if (nErr == WSAEMSGSIZE) + continue; + if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS) + { + Sleep(10); + continue; + } + } + if (!strLine.empty()) + return true; + if (nBytes == 0) + { + // socket closed + printf("IRC socket closed\n"); + return false; + } + else + { + // socket error + int nErr = WSAGetLastError(); + printf("IRC recv failed: %d\n", nErr); + return false; + } + } + } +} + +bool RecvLineIRC(SOCKET hSocket, string& strLine) +{ + loop + { + bool fRet = RecvLine(hSocket, strLine); + if (fRet) + { + if (fShutdown) + return false; + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords.size() >= 1 && vWords[0] == "PING") + { + strLine[1] = 'O'; + strLine += '\r'; + Send(hSocket, strLine.c_str()); + continue; + } + } + return fRet; + } +} + +int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL, const char* psz4=NULL) +{ + loop + { + string strLine; + strLine.reserve(10000); + if (!RecvLineIRC(hSocket, strLine)) + return 0; + printf("IRC %s\n", strLine.c_str()); + if (psz1 && strLine.find(psz1) != -1) + return 1; + if (psz2 && strLine.find(psz2) != -1) + return 2; + if (psz3 && strLine.find(psz3) != -1) + return 3; + if (psz4 && strLine.find(psz4) != -1) + return 4; + } +} + +bool Wait(int nSeconds) +{ + if (fShutdown) + return false; + printf("IRC waiting %d seconds to reconnect\n", nSeconds); + for (int i = 0; i < nSeconds; i++) + { + if (fShutdown) + return false; + Sleep(1000); + } + return true; +} + +bool RecvCodeLine(SOCKET hSocket, const char* psz1, string& strRet) +{ + strRet.clear(); + loop + { + string strLine; + if (!RecvLineIRC(hSocket, strLine)) + return false; + + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords.size() < 2) + continue; + + if (vWords[1] == psz1) + { + printf("IRC %s\n", strLine.c_str()); + strRet = strLine; + return true; + } + } +} + +bool GetIPFromIRC(SOCKET hSocket, string strMyName, unsigned int& ipRet) +{ + Send(hSocket, strprintf("USERHOST %s\r", strMyName.c_str()).c_str()); + + string strLine; + if (!RecvCodeLine(hSocket, "302", strLine)) + return false; + + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords.size() < 4) + return false; + + string str = vWords[3]; + if (str.rfind("@") == string::npos) + return false; + string strHost = str.substr(str.rfind("@")+1); + + // Hybrid IRC used by lfnet always returns IP when you userhost yourself, + // but in case another IRC is ever used this should work. + printf("GetIPFromIRC() got userhost %s\n", strHost.c_str()); + if (fUseProxy) + return false; + CAddress addr(strHost, 0, true); + if (!addr.IsValid()) + return false; + ipRet = addr.ip; + + return true; +} + + + +void ThreadIRCSeed(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadIRCSeed(parg)); + try + { + ThreadIRCSeed2(parg); + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "ThreadIRCSeed()"); + } catch (...) { + PrintExceptionContinue(NULL, "ThreadIRCSeed()"); + } + printf("ThreadIRCSeed exiting\n"); +} + +void ThreadIRCSeed2(void* parg) +{ + /* Dont advertise on IRC if we don't allow incoming connections */ + if (mapArgs.count("-connect") || fNoListen) + return; + + if (GetBoolArg("-noirc")) + return; + printf("ThreadIRCSeed started\n"); + int nErrorWait = 10; + int nRetryWait = 10; + bool fNameInUse = false; + bool fTOR = (fUseProxy && addrProxy.port == htons(9050)); + + while (!fShutdown) + { + //CAddress addrConnect("216.155.130.130:6667"); // chat.freenode.net + CAddress addrConnect("92.243.23.21", 6667); // irc.lfnet.org + if (!fTOR) + { + //struct hostent* phostent = gethostbyname("chat.freenode.net"); + CAddress addrIRC("irc.lfnet.org", 6667, true); + if (addrIRC.IsValid()) + addrConnect = addrIRC; + } + + SOCKET hSocket; + if (!ConnectSocket(addrConnect, hSocket)) + { + printf("IRC connect failed\n"); + nErrorWait = nErrorWait * 11 / 10; + if (Wait(nErrorWait += 60)) + continue; + else + return; + } + + if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname", "ignoring hostname")) + { + closesocket(hSocket); + hSocket = INVALID_SOCKET; + nErrorWait = nErrorWait * 11 / 10; + if (Wait(nErrorWait += 60)) + continue; + else + return; + } + + string strMyName; + if (addrLocalHost.IsRoutable() && !fUseProxy && !fNameInUse) + strMyName = EncodeAddress(addrLocalHost); + else + strMyName = strprintf("x%u", GetRand(1000000000)); + + Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str()); + Send(hSocket, strprintf("USER %s 8 * : %s\r", strMyName.c_str(), strMyName.c_str()).c_str()); + + int nRet = RecvUntil(hSocket, " 004 ", " 433 "); + if (nRet != 1) + { + closesocket(hSocket); + hSocket = INVALID_SOCKET; + if (nRet == 2) + { + printf("IRC name already in use\n"); + fNameInUse = true; + Wait(10); + continue; + } + nErrorWait = nErrorWait * 11 / 10; + if (Wait(nErrorWait += 60)) + continue; + else + return; + } + Sleep(500); + + // Get our external IP from the IRC server and re-nick before joining the channel + CAddress addrFromIRC; + if (GetIPFromIRC(hSocket, strMyName, addrFromIRC.ip)) + { + printf("GetIPFromIRC() returned %s\n", addrFromIRC.ToStringIP().c_str()); + if (!fUseProxy && addrFromIRC.IsRoutable()) + { + // IRC lets you to re-nick + fGotExternalIP = true; + addrLocalHost.ip = addrFromIRC.ip; + strMyName = EncodeAddress(addrLocalHost); + Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str()); + } + } + + if (fTestNet) { + Send(hSocket, "JOIN #bitcoinTEST\r"); + Send(hSocket, "WHO #bitcoinTEST\r"); + } else { + // randomly join #bitcoin00-#bitcoin99 + int channel_number = GetRandInt(100); + Send(hSocket, strprintf("JOIN #bitcoin%02d\r", channel_number).c_str()); + Send(hSocket, strprintf("WHO #bitcoin%02d\r", channel_number).c_str()); + } + + int64 nStart = GetTime(); + string strLine; + strLine.reserve(10000); + while (!fShutdown && RecvLineIRC(hSocket, strLine)) + { + if (strLine.empty() || strLine.size() > 900 || strLine[0] != ':') + continue; + + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords.size() < 2) + continue; + + char pszName[10000]; + pszName[0] = '\0'; + + if (vWords[1] == "352" && vWords.size() >= 8) + { + // index 7 is limited to 16 characters + // could get full length name at index 10, but would be different from join messages + strlcpy(pszName, vWords[7].c_str(), sizeof(pszName)); + printf("IRC got who\n"); + } + + if (vWords[1] == "JOIN" && vWords[0].size() > 1) + { + // :username!username@50000007.F000000B.90000002.IP JOIN :#channelname + strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName)); + if (strchr(pszName, '!')) + *strchr(pszName, '!') = '\0'; + printf("IRC got join\n"); + } + + if (pszName[0] == 'u') + { + CAddress addr; + if (DecodeAddress(pszName, addr)) + { + addr.nTime = GetAdjustedTime(); + if (AddAddress(addr, 51 * 60)) + printf("IRC got new address: %s\n", addr.ToString().c_str()); + nGotIRCAddresses++; + } + else + { + printf("IRC decode failed\n"); + } + } + } + closesocket(hSocket); + hSocket = INVALID_SOCKET; + + // IRC usually blocks TOR, so only try once + if (fTOR) + return; + + if (GetTime() - nStart > 20 * 60) + { + nErrorWait /= 3; + nRetryWait /= 3; + } + + nRetryWait = nRetryWait * 11 / 10; + if (!Wait(nRetryWait += 60)) + return; + } +} + + + + + + + + + + +#ifdef TEST +int main(int argc, char *argv[]) +{ + WSADATA wsadata; + if (WSAStartup(MAKEWORD(2,2), &wsadata) != NO_ERROR) + { + printf("Error at WSAStartup()\n"); + return false; + } + + ThreadIRCSeed(NULL); + + WSACleanup(); + return 0; +} +#endif diff --git a/src/irc.h b/src/irc.h new file mode 100644 index 0000000000..18e53597f6 --- /dev/null +++ b/src/irc.h @@ -0,0 +1,13 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_IRC_H +#define BITCOIN_IRC_H + +bool RecvLine(SOCKET hSocket, std::string& strLine); +void ThreadIRCSeed(void* parg); + +extern int nGotIRCAddresses; +extern bool fGotExternalIP; + +#endif diff --git a/src/json/json_spirit.h b/src/json/json_spirit.h new file mode 100644 index 0000000000..ac1879d5b3 --- /dev/null +++ b/src/json/json_spirit.h @@ -0,0 +1,18 @@ +#ifndef JSON_SPIRIT +#define JSON_SPIRIT + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include "json_spirit_reader.h" +#include "json_spirit_writer.h" +#include "json_spirit_utils.h" + +#endif diff --git a/src/json/json_spirit_error_position.h b/src/json/json_spirit_error_position.h new file mode 100644 index 0000000000..17208507df --- /dev/null +++ b/src/json/json_spirit_error_position.h @@ -0,0 +1,54 @@ +#ifndef JSON_SPIRIT_ERROR_POSITION +#define JSON_SPIRIT_ERROR_POSITION + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include + +namespace json_spirit +{ + // An Error_position exception is thrown by the "read_or_throw" functions below on finding an error. + // Note the "read_or_throw" functions are around 3 times slower than the standard functions "read" + // functions that return a bool. + // + struct Error_position + { + Error_position(); + Error_position( unsigned int line, unsigned int column, const std::string& reason ); + bool operator==( const Error_position& lhs ) const; + unsigned int line_; + unsigned int column_; + std::string reason_; + }; + + inline Error_position::Error_position() + : line_( 0 ) + , column_( 0 ) + { + } + + inline Error_position::Error_position( unsigned int line, unsigned int column, const std::string& reason ) + : line_( line ) + , column_( column ) + , reason_( reason ) + { + } + + inline bool Error_position::operator==( const Error_position& lhs ) const + { + if( this == &lhs ) return true; + + return ( reason_ == lhs.reason_ ) && + ( line_ == lhs.line_ ) && + ( column_ == lhs.column_ ); +} +} + +#endif diff --git a/src/json/json_spirit_reader.cpp b/src/json/json_spirit_reader.cpp new file mode 100644 index 0000000000..3469bf17af --- /dev/null +++ b/src/json/json_spirit_reader.cpp @@ -0,0 +1,137 @@ +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json/json_spirit_reader.h" +#include "json/json_spirit_reader_template.h" + +using namespace json_spirit; + +bool json_spirit::read( const std::string& s, Value& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::string& s, Value& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::istream& is, Value& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::istream& is, Value& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#ifndef BOOST_NO_STD_WSTRING + +bool json_spirit::read( const std::wstring& s, wValue& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::wstring& s, wValue& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::wistream& is, wValue& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::wistream& is, wValue& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#endif + +bool json_spirit::read( const std::string& s, mValue& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::string& s, mValue& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::istream& is, mValue& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::istream& is, mValue& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#ifndef BOOST_NO_STD_WSTRING + +bool json_spirit::read( const std::wstring& s, wmValue& value ) +{ + return read_string( s, value ); +} + +void json_spirit::read_or_throw( const std::wstring& s, wmValue& value ) +{ + read_string_or_throw( s, value ); +} + +bool json_spirit::read( std::wistream& is, wmValue& value ) +{ + return read_stream( is, value ); +} + +void json_spirit::read_or_throw( std::wistream& is, wmValue& value ) +{ + read_stream_or_throw( is, value ); +} + +bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) +{ + return read_range( begin, end, value ); +} + +void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) +{ + begin = read_range_or_throw( begin, end, value ); +} + +#endif diff --git a/src/json/json_spirit_reader.h b/src/json/json_spirit_reader.h new file mode 100644 index 0000000000..96494a9789 --- /dev/null +++ b/src/json/json_spirit_reader.h @@ -0,0 +1,62 @@ +#ifndef JSON_SPIRIT_READER +#define JSON_SPIRIT_READER + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include "json_spirit_error_position.h" +#include + +namespace json_spirit +{ + // functions to reads a JSON values + + bool read( const std::string& s, Value& value ); + bool read( std::istream& is, Value& value ); + bool read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); + + void read_or_throw( const std::string& s, Value& value ); + void read_or_throw( std::istream& is, Value& value ); + void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); + +#ifndef BOOST_NO_STD_WSTRING + + bool read( const std::wstring& s, wValue& value ); + bool read( std::wistream& is, wValue& value ); + bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); + + void read_or_throw( const std::wstring& s, wValue& value ); + void read_or_throw( std::wistream& is, wValue& value ); + void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); + +#endif + + bool read( const std::string& s, mValue& value ); + bool read( std::istream& is, mValue& value ); + bool read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); + + void read_or_throw( const std::string& s, mValue& value ); + void read_or_throw( std::istream& is, mValue& value ); + void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); + +#ifndef BOOST_NO_STD_WSTRING + + bool read( const std::wstring& s, wmValue& value ); + bool read( std::wistream& is, wmValue& value ); + bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); + + void read_or_throw( const std::wstring& s, wmValue& value ); + void read_or_throw( std::wistream& is, wmValue& value ); + void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); + +#endif +} + +#endif diff --git a/src/json/json_spirit_reader_template.h b/src/json/json_spirit_reader_template.h new file mode 100644 index 0000000000..4dec00e6c9 --- /dev/null +++ b/src/json/json_spirit_reader_template.h @@ -0,0 +1,612 @@ +#ifndef JSON_SPIRIT_READER_TEMPLATE +#define JSON_SPIRIT_READER_TEMPLATE + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json_spirit_value.h" +#include "json_spirit_error_position.h" + +//#define BOOST_SPIRIT_THREADSAFE // uncomment for multithreaded use, requires linking to boost.thread + +#include +#include +#include + +#if BOOST_VERSION >= 103800 + #include + #include + #include + #include + #include + #define spirit_namespace boost::spirit::classic +#else + #include + #include + #include + #include + #include + #define spirit_namespace boost::spirit +#endif + +namespace json_spirit +{ + const spirit_namespace::int_parser < boost::int64_t > int64_p = spirit_namespace::int_parser < boost::int64_t >(); + const spirit_namespace::uint_parser< boost::uint64_t > uint64_p = spirit_namespace::uint_parser< boost::uint64_t >(); + + template< class Iter_type > + bool is_eq( Iter_type first, Iter_type last, const char* c_str ) + { + for( Iter_type i = first; i != last; ++i, ++c_str ) + { + if( *c_str == 0 ) return false; + + if( *i != *c_str ) return false; + } + + return true; + } + + template< class Char_type > + Char_type hex_to_num( const Char_type c ) + { + if( ( c >= '0' ) && ( c <= '9' ) ) return c - '0'; + if( ( c >= 'a' ) && ( c <= 'f' ) ) return c - 'a' + 10; + if( ( c >= 'A' ) && ( c <= 'F' ) ) return c - 'A' + 10; + return 0; + } + + template< class Char_type, class Iter_type > + Char_type hex_str_to_char( Iter_type& begin ) + { + const Char_type c1( *( ++begin ) ); + const Char_type c2( *( ++begin ) ); + + return ( hex_to_num( c1 ) << 4 ) + hex_to_num( c2 ); + } + + template< class Char_type, class Iter_type > + Char_type unicode_str_to_char( Iter_type& begin ) + { + const Char_type c1( *( ++begin ) ); + const Char_type c2( *( ++begin ) ); + const Char_type c3( *( ++begin ) ); + const Char_type c4( *( ++begin ) ); + + return ( hex_to_num( c1 ) << 12 ) + + ( hex_to_num( c2 ) << 8 ) + + ( hex_to_num( c3 ) << 4 ) + + hex_to_num( c4 ); + } + + template< class String_type > + void append_esc_char_and_incr_iter( String_type& s, + typename String_type::const_iterator& begin, + typename String_type::const_iterator end ) + { + typedef typename String_type::value_type Char_type; + + const Char_type c2( *begin ); + + switch( c2 ) + { + case 't': s += '\t'; break; + case 'b': s += '\b'; break; + case 'f': s += '\f'; break; + case 'n': s += '\n'; break; + case 'r': s += '\r'; break; + case '\\': s += '\\'; break; + case '/': s += '/'; break; + case '"': s += '"'; break; + case 'x': + { + if( end - begin >= 3 ) // expecting "xHH..." + { + s += hex_str_to_char< Char_type >( begin ); + } + break; + } + case 'u': + { + if( end - begin >= 5 ) // expecting "uHHHH..." + { + s += unicode_str_to_char< Char_type >( begin ); + } + break; + } + } + } + + template< class String_type > + String_type substitute_esc_chars( typename String_type::const_iterator begin, + typename String_type::const_iterator end ) + { + typedef typename String_type::const_iterator Iter_type; + + if( end - begin < 2 ) return String_type( begin, end ); + + String_type result; + + result.reserve( end - begin ); + + const Iter_type end_minus_1( end - 1 ); + + Iter_type substr_start = begin; + Iter_type i = begin; + + for( ; i < end_minus_1; ++i ) + { + if( *i == '\\' ) + { + result.append( substr_start, i ); + + ++i; // skip the '\' + + append_esc_char_and_incr_iter( result, i, end ); + + substr_start = i + 1; + } + } + + result.append( substr_start, end ); + + return result; + } + + template< class String_type > + String_type get_str_( typename String_type::const_iterator begin, + typename String_type::const_iterator end ) + { + assert( end - begin >= 2 ); + + typedef typename String_type::const_iterator Iter_type; + + Iter_type str_without_quotes( ++begin ); + Iter_type end_without_quotes( --end ); + + return substitute_esc_chars< String_type >( str_without_quotes, end_without_quotes ); + } + + inline std::string get_str( std::string::const_iterator begin, std::string::const_iterator end ) + { + return get_str_< std::string >( begin, end ); + } + + inline std::wstring get_str( std::wstring::const_iterator begin, std::wstring::const_iterator end ) + { + return get_str_< std::wstring >( begin, end ); + } + + template< class String_type, class Iter_type > + String_type get_str( Iter_type begin, Iter_type end ) + { + const String_type tmp( begin, end ); // convert multipass iterators to string iterators + + return get_str( tmp.begin(), tmp.end() ); + } + + // this class's methods get called by the spirit parse resulting + // in the creation of a JSON object or array + // + // NB Iter_type could be a std::string iterator, wstring iterator, a position iterator or a multipass iterator + // + template< class Value_type, class Iter_type > + class Semantic_actions + { + public: + + typedef typename Value_type::Config_type Config_type; + typedef typename Config_type::String_type String_type; + typedef typename Config_type::Object_type Object_type; + typedef typename Config_type::Array_type Array_type; + typedef typename String_type::value_type Char_type; + + Semantic_actions( Value_type& value ) + : value_( value ) + , current_p_( 0 ) + { + } + + void begin_obj( Char_type c ) + { + assert( c == '{' ); + + begin_compound< Object_type >(); + } + + void end_obj( Char_type c ) + { + assert( c == '}' ); + + end_compound(); + } + + void begin_array( Char_type c ) + { + assert( c == '[' ); + + begin_compound< Array_type >(); + } + + void end_array( Char_type c ) + { + assert( c == ']' ); + + end_compound(); + } + + void new_name( Iter_type begin, Iter_type end ) + { + assert( current_p_->type() == obj_type ); + + name_ = get_str< String_type >( begin, end ); + } + + void new_str( Iter_type begin, Iter_type end ) + { + add_to_current( get_str< String_type >( begin, end ) ); + } + + void new_true( Iter_type begin, Iter_type end ) + { + assert( is_eq( begin, end, "true" ) ); + + add_to_current( true ); + } + + void new_false( Iter_type begin, Iter_type end ) + { + assert( is_eq( begin, end, "false" ) ); + + add_to_current( false ); + } + + void new_null( Iter_type begin, Iter_type end ) + { + assert( is_eq( begin, end, "null" ) ); + + add_to_current( Value_type() ); + } + + void new_int( boost::int64_t i ) + { + add_to_current( i ); + } + + void new_uint64( boost::uint64_t ui ) + { + add_to_current( ui ); + } + + void new_real( double d ) + { + add_to_current( d ); + } + + private: + + Semantic_actions& operator=( const Semantic_actions& ); + // to prevent "assignment operator could not be generated" warning + + Value_type* add_first( const Value_type& value ) + { + assert( current_p_ == 0 ); + + value_ = value; + current_p_ = &value_; + return current_p_; + } + + template< class Array_or_obj > + void begin_compound() + { + if( current_p_ == 0 ) + { + add_first( Array_or_obj() ); + } + else + { + stack_.push_back( current_p_ ); + + Array_or_obj new_array_or_obj; // avoid copy by building new array or object in place + + current_p_ = add_to_current( new_array_or_obj ); + } + } + + void end_compound() + { + if( current_p_ != &value_ ) + { + current_p_ = stack_.back(); + + stack_.pop_back(); + } + } + + Value_type* add_to_current( const Value_type& value ) + { + if( current_p_ == 0 ) + { + return add_first( value ); + } + else if( current_p_->type() == array_type ) + { + current_p_->get_array().push_back( value ); + + return ¤t_p_->get_array().back(); + } + + assert( current_p_->type() == obj_type ); + + return &Config_type::add( current_p_->get_obj(), name_, value ); + } + + Value_type& value_; // this is the object or array that is being created + Value_type* current_p_; // the child object or array that is currently being constructed + + std::vector< Value_type* > stack_; // previous child objects and arrays + + String_type name_; // of current name/value pair + }; + + template< typename Iter_type > + void throw_error( spirit_namespace::position_iterator< Iter_type > i, const std::string& reason ) + { + throw Error_position( i.get_position().line, i.get_position().column, reason ); + } + + template< typename Iter_type > + void throw_error( Iter_type i, const std::string& reason ) + { + throw reason; + } + + // the spirit grammer + // + template< class Value_type, class Iter_type > + class Json_grammer : public spirit_namespace::grammar< Json_grammer< Value_type, Iter_type > > + { + public: + + typedef Semantic_actions< Value_type, Iter_type > Semantic_actions_t; + + Json_grammer( Semantic_actions_t& semantic_actions ) + : actions_( semantic_actions ) + { + } + + static void throw_not_value( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not a value" ); + } + + static void throw_not_array( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not an array" ); + } + + static void throw_not_object( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not an object" ); + } + + static void throw_not_pair( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not a pair" ); + } + + static void throw_not_colon( Iter_type begin, Iter_type end ) + { + throw_error( begin, "no colon in pair" ); + } + + static void throw_not_string( Iter_type begin, Iter_type end ) + { + throw_error( begin, "not a string" ); + } + + template< typename ScannerT > + class definition + { + public: + + definition( const Json_grammer& self ) + { + using namespace spirit_namespace; + + typedef typename Value_type::String_type::value_type Char_type; + + // first we convert the semantic action class methods to functors with the + // parameter signature expected by spirit + + typedef boost::function< void( Char_type ) > Char_action; + typedef boost::function< void( Iter_type, Iter_type ) > Str_action; + typedef boost::function< void( double ) > Real_action; + typedef boost::function< void( boost::int64_t ) > Int_action; + typedef boost::function< void( boost::uint64_t ) > Uint64_action; + + Char_action begin_obj ( boost::bind( &Semantic_actions_t::begin_obj, &self.actions_, _1 ) ); + Char_action end_obj ( boost::bind( &Semantic_actions_t::end_obj, &self.actions_, _1 ) ); + Char_action begin_array( boost::bind( &Semantic_actions_t::begin_array, &self.actions_, _1 ) ); + Char_action end_array ( boost::bind( &Semantic_actions_t::end_array, &self.actions_, _1 ) ); + Str_action new_name ( boost::bind( &Semantic_actions_t::new_name, &self.actions_, _1, _2 ) ); + Str_action new_str ( boost::bind( &Semantic_actions_t::new_str, &self.actions_, _1, _2 ) ); + Str_action new_true ( boost::bind( &Semantic_actions_t::new_true, &self.actions_, _1, _2 ) ); + Str_action new_false ( boost::bind( &Semantic_actions_t::new_false, &self.actions_, _1, _2 ) ); + Str_action new_null ( boost::bind( &Semantic_actions_t::new_null, &self.actions_, _1, _2 ) ); + Real_action new_real ( boost::bind( &Semantic_actions_t::new_real, &self.actions_, _1 ) ); + Int_action new_int ( boost::bind( &Semantic_actions_t::new_int, &self.actions_, _1 ) ); + Uint64_action new_uint64 ( boost::bind( &Semantic_actions_t::new_uint64, &self.actions_, _1 ) ); + + // actual grammer + + json_ + = value_ | eps_p[ &throw_not_value ] + ; + + value_ + = string_[ new_str ] + | number_ + | object_ + | array_ + | str_p( "true" ) [ new_true ] + | str_p( "false" )[ new_false ] + | str_p( "null" ) [ new_null ] + ; + + object_ + = ch_p('{')[ begin_obj ] + >> !members_ + >> ( ch_p('}')[ end_obj ] | eps_p[ &throw_not_object ] ) + ; + + members_ + = pair_ >> *( ',' >> pair_ ) + ; + + pair_ + = string_[ new_name ] + >> ( ':' | eps_p[ &throw_not_colon ] ) + >> ( value_ | eps_p[ &throw_not_value ] ) + ; + + array_ + = ch_p('[')[ begin_array ] + >> !elements_ + >> ( ch_p(']')[ end_array ] | eps_p[ &throw_not_array ] ) + ; + + elements_ + = value_ >> *( ',' >> value_ ) + ; + + string_ + = lexeme_d // this causes white space inside a string to be retained + [ + confix_p + ( + '"', + *lex_escape_ch_p, + '"' + ) + ] + ; + + number_ + = strict_real_p[ new_real ] + | int64_p [ new_int ] + | uint64_p [ new_uint64 ] + ; + } + + spirit_namespace::rule< ScannerT > json_, object_, members_, pair_, array_, elements_, value_, string_, number_; + + const spirit_namespace::rule< ScannerT >& start() const { return json_; } + }; + + private: + + Json_grammer& operator=( const Json_grammer& ); // to prevent "assignment operator could not be generated" warning + + Semantic_actions_t& actions_; + }; + + template< class Iter_type, class Value_type > + Iter_type read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) + { + Semantic_actions< Value_type, Iter_type > semantic_actions( value ); + + const spirit_namespace::parse_info< Iter_type > info = + spirit_namespace::parse( begin, end, + Json_grammer< Value_type, Iter_type >( semantic_actions ), + spirit_namespace::space_p ); + + if( !info.hit ) + { + assert( false ); // in theory exception should already have been thrown + throw_error( info.stop, "error" ); + } + + return info.stop; + } + + template< class Iter_type, class Value_type > + void add_posn_iter_and_read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) + { + typedef spirit_namespace::position_iterator< Iter_type > Posn_iter_t; + + const Posn_iter_t posn_begin( begin, end ); + const Posn_iter_t posn_end( end, end ); + + read_range_or_throw( posn_begin, posn_end, value ); + } + + template< class Iter_type, class Value_type > + bool read_range( Iter_type& begin, Iter_type end, Value_type& value ) + { + try + { + begin = read_range_or_throw( begin, end, value ); + + return true; + } + catch( ... ) + { + return false; + } + } + + template< class String_type, class Value_type > + void read_string_or_throw( const String_type& s, Value_type& value ) + { + add_posn_iter_and_read_range_or_throw( s.begin(), s.end(), value ); + } + + template< class String_type, class Value_type > + bool read_string( const String_type& s, Value_type& value ) + { + typename String_type::const_iterator begin = s.begin(); + + return read_range( begin, s.end(), value ); + } + + template< class Istream_type > + struct Multi_pass_iters + { + typedef typename Istream_type::char_type Char_type; + typedef std::istream_iterator< Char_type, Char_type > istream_iter; + typedef spirit_namespace::multi_pass< istream_iter > Mp_iter; + + Multi_pass_iters( Istream_type& is ) + { + is.unsetf( std::ios::skipws ); + + begin_ = spirit_namespace::make_multi_pass( istream_iter( is ) ); + end_ = spirit_namespace::make_multi_pass( istream_iter() ); + } + + Mp_iter begin_; + Mp_iter end_; + }; + + template< class Istream_type, class Value_type > + bool read_stream( Istream_type& is, Value_type& value ) + { + Multi_pass_iters< Istream_type > mp_iters( is ); + + return read_range( mp_iters.begin_, mp_iters.end_, value ); + } + + template< class Istream_type, class Value_type > + void read_stream_or_throw( Istream_type& is, Value_type& value ) + { + const Multi_pass_iters< Istream_type > mp_iters( is ); + + add_posn_iter_and_read_range_or_throw( mp_iters.begin_, mp_iters.end_, value ); + } +} + +#endif diff --git a/src/json/json_spirit_stream_reader.h b/src/json/json_spirit_stream_reader.h new file mode 100644 index 0000000000..7e59c9adc2 --- /dev/null +++ b/src/json/json_spirit_stream_reader.h @@ -0,0 +1,70 @@ +#ifndef JSON_SPIRIT_READ_STREAM +#define JSON_SPIRIT_READ_STREAM + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_reader_template.h" + +namespace json_spirit +{ + // these classes allows you to read multiple top level contiguous values from a stream, + // the normal stream read functions have a bug that prevent multiple top level values + // from being read unless they are separated by spaces + + template< class Istream_type, class Value_type > + class Stream_reader + { + public: + + Stream_reader( Istream_type& is ) + : iters_( is ) + { + } + + bool read_next( Value_type& value ) + { + return read_range( iters_.begin_, iters_.end_, value ); + } + + private: + + typedef Multi_pass_iters< Istream_type > Mp_iters; + + Mp_iters iters_; + }; + + template< class Istream_type, class Value_type > + class Stream_reader_thrower + { + public: + + Stream_reader_thrower( Istream_type& is ) + : iters_( is ) + , posn_begin_( iters_.begin_, iters_.end_ ) + , posn_end_( iters_.end_, iters_.end_ ) + { + } + + void read_next( Value_type& value ) + { + posn_begin_ = read_range_or_throw( posn_begin_, posn_end_, value ); + } + + private: + + typedef Multi_pass_iters< Istream_type > Mp_iters; + typedef spirit_namespace::position_iterator< typename Mp_iters::Mp_iter > Posn_iter_t; + + Mp_iters iters_; + Posn_iter_t posn_begin_, posn_end_; + }; +} + +#endif diff --git a/src/json/json_spirit_utils.h b/src/json/json_spirit_utils.h new file mode 100644 index 0000000000..553e3b96a4 --- /dev/null +++ b/src/json/json_spirit_utils.h @@ -0,0 +1,61 @@ +#ifndef JSON_SPIRIT_UTILS +#define JSON_SPIRIT_UTILS + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include + +namespace json_spirit +{ + template< class Obj_t, class Map_t > + void obj_to_map( const Obj_t& obj, Map_t& mp_obj ) + { + mp_obj.clear(); + + for( typename Obj_t::const_iterator i = obj.begin(); i != obj.end(); ++i ) + { + mp_obj[ i->name_ ] = i->value_; + } + } + + template< class Obj_t, class Map_t > + void map_to_obj( const Map_t& mp_obj, Obj_t& obj ) + { + obj.clear(); + + for( typename Map_t::const_iterator i = mp_obj.begin(); i != mp_obj.end(); ++i ) + { + obj.push_back( typename Obj_t::value_type( i->first, i->second ) ); + } + } + + typedef std::map< std::string, Value > Mapped_obj; + +#ifndef BOOST_NO_STD_WSTRING + typedef std::map< std::wstring, wValue > wMapped_obj; +#endif + + template< class Object_type, class String_type > + const typename Object_type::value_type::Value_type& find_value( const Object_type& obj, const String_type& name ) + { + for( typename Object_type::const_iterator i = obj.begin(); i != obj.end(); ++i ) + { + if( i->name_ == name ) + { + return i->value_; + } + } + + return Object_type::value_type::Value_type::null; + } +} + +#endif diff --git a/src/json/json_spirit_value.cpp b/src/json/json_spirit_value.cpp new file mode 100644 index 0000000000..2d7236855d --- /dev/null +++ b/src/json/json_spirit_value.cpp @@ -0,0 +1,8 @@ +/* Copyright (c) 2007 John W Wilkinson + + This source code can be used for any purpose as long as + this comment is retained. */ + +// json spirit version 2.00 + +#include "json/json_spirit_value.h" diff --git a/src/json/json_spirit_value.h b/src/json/json_spirit_value.h new file mode 100644 index 0000000000..7e83a2a7e3 --- /dev/null +++ b/src/json/json_spirit_value.h @@ -0,0 +1,534 @@ +#ifndef JSON_SPIRIT_VALUE +#define JSON_SPIRIT_VALUE + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace json_spirit +{ + enum Value_type{ obj_type, array_type, str_type, bool_type, int_type, real_type, null_type }; + static const char* Value_type_name[]={"obj", "array", "str", "bool", "int", "real", "null"}; + + template< class Config > // Config determines whether the value uses std::string or std::wstring and + // whether JSON Objects are represented as vectors or maps + class Value_impl + { + public: + + typedef Config Config_type; + typedef typename Config::String_type String_type; + typedef typename Config::Object_type Object; + typedef typename Config::Array_type Array; + typedef typename String_type::const_pointer Const_str_ptr; // eg const char* + + Value_impl(); // creates null value + Value_impl( Const_str_ptr value ); + Value_impl( const String_type& value ); + Value_impl( const Object& value ); + Value_impl( const Array& value ); + Value_impl( bool value ); + Value_impl( int value ); + Value_impl( boost::int64_t value ); + Value_impl( boost::uint64_t value ); + Value_impl( double value ); + + Value_impl( const Value_impl& other ); + + bool operator==( const Value_impl& lhs ) const; + + Value_impl& operator=( const Value_impl& lhs ); + + Value_type type() const; + + bool is_uint64() const; + bool is_null() const; + + const String_type& get_str() const; + const Object& get_obj() const; + const Array& get_array() const; + bool get_bool() const; + int get_int() const; + boost::int64_t get_int64() const; + boost::uint64_t get_uint64() const; + double get_real() const; + + Object& get_obj(); + Array& get_array(); + + template< typename T > T get_value() const; // example usage: int i = value.get_value< int >(); + // or double d = value.get_value< double >(); + + static const Value_impl null; + + private: + + void check_type( const Value_type vtype ) const; + + typedef boost::variant< String_type, + boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >, + bool, boost::int64_t, double > Variant; + + Value_type type_; + Variant v_; + bool is_uint64_; + }; + + // vector objects + + template< class Config > + struct Pair_impl + { + typedef typename Config::String_type String_type; + typedef typename Config::Value_type Value_type; + + Pair_impl( const String_type& name, const Value_type& value ); + + bool operator==( const Pair_impl& lhs ) const; + + String_type name_; + Value_type value_; + }; + + template< class String > + struct Config_vector + { + typedef String String_type; + typedef Value_impl< Config_vector > Value_type; + typedef Pair_impl < Config_vector > Pair_type; + typedef std::vector< Value_type > Array_type; + typedef std::vector< Pair_type > Object_type; + + static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) + { + obj.push_back( Pair_type( name , value ) ); + + return obj.back().value_; + } + + static String_type get_name( const Pair_type& pair ) + { + return pair.name_; + } + + static Value_type get_value( const Pair_type& pair ) + { + return pair.value_; + } + }; + + // typedefs for ASCII + + typedef Config_vector< std::string > Config; + + typedef Config::Value_type Value; + typedef Config::Pair_type Pair; + typedef Config::Object_type Object; + typedef Config::Array_type Array; + + // typedefs for Unicode + +#ifndef BOOST_NO_STD_WSTRING + + typedef Config_vector< std::wstring > wConfig; + + typedef wConfig::Value_type wValue; + typedef wConfig::Pair_type wPair; + typedef wConfig::Object_type wObject; + typedef wConfig::Array_type wArray; +#endif + + // map objects + + template< class String > + struct Config_map + { + typedef String String_type; + typedef Value_impl< Config_map > Value_type; + typedef std::vector< Value_type > Array_type; + typedef std::map< String_type, Value_type > Object_type; + typedef typename Object_type::value_type Pair_type; + + static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) + { + return obj[ name ] = value; + } + + static String_type get_name( const Pair_type& pair ) + { + return pair.first; + } + + static Value_type get_value( const Pair_type& pair ) + { + return pair.second; + } + }; + + // typedefs for ASCII + + typedef Config_map< std::string > mConfig; + + typedef mConfig::Value_type mValue; + typedef mConfig::Object_type mObject; + typedef mConfig::Array_type mArray; + + // typedefs for Unicode + +#ifndef BOOST_NO_STD_WSTRING + + typedef Config_map< std::wstring > wmConfig; + + typedef wmConfig::Value_type wmValue; + typedef wmConfig::Object_type wmObject; + typedef wmConfig::Array_type wmArray; + +#endif + + /////////////////////////////////////////////////////////////////////////////////////////////// + // + // implementation + + template< class Config > + const Value_impl< Config > Value_impl< Config >::null; + + template< class Config > + Value_impl< Config >::Value_impl() + : type_( null_type ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Const_str_ptr value ) + : type_( str_type ) + , v_( String_type( value ) ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const String_type& value ) + : type_( str_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Object& value ) + : type_( obj_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Array& value ) + : type_( array_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( bool value ) + : type_( bool_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( int value ) + : type_( int_type ) + , v_( static_cast< boost::int64_t >( value ) ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( boost::int64_t value ) + : type_( int_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( boost::uint64_t value ) + : type_( int_type ) + , v_( static_cast< boost::int64_t >( value ) ) + , is_uint64_( true ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( double value ) + : type_( real_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( const Value_impl< Config >& other ) + : type_( other.type() ) + , v_( other.v_ ) + , is_uint64_( other.is_uint64_ ) + { + } + + template< class Config > + Value_impl< Config >& Value_impl< Config >::operator=( const Value_impl& lhs ) + { + Value_impl tmp( lhs ); + + std::swap( type_, tmp.type_ ); + std::swap( v_, tmp.v_ ); + std::swap( is_uint64_, tmp.is_uint64_ ); + + return *this; + } + + template< class Config > + bool Value_impl< Config >::operator==( const Value_impl& lhs ) const + { + if( this == &lhs ) return true; + + if( type() != lhs.type() ) return false; + + return v_ == lhs.v_; + } + + template< class Config > + Value_type Value_impl< Config >::type() const + { + return type_; + } + + template< class Config > + bool Value_impl< Config >::is_uint64() const + { + return is_uint64_; + } + + template< class Config > + bool Value_impl< Config >::is_null() const + { + return type() == null_type; + } + + template< class Config > + void Value_impl< Config >::check_type( const Value_type vtype ) const + { + if( type() != vtype ) + { + std::ostringstream os; + + ///// Bitcoin: Tell the types by name instead of by number + os << "value is type " << Value_type_name[type()] << ", expected " << Value_type_name[vtype]; + + throw std::runtime_error( os.str() ); + } + } + + template< class Config > + const typename Config::String_type& Value_impl< Config >::get_str() const + { + check_type( str_type ); + + return *boost::get< String_type >( &v_ ); + } + + template< class Config > + const typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() const + { + check_type( obj_type ); + + return *boost::get< Object >( &v_ ); + } + + template< class Config > + const typename Value_impl< Config >::Array& Value_impl< Config >::get_array() const + { + check_type( array_type ); + + return *boost::get< Array >( &v_ ); + } + + template< class Config > + bool Value_impl< Config >::get_bool() const + { + check_type( bool_type ); + + return boost::get< bool >( v_ ); + } + + template< class Config > + int Value_impl< Config >::get_int() const + { + check_type( int_type ); + + return static_cast< int >( get_int64() ); + } + + template< class Config > + boost::int64_t Value_impl< Config >::get_int64() const + { + check_type( int_type ); + + return boost::get< boost::int64_t >( v_ ); + } + + template< class Config > + boost::uint64_t Value_impl< Config >::get_uint64() const + { + check_type( int_type ); + + return static_cast< boost::uint64_t >( get_int64() ); + } + + template< class Config > + double Value_impl< Config >::get_real() const + { + if( type() == int_type ) + { + return is_uint64() ? static_cast< double >( get_uint64() ) + : static_cast< double >( get_int64() ); + } + + check_type( real_type ); + + return boost::get< double >( v_ ); + } + + template< class Config > + typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() + { + check_type( obj_type ); + + return *boost::get< Object >( &v_ ); + } + + template< class Config > + typename Value_impl< Config >::Array& Value_impl< Config >::get_array() + { + check_type( array_type ); + + return *boost::get< Array >( &v_ ); + } + + template< class Config > + Pair_impl< Config >::Pair_impl( const String_type& name, const Value_type& value ) + : name_( name ) + , value_( value ) + { + } + + template< class Config > + bool Pair_impl< Config >::operator==( const Pair_impl< Config >& lhs ) const + { + if( this == &lhs ) return true; + + return ( name_ == lhs.name_ ) && ( value_ == lhs.value_ ); + } + + // converts a C string, ie. 8 bit char array, to a string object + // + template < class String_type > + String_type to_str( const char* c_str ) + { + String_type result; + + for( const char* p = c_str; *p != 0; ++p ) + { + result += *p; + } + + return result; + } + + // + + namespace internal_ + { + template< typename T > + struct Type_to_type + { + }; + + template< class Value > + int get_value( const Value& value, Type_to_type< int > ) + { + return value.get_int(); + } + + template< class Value > + boost::int64_t get_value( const Value& value, Type_to_type< boost::int64_t > ) + { + return value.get_int64(); + } + + template< class Value > + boost::uint64_t get_value( const Value& value, Type_to_type< boost::uint64_t > ) + { + return value.get_uint64(); + } + + template< class Value > + double get_value( const Value& value, Type_to_type< double > ) + { + return value.get_real(); + } + + template< class Value > + typename Value::String_type get_value( const Value& value, Type_to_type< typename Value::String_type > ) + { + return value.get_str(); + } + + template< class Value > + typename Value::Array get_value( const Value& value, Type_to_type< typename Value::Array > ) + { + return value.get_array(); + } + + template< class Value > + typename Value::Object get_value( const Value& value, Type_to_type< typename Value::Object > ) + { + return value.get_obj(); + } + + template< class Value > + bool get_value( const Value& value, Type_to_type< bool > ) + { + return value.get_bool(); + } + } + + template< class Config > + template< typename T > + T Value_impl< Config >::get_value() const + { + return internal_::get_value( *this, internal_::Type_to_type< T >() ); + } +} + +#endif diff --git a/src/json/json_spirit_writer.cpp b/src/json/json_spirit_writer.cpp new file mode 100644 index 0000000000..32dfeb2c6c --- /dev/null +++ b/src/json/json_spirit_writer.cpp @@ -0,0 +1,95 @@ +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json/json_spirit_writer.h" +#include "json/json_spirit_writer_template.h" + +void json_spirit::write( const Value& value, std::ostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const Value& value, std::ostream& os ) +{ + write_stream( value, os, true ); +} + +std::string json_spirit::write( const Value& value ) +{ + return write_string( value, false ); +} + +std::string json_spirit::write_formatted( const Value& value ) +{ + return write_string( value, true ); +} + +#ifndef BOOST_NO_STD_WSTRING + +void json_spirit::write( const wValue& value, std::wostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const wValue& value, std::wostream& os ) +{ + write_stream( value, os, true ); +} + +std::wstring json_spirit::write( const wValue& value ) +{ + return write_string( value, false ); +} + +std::wstring json_spirit::write_formatted( const wValue& value ) +{ + return write_string( value, true ); +} + +#endif + +void json_spirit::write( const mValue& value, std::ostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const mValue& value, std::ostream& os ) +{ + write_stream( value, os, true ); +} + +std::string json_spirit::write( const mValue& value ) +{ + return write_string( value, false ); +} + +std::string json_spirit::write_formatted( const mValue& value ) +{ + return write_string( value, true ); +} + +#ifndef BOOST_NO_STD_WSTRING + +void json_spirit::write( const wmValue& value, std::wostream& os ) +{ + write_stream( value, os, false ); +} + +void json_spirit::write_formatted( const wmValue& value, std::wostream& os ) +{ + write_stream( value, os, true ); +} + +std::wstring json_spirit::write( const wmValue& value ) +{ + return write_string( value, false ); +} + +std::wstring json_spirit::write_formatted( const wmValue& value ) +{ + return write_string( value, true ); +} + +#endif diff --git a/src/json/json_spirit_writer.h b/src/json/json_spirit_writer.h new file mode 100644 index 0000000000..52e14068e7 --- /dev/null +++ b/src/json/json_spirit_writer.h @@ -0,0 +1,50 @@ +#ifndef JSON_SPIRIT_WRITER +#define JSON_SPIRIT_WRITER + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include "json_spirit_value.h" +#include + +namespace json_spirit +{ + // functions to convert JSON Values to text, + // the "formatted" versions add whitespace to format the output nicely + + void write ( const Value& value, std::ostream& os ); + void write_formatted( const Value& value, std::ostream& os ); + std::string write ( const Value& value ); + std::string write_formatted( const Value& value ); + +#ifndef BOOST_NO_STD_WSTRING + + void write ( const wValue& value, std::wostream& os ); + void write_formatted( const wValue& value, std::wostream& os ); + std::wstring write ( const wValue& value ); + std::wstring write_formatted( const wValue& value ); + +#endif + + void write ( const mValue& value, std::ostream& os ); + void write_formatted( const mValue& value, std::ostream& os ); + std::string write ( const mValue& value ); + std::string write_formatted( const mValue& value ); + +#ifndef BOOST_NO_STD_WSTRING + + void write ( const wmValue& value, std::wostream& os ); + void write_formatted( const wmValue& value, std::wostream& os ); + std::wstring write ( const wmValue& value ); + std::wstring write_formatted( const wmValue& value ); + +#endif +} + +#endif diff --git a/src/json/json_spirit_writer_template.h b/src/json/json_spirit_writer_template.h new file mode 100644 index 0000000000..28c49ddc64 --- /dev/null +++ b/src/json/json_spirit_writer_template.h @@ -0,0 +1,248 @@ +#ifndef JSON_SPIRIT_WRITER_TEMPLATE +#define JSON_SPIRIT_WRITER_TEMPLATE + +// Copyright John W. Wilkinson 2007 - 2009. +// Distributed under the MIT License, see accompanying file LICENSE.txt + +// json spirit version 4.03 + +#include "json_spirit_value.h" + +#include +#include +#include + +namespace json_spirit +{ + inline char to_hex_char( unsigned int c ) + { + assert( c <= 0xF ); + + const char ch = static_cast< char >( c ); + + if( ch < 10 ) return '0' + ch; + + return 'A' - 10 + ch; + } + + template< class String_type > + String_type non_printable_to_string( unsigned int c ) + { + typedef typename String_type::value_type Char_type; + + String_type result( 6, '\\' ); + + result[1] = 'u'; + + result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 2 ] = to_hex_char( c & 0x000F ); + + return result; + } + + template< typename Char_type, class String_type > + bool add_esc_char( Char_type c, String_type& s ) + { + switch( c ) + { + case '"': s += to_str< String_type >( "\\\"" ); return true; + case '\\': s += to_str< String_type >( "\\\\" ); return true; + case '\b': s += to_str< String_type >( "\\b" ); return true; + case '\f': s += to_str< String_type >( "\\f" ); return true; + case '\n': s += to_str< String_type >( "\\n" ); return true; + case '\r': s += to_str< String_type >( "\\r" ); return true; + case '\t': s += to_str< String_type >( "\\t" ); return true; + } + + return false; + } + + template< class String_type > + String_type add_esc_chars( const String_type& s ) + { + typedef typename String_type::const_iterator Iter_type; + typedef typename String_type::value_type Char_type; + + String_type result; + + const Iter_type end( s.end() ); + + for( Iter_type i = s.begin(); i != end; ++i ) + { + const Char_type c( *i ); + + if( add_esc_char( c, result ) ) continue; + + const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c ); + + if( iswprint( unsigned_c ) ) + { + result += c; + } + else + { + result += non_printable_to_string< String_type >( unsigned_c ); + } + } + + return result; + } + + // this class generates the JSON text, + // it keeps track of the indentation level etc. + // + template< class Value_type, class Ostream_type > + class Generator + { + typedef typename Value_type::Config_type Config_type; + typedef typename Config_type::String_type String_type; + typedef typename Config_type::Object_type Object_type; + typedef typename Config_type::Array_type Array_type; + typedef typename String_type::value_type Char_type; + typedef typename Object_type::value_type Obj_member_type; + + public: + + Generator( const Value_type& value, Ostream_type& os, bool pretty ) + : os_( os ) + , indentation_level_( 0 ) + , pretty_( pretty ) + { + output( value ); + } + + private: + + void output( const Value_type& value ) + { + switch( value.type() ) + { + case obj_type: output( value.get_obj() ); break; + case array_type: output( value.get_array() ); break; + case str_type: output( value.get_str() ); break; + case bool_type: output( value.get_bool() ); break; + case int_type: output_int( value ); break; + + /// Bitcoin: Added std::fixed and changed precision from 16 to 8 + case real_type: os_ << std::showpoint << std::fixed << std::setprecision(8) + << value.get_real(); break; + + case null_type: os_ << "null"; break; + default: assert( false ); + } + } + + void output( const Object_type& obj ) + { + output_array_or_obj( obj, '{', '}' ); + } + + void output( const Array_type& arr ) + { + output_array_or_obj( arr, '[', ']' ); + } + + void output( const Obj_member_type& member ) + { + output( Config_type::get_name( member ) ); space(); + os_ << ':'; space(); + output( Config_type::get_value( member ) ); + } + + void output_int( const Value_type& value ) + { + if( value.is_uint64() ) + { + os_ << value.get_uint64(); + } + else + { + os_ << value.get_int64(); + } + } + + void output( const String_type& s ) + { + os_ << '"' << add_esc_chars( s ) << '"'; + } + + void output( bool b ) + { + os_ << to_str< String_type >( b ? "true" : "false" ); + } + + template< class T > + void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char ) + { + os_ << start_char; new_line(); + + ++indentation_level_; + + for( typename T::const_iterator i = t.begin(); i != t.end(); ++i ) + { + indent(); output( *i ); + + typename T::const_iterator next = i; + + if( ++next != t.end()) + { + os_ << ','; + } + + new_line(); + } + + --indentation_level_; + + indent(); os_ << end_char; + } + + void indent() + { + if( !pretty_ ) return; + + for( int i = 0; i < indentation_level_; ++i ) + { + os_ << " "; + } + } + + void space() + { + if( pretty_ ) os_ << ' '; + } + + void new_line() + { + if( pretty_ ) os_ << '\n'; + } + + Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning + + Ostream_type& os_; + int indentation_level_; + bool pretty_; + }; + + template< class Value_type, class Ostream_type > + void write_stream( const Value_type& value, Ostream_type& os, bool pretty ) + { + Generator< Value_type, Ostream_type >( value, os, pretty ); + } + + template< class Value_type > + typename Value_type::String_type write_string( const Value_type& value, bool pretty ) + { + typedef typename Value_type::String_type::value_type Char_type; + + std::basic_ostringstream< Char_type > os; + + write_stream( value, os, pretty ); + + return os.str(); + } +} + +#endif diff --git a/src/key.h b/src/key.h new file mode 100644 index 0000000000..c973d6eb82 --- /dev/null +++ b/src/key.h @@ -0,0 +1,175 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_KEY_H +#define BITCOIN_KEY_H + +#include +#include +#include + +// secp160k1 +// const unsigned int PRIVATE_KEY_SIZE = 192; +// const unsigned int PUBLIC_KEY_SIZE = 41; +// const unsigned int SIGNATURE_SIZE = 48; +// +// secp192k1 +// const unsigned int PRIVATE_KEY_SIZE = 222; +// const unsigned int PUBLIC_KEY_SIZE = 49; +// const unsigned int SIGNATURE_SIZE = 57; +// +// secp224k1 +// const unsigned int PRIVATE_KEY_SIZE = 250; +// const unsigned int PUBLIC_KEY_SIZE = 57; +// const unsigned int SIGNATURE_SIZE = 66; +// +// secp256k1: +// const unsigned int PRIVATE_KEY_SIZE = 279; +// const unsigned int PUBLIC_KEY_SIZE = 65; +// const unsigned int SIGNATURE_SIZE = 72; +// +// see www.keylength.com +// script supports up to 75 for single byte push + + + +class key_error : public std::runtime_error +{ +public: + explicit key_error(const std::string& str) : std::runtime_error(str) {} +}; + + +// secure_allocator is defined in serialize.h +typedef std::vector > CPrivKey; + + + +class CKey +{ +protected: + EC_KEY* pkey; + bool fSet; + +public: + CKey() + { + pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + if (pkey == NULL) + throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed"); + fSet = false; + } + + CKey(const CKey& b) + { + pkey = EC_KEY_dup(b.pkey); + if (pkey == NULL) + throw key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed"); + fSet = b.fSet; + } + + CKey& operator=(const CKey& b) + { + if (!EC_KEY_copy(pkey, b.pkey)) + throw key_error("CKey::operator=(const CKey&) : EC_KEY_copy failed"); + fSet = b.fSet; + return (*this); + } + + ~CKey() + { + EC_KEY_free(pkey); + } + + bool IsNull() const + { + return !fSet; + } + + void MakeNewKey() + { + if (!EC_KEY_generate_key(pkey)) + throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed"); + fSet = true; + } + + bool SetPrivKey(const CPrivKey& vchPrivKey) + { + const unsigned char* pbegin = &vchPrivKey[0]; + if (!d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size())) + return false; + fSet = true; + return true; + } + + CPrivKey GetPrivKey() const + { + unsigned int nSize = i2d_ECPrivateKey(pkey, NULL); + if (!nSize) + throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed"); + CPrivKey vchPrivKey(nSize, 0); + unsigned char* pbegin = &vchPrivKey[0]; + if (i2d_ECPrivateKey(pkey, &pbegin) != nSize) + throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size"); + return vchPrivKey; + } + + bool SetPubKey(const std::vector& vchPubKey) + { + const unsigned char* pbegin = &vchPubKey[0]; + if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size())) + return false; + fSet = true; + return true; + } + + std::vector GetPubKey() const + { + unsigned int nSize = i2o_ECPublicKey(pkey, NULL); + if (!nSize) + throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed"); + std::vector vchPubKey(nSize, 0); + unsigned char* pbegin = &vchPubKey[0]; + if (i2o_ECPublicKey(pkey, &pbegin) != nSize) + throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size"); + return vchPubKey; + } + + bool Sign(uint256 hash, std::vector& vchSig) + { + vchSig.clear(); + unsigned char pchSig[10000]; + unsigned int nSize = 0; + if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), pchSig, &nSize, pkey)) + return false; + vchSig.resize(nSize); + memcpy(&vchSig[0], pchSig, nSize); + return true; + } + + bool Verify(uint256 hash, const std::vector& vchSig) + { + // -1 = error, 0 = bad sig, 1 = good + if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) + return false; + return true; + } + + static bool Sign(const CPrivKey& vchPrivKey, uint256 hash, std::vector& vchSig) + { + CKey key; + if (!key.SetPrivKey(vchPrivKey)) + return false; + return key.Sign(hash, vchSig); + } + + static bool Verify(const std::vector& vchPubKey, uint256 hash, const std::vector& vchSig) + { + CKey key; + if (!key.SetPubKey(vchPubKey)) + return false; + return key.Verify(hash, vchSig); + } +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000000..0456041ee5 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,4033 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#include "headers.h" +#include "cryptopp/sha.h" + +using namespace std; +using namespace boost; + +// +// Global state +// + +CCriticalSection cs_main; + +map mapTransactions; +CCriticalSection cs_mapTransactions; +unsigned int nTransactionsUpdated = 0; +map mapNextTx; + +map mapBlockIndex; +uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); +CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); +CBlockIndex* pindexGenesisBlock = NULL; +int nBestHeight = -1; +CBigNum bnBestChainWork = 0; +CBigNum bnBestInvalidWork = 0; +uint256 hashBestChain = 0; +CBlockIndex* pindexBest = NULL; +int64 nTimeBestReceived = 0; + +map mapOrphanBlocks; +multimap mapOrphanBlocksByPrev; + +map mapOrphanTransactions; +multimap mapOrphanTransactionsByPrev; + +map mapWallet; +vector vWalletUpdated; +CCriticalSection cs_mapWallet; + +map, CPrivKey> mapKeys; +map > mapPubKeys; +CCriticalSection cs_mapKeys; +CKey keyUser; + +map mapRequestCount; +CCriticalSection cs_mapRequestCount; + +map mapAddressBook; +CCriticalSection cs_mapAddressBook; + +vector vchDefaultKey; + +double dHashesPerSec; +int64 nHPSTimerStart; + +// Settings +int fGenerateBitcoins = false; +int64 nTransactionFee = 0; +CAddress addrIncoming; +int fLimitProcessors = false; +int nLimitProcessors = 1; +int fMinimizeToTray = true; +int fMinimizeOnClose = true; +#if USE_UPNP +int fUseUPnP = true; +#else +int fUseUPnP = false; +#endif + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapKeys +// + +bool AddKey(const CKey& key) +{ + CRITICAL_BLOCK(cs_mapKeys) + { + mapKeys[key.GetPubKey()] = key.GetPrivKey(); + mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey(); + } + return CWalletDB().WriteKey(key.GetPubKey(), key.GetPrivKey()); +} + +vector GenerateNewKey() +{ + RandAddSeedPerfmon(); + CKey key; + key.MakeNewKey(); + if (!AddKey(key)) + throw runtime_error("GenerateNewKey() : AddKey failed"); + return key.GetPubKey(); +} + + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapWallet +// + +bool AddToWallet(const CWalletTx& wtxIn) +{ + uint256 hash = wtxIn.GetHash(); + CRITICAL_BLOCK(cs_mapWallet) + { + // Inserts only if not already there, returns tx inserted or tx found + pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); + CWalletTx& wtx = (*ret.first).second; + bool fInsertedNew = ret.second; + if (fInsertedNew) + wtx.nTimeReceived = GetAdjustedTime(); + + bool fUpdated = false; + if (!fInsertedNew) + { + // Merge + if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) + { + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) + { + wtx.vMerkleBranch = wtxIn.vMerkleBranch; + wtx.nIndex = wtxIn.nIndex; + fUpdated = true; + } + if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) + { + wtx.fFromMe = wtxIn.fFromMe; + fUpdated = true; + } + fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent); + } + + //// debug print + printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + + // Write to disk + if (fInsertedNew || fUpdated) + if (!wtx.WriteToDisk()) + return false; + + // If default receiving address gets used, replace it with a new one + CScript scriptDefaultKey; + scriptDefaultKey.SetBitcoinAddress(vchDefaultKey); + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (txout.scriptPubKey == scriptDefaultKey) + { + CWalletDB walletdb; + vchDefaultKey = GetKeyFromKeyPool(); + walletdb.WriteDefaultKey(vchDefaultKey); + walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); + } + } + + // Notify UI + vWalletUpdated.push_back(hash); + } + + // Refresh UI + MainFrameRepaint(); + return true; +} + +bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false) +{ + uint256 hash = tx.GetHash(); + bool fExisted = mapWallet.count(hash); + if (fExisted && !fUpdate) return false; + if (fExisted || tx.IsMine() || tx.IsFromMe()) + { + CWalletTx wtx(tx); + // Get merkle branch if transaction was found in a block + if (pblock) + wtx.SetMerkleBranch(pblock); + return AddToWallet(wtx); + } + return false; +} + +bool EraseFromWallet(uint256 hash) +{ + CRITICAL_BLOCK(cs_mapWallet) + { + if (mapWallet.erase(hash)) + CWalletDB().EraseTx(hash); + } + return true; +} + +void WalletUpdateSpent(const COutPoint& prevout) +{ + // Anytime a signature is successfully verified, it's proof the outpoint is spent. + // Update the wallet spent flag if it doesn't know due to wallet.dat being + // restored from backup or the user making copies of wallet.dat. + CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + CWalletTx& wtx = (*mi).second; + if (!wtx.IsSpent(prevout.n) && wtx.vout[prevout.n].IsMine()) + { + printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + wtx.MarkSpent(prevout.n); + wtx.WriteToDisk(); + vWalletUpdated.push_back(prevout.hash); + } + } + } +} + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapOrphanTransactions +// + +void AddOrphanTx(const CDataStream& vMsg) +{ + CTransaction tx; + CDataStream(vMsg) >> tx; + uint256 hash = tx.GetHash(); + if (mapOrphanTransactions.count(hash)) + return; + CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg); + BOOST_FOREACH(const CTxIn& txin, tx.vin) + mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg)); +} + +void EraseOrphanTx(uint256 hash) +{ + if (!mapOrphanTransactions.count(hash)) + return; + const CDataStream* pvMsg = mapOrphanTransactions[hash]; + CTransaction tx; + CDataStream(*pvMsg) >> tx; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + for (multimap::iterator mi = mapOrphanTransactionsByPrev.lower_bound(txin.prevout.hash); + mi != mapOrphanTransactionsByPrev.upper_bound(txin.prevout.hash);) + { + if ((*mi).second == pvMsg) + mapOrphanTransactionsByPrev.erase(mi++); + else + mi++; + } + } + delete pvMsg; + mapOrphanTransactions.erase(hash); +} + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CTransaction and CTxIndex +// + +bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet) +{ + SetNull(); + if (!txdb.ReadTxIndex(prevout.hash, txindexRet)) + return false; + if (!ReadFromDisk(txindexRet.pos)) + return false; + if (prevout.n >= vout.size()) + { + SetNull(); + return false; + } + return true; +} + +bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout) +{ + CTxIndex txindex; + return ReadFromDisk(txdb, prevout, txindex); +} + +bool CTransaction::ReadFromDisk(COutPoint prevout) +{ + CTxDB txdb("r"); + CTxIndex txindex; + return ReadFromDisk(txdb, prevout, txindex); +} + +bool CTxIn::IsMine() const +{ + CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (prevout.n < prev.vout.size()) + if (prev.vout[prevout.n].IsMine()) + return true; + } + } + return false; +} + +int64 CTxIn::GetDebit() const +{ + CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (prevout.n < prev.vout.size()) + if (prev.vout[prevout.n].IsMine()) + return prev.vout[prevout.n].nValue; + } + } + return 0; +} + +int64 CWalletTx::GetTxTime() const +{ + if (!fTimeReceivedIsTxTime && hashBlock != 0) + { + // If we did not receive the transaction directly, we rely on the block's + // time to figure out when it happened. We use the median over a range + // of blocks to try to filter out inaccurate block times. + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex) + return pindex->GetMedianTime(); + } + } + return nTimeReceived; +} + +int CWalletTx::GetRequestCount() const +{ + // Returns -1 if it wasn't being tracked + int nRequests = -1; + CRITICAL_BLOCK(cs_mapRequestCount) + { + if (IsCoinBase()) + { + // Generated block + if (hashBlock != 0) + { + map::iterator mi = mapRequestCount.find(hashBlock); + if (mi != mapRequestCount.end()) + nRequests = (*mi).second; + } + } + else + { + // Did anyone request this transaction? + map::iterator mi = mapRequestCount.find(GetHash()); + if (mi != mapRequestCount.end()) + { + nRequests = (*mi).second; + + // How about the block it's in? + if (nRequests == 0 && hashBlock != 0) + { + map::iterator mi = mapRequestCount.find(hashBlock); + if (mi != mapRequestCount.end()) + nRequests = (*mi).second; + else + nRequests = 1; // If it's in someone else's block it must have got out + } + } + } + } + return nRequests; +} + +void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list >& listReceived, + list >& listSent, int64& nFee, string& strSentAccount) const +{ + nGeneratedImmature = nGeneratedMature = nFee = 0; + listReceived.clear(); + listSent.clear(); + strSentAccount = strFromAccount; + + if (IsCoinBase()) + { + if (GetBlocksToMaturity() > 0) + nGeneratedImmature = CTransaction::GetCredit(); + else + nGeneratedMature = GetCredit(); + return; + } + + // Compute fee: + int64 nDebit = GetDebit(); + if (nDebit > 0) // debit>0 means we signed/sent this transaction + { + int64 nValueOut = GetValueOut(); + nFee = nDebit - nValueOut; + } + + // Sent/received. Standard client will never generate a send-to-multiple-recipients, + // but non-standard clients might (so return a list of address/amount pairs) + BOOST_FOREACH(const CTxOut& txout, vout) + { + string address; + uint160 hash160; + vector vchPubKey; + if (ExtractHash160(txout.scriptPubKey, hash160)) + address = Hash160ToAddress(hash160); + else if (ExtractPubKey(txout.scriptPubKey, false, vchPubKey)) + address = PubKeyToAddress(vchPubKey); + else + { + printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", + this->GetHash().ToString().c_str()); + address = " unknown "; + } + + // Don't report 'change' txouts + if (nDebit > 0 && txout.IsChange()) + continue; + + if (nDebit > 0) + listSent.push_back(make_pair(address, txout.nValue)); + + if (txout.IsMine()) + listReceived.push_back(make_pair(address, txout.nValue)); + } + +} + +void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, + int64& nSent, int64& nFee) const +{ + nGenerated = nReceived = nSent = nFee = 0; + + int64 allGeneratedImmature, allGeneratedMature, allFee; + allGeneratedImmature = allGeneratedMature = allFee = 0; + string strSentAccount; + list > listReceived; + list > listSent; + GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); + + if (strAccount == "") + nGenerated = allGeneratedMature; + if (strAccount == strSentAccount) + { + BOOST_FOREACH(const PAIRTYPE(string,int64)& s, listSent) + nSent += s.second; + nFee = allFee; + } + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) + { + if (mapAddressBook.count(r.first)) + { + if (mapAddressBook[r.first] == strAccount) + { + nReceived += r.second; + } + } + else if (strAccount.empty()) + { + nReceived += r.second; + } + } + } +} + + + +int CMerkleTx::SetMerkleBranch(const CBlock* pblock) +{ + if (fClient) + { + if (hashBlock == 0) + return 0; + } + else + { + CBlock blockTmp; + if (pblock == NULL) + { + // Load the block this tx is in + CTxIndex txindex; + if (!CTxDB("r").ReadTxIndex(GetHash(), txindex)) + return 0; + if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos)) + return 0; + pblock = &blockTmp; + } + + // Update the tx's hashBlock + hashBlock = pblock->GetHash(); + + // Locate the transaction + for (nIndex = 0; nIndex < pblock->vtx.size(); nIndex++) + if (pblock->vtx[nIndex] == *(CTransaction*)this) + break; + if (nIndex == pblock->vtx.size()) + { + vMerkleBranch.clear(); + nIndex = -1; + printf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); + return 0; + } + + // Fill in merkle branch + vMerkleBranch = pblock->GetMerkleBranch(nIndex); + } + + // Is the tx in a block that's in the main chain + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !pindex->IsInMainChain()) + return 0; + + return pindexBest->nHeight - pindex->nHeight + 1; +} + + + +void CWalletTx::AddSupportingTransactions(CTxDB& txdb) +{ + vtxPrev.clear(); + + const int COPY_DEPTH = 3; + if (SetMerkleBranch() < COPY_DEPTH) + { + vector vWorkQueue; + BOOST_FOREACH(const CTxIn& txin, vin) + vWorkQueue.push_back(txin.prevout.hash); + + // This critsect is OK because txdb is already open + CRITICAL_BLOCK(cs_mapWallet) + { + map mapWalletPrev; + set setAlreadyDone; + for (int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hash = vWorkQueue[i]; + if (setAlreadyDone.count(hash)) + continue; + setAlreadyDone.insert(hash); + + CMerkleTx tx; + if (mapWallet.count(hash)) + { + tx = mapWallet[hash]; + BOOST_FOREACH(const CMerkleTx& txWalletPrev, mapWallet[hash].vtxPrev) + mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; + } + else if (mapWalletPrev.count(hash)) + { + tx = *mapWalletPrev[hash]; + } + else if (!fClient && txdb.ReadDiskTx(hash, tx)) + { + ; + } + else + { + printf("ERROR: AddSupportingTransactions() : unsupported transaction\n"); + continue; + } + + int nDepth = tx.SetMerkleBranch(); + vtxPrev.push_back(tx); + + if (nDepth < COPY_DEPTH) + BOOST_FOREACH(const CTxIn& txin, tx.vin) + vWorkQueue.push_back(txin.prevout.hash); + } + } + } + + reverse(vtxPrev.begin(), vtxPrev.end()); +} + + + + + + + + + + + +bool CTransaction::CheckTransaction() const +{ + // Basic checks that don't depend on any context + if (vin.empty() || vout.empty()) + return error("CTransaction::CheckTransaction() : vin or vout empty"); + + // Size limits + if (::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE) + return error("CTransaction::CheckTransaction() : size limits failed"); + + // Check for negative or overflow output values + int64 nValueOut = 0; + BOOST_FOREACH(const CTxOut& txout, vout) + { + if (txout.nValue < 0) + return error("CTransaction::CheckTransaction() : txout.nValue negative"); + if (txout.nValue > MAX_MONEY) + return error("CTransaction::CheckTransaction() : txout.nValue too high"); + nValueOut += txout.nValue; + if (!MoneyRange(nValueOut)) + return error("CTransaction::CheckTransaction() : txout total out of range"); + } + + if (IsCoinBase()) + { + if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) + return error("CTransaction::CheckTransaction() : coinbase script size"); + } + else + { + BOOST_FOREACH(const CTxIn& txin, vin) + if (txin.prevout.IsNull()) + return error("CTransaction::CheckTransaction() : prevout is null"); + } + + return true; +} + +bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs) +{ + if (pfMissingInputs) + *pfMissingInputs = false; + + if (!CheckTransaction()) + return error("AcceptToMemoryPool() : CheckTransaction failed"); + + // Coinbase is only valid in a block, not as a loose transaction + if (IsCoinBase()) + return error("AcceptToMemoryPool() : coinbase as individual tx"); + + // To help v0.1.5 clients who would see it as a negative number + if ((int64)nLockTime > INT_MAX) + return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet"); + + // Safety limits + unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK); + // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service + // attacks disallow transactions with more than one SigOp per 34 bytes. + // 34 bytes because a TxOut is: + // 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length + if (GetSigOpCount() > nSize / 34 || nSize < 100) + return error("AcceptToMemoryPool() : nonstandard transaction"); + + // Rather not work on nonstandard transactions (unless -testnet) + if (!fTestNet && !IsStandard()) + return error("AcceptToMemoryPool() : nonstandard transaction type"); + + // Do we already have it? + uint256 hash = GetHash(); + CRITICAL_BLOCK(cs_mapTransactions) + if (mapTransactions.count(hash)) + return false; + if (fCheckInputs) + if (txdb.ContainsTx(hash)) + return false; + + // Check for conflicts with in-memory transactions + CTransaction* ptxOld = NULL; + for (int i = 0; i < vin.size(); i++) + { + COutPoint outpoint = vin[i].prevout; + if (mapNextTx.count(outpoint)) + { + // Disable replacement feature for now + return false; + + // Allow replacing with a newer version of the same transaction + if (i != 0) + return false; + ptxOld = mapNextTx[outpoint].ptx; + if (ptxOld->IsFinal()) + return false; + if (!IsNewerThan(*ptxOld)) + return false; + for (int i = 0; i < vin.size(); i++) + { + COutPoint outpoint = vin[i].prevout; + if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld) + return false; + } + break; + } + } + + if (fCheckInputs) + { + // Check against previous transactions + map mapUnused; + int64 nFees = 0; + if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false)) + { + if (pfMissingInputs) + *pfMissingInputs = true; + return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); + } + + // Don't accept it if it can't get into a block + if (nFees < GetMinFee(1000, true, true)) + return error("AcceptToMemoryPool() : not enough fees"); + + // Continuously rate-limit free transactions + // This mitigates 'penny-flooding' -- sending thousands of free transactions just to + // be annoying or make other's transactions take longer to confirm. + if (nFees < MIN_RELAY_TX_FEE) + { + static CCriticalSection cs; + static double dFreeCount; + static int64 nLastTime; + int64 nNow = GetTime(); + + CRITICAL_BLOCK(cs) + { + // Use an exponentially decaying ~10-minute window: + dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); + nLastTime = nNow; + // -limitfreerelay unit is thousand-bytes-per-minute + // At default rate it would take over a month to fill 1GB + if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe()) + return error("AcceptToMemoryPool() : free transaction rejected by rate limiter"); + if (fDebug) + printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + dFreeCount += nSize; + } + } + } + + // Store transaction in memory + CRITICAL_BLOCK(cs_mapTransactions) + { + if (ptxOld) + { + printf("AcceptToMemoryPool() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); + ptxOld->RemoveFromMemoryPool(); + } + AddToMemoryPoolUnchecked(); + } + + ///// are we sure this is ok when loading transactions or restoring block txes + // If updated, erase old tx from wallet + if (ptxOld) + EraseFromWallet(ptxOld->GetHash()); + + printf("AcceptToMemoryPool(): accepted %s\n", hash.ToString().substr(0,10).c_str()); + return true; +} + + +bool CTransaction::AddToMemoryPoolUnchecked() +{ + // Add to memory pool without checking anything. Don't call this directly, + // call AcceptToMemoryPool to properly check the transaction first. + CRITICAL_BLOCK(cs_mapTransactions) + { + uint256 hash = GetHash(); + mapTransactions[hash] = *this; + for (int i = 0; i < vin.size(); i++) + mapNextTx[vin[i].prevout] = CInPoint(&mapTransactions[hash], i); + nTransactionsUpdated++; + } + return true; +} + + +bool CTransaction::RemoveFromMemoryPool() +{ + // Remove transaction from memory pool + CRITICAL_BLOCK(cs_mapTransactions) + { + BOOST_FOREACH(const CTxIn& txin, vin) + mapNextTx.erase(txin.prevout); + mapTransactions.erase(GetHash()); + nTransactionsUpdated++; + } + return true; +} + + + + + + +int CMerkleTx::GetDepthInMainChain(int& nHeightRet) const +{ + if (hashBlock == 0 || nIndex == -1) + return 0; + + // Find the block it claims to be in + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !pindex->IsInMainChain()) + return 0; + + // Make sure the merkle branch connects to this block + if (!fMerkleVerified) + { + if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) + return 0; + fMerkleVerified = true; + } + + nHeightRet = pindex->nHeight; + return pindexBest->nHeight - pindex->nHeight + 1; +} + + +int CMerkleTx::GetBlocksToMaturity() const +{ + if (!IsCoinBase()) + return 0; + return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain()); +} + + +bool CMerkleTx::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs) +{ + if (fClient) + { + if (!IsInMainChain() && !ClientConnectInputs()) + return false; + return CTransaction::AcceptToMemoryPool(txdb, false); + } + else + { + return CTransaction::AcceptToMemoryPool(txdb, fCheckInputs); + } +} + + + +bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) +{ + CRITICAL_BLOCK(cs_mapTransactions) + { + // Add previous supporting transactions first + BOOST_FOREACH(CMerkleTx& tx, vtxPrev) + { + if (!tx.IsCoinBase()) + { + uint256 hash = tx.GetHash(); + if (!mapTransactions.count(hash) && !txdb.ContainsTx(hash)) + tx.AcceptToMemoryPool(txdb, fCheckInputs); + } + } + return AcceptToMemoryPool(txdb, fCheckInputs); + } + return false; +} + +int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) +{ + int ret = 0; + + CBlockIndex* pindex = pindexStart; + CRITICAL_BLOCK(cs_mapWallet) + { + while (pindex) + { + CBlock block; + block.ReadFromDisk(pindex, true); + BOOST_FOREACH(CTransaction& tx, block.vtx) + { + if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) + ret++; + } + pindex = pindex->pnext; + } + } + return ret; +} + +void ReacceptWalletTransactions() +{ + CTxDB txdb("r"); + bool fRepeat = true; + while (fRepeat) CRITICAL_BLOCK(cs_mapWallet) + { + fRepeat = false; + vector vMissingTx; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + if (wtx.IsCoinBase() && wtx.IsSpent(0)) + continue; + + CTxIndex txindex; + bool fUpdated = false; + if (txdb.ReadTxIndex(wtx.GetHash(), txindex)) + { + // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat + if (txindex.vSpent.size() != wtx.vout.size()) + { + printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size()); + continue; + } + for (int i = 0; i < txindex.vSpent.size(); i++) + { + if (wtx.IsSpent(i)) + continue; + if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine()) + { + wtx.MarkSpent(i); + fUpdated = true; + vMissingTx.push_back(txindex.vSpent[i]); + } + } + if (fUpdated) + { + printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + wtx.MarkDirty(); + wtx.WriteToDisk(); + } + } + else + { + // Reaccept any txes of ours that aren't already in a block + if (!wtx.IsCoinBase()) + wtx.AcceptWalletTransaction(txdb, false); + } + } + if (!vMissingTx.empty()) + { + // TODO: optimize this to scan just part of the block chain? + if (ScanForWalletTransactions(pindexGenesisBlock)) + fRepeat = true; // Found missing transactions: re-do Reaccept. + } + } +} + + +void CWalletTx::RelayWalletTransaction(CTxDB& txdb) +{ + BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) + { + if (!tx.IsCoinBase()) + { + uint256 hash = tx.GetHash(); + if (!txdb.ContainsTx(hash)) + RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); + } + } + if (!IsCoinBase()) + { + uint256 hash = GetHash(); + if (!txdb.ContainsTx(hash)) + { + printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); + RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); + } + } +} + +void ResendWalletTransactions() +{ + // Do this infrequently and randomly to avoid giving away + // that these are our transactions. + static int64 nNextTime; + if (GetTime() < nNextTime) + return; + bool fFirst = (nNextTime == 0); + nNextTime = GetTime() + GetRand(30 * 60); + if (fFirst) + return; + + // Only do it if there's been a new block since last time + static int64 nLastTime; + if (nTimeBestReceived < nLastTime) + return; + nLastTime = GetTime(); + + // Rebroadcast any of our txes that aren't in a block yet + printf("ResendWalletTransactions()\n"); + CTxDB txdb("r"); + CRITICAL_BLOCK(cs_mapWallet) + { + // Sort them in chronological order + multimap mapSorted; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + // Don't rebroadcast until it's had plenty of time that + // it should have gotten in already by now. + if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) + mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); + } + BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) + { + CWalletTx& wtx = *item.second; + wtx.RelayWalletTransaction(txdb); + } + } +} + +int CTxIndex::GetDepthInMainChain() const +{ + // Read block header + CBlock block; + if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false)) + return 0; + // Find the block in the index + map::iterator mi = mapBlockIndex.find(block.GetHash()); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !pindex->IsInMainChain()) + return 0; + return 1 + nBestHeight - pindex->nHeight; +} + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CBlock and CBlockIndex +// + +bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions) +{ + if (!fReadTransactions) + { + *this = pindex->GetBlockHeader(); + return true; + } + if (!ReadFromDisk(pindex->nFile, pindex->nBlockPos, fReadTransactions)) + return false; + if (GetHash() != pindex->GetBlockHash()) + return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); + return true; +} + +uint256 GetOrphanRoot(const CBlock* pblock) +{ + // Work back to the first block in the orphan chain + while (mapOrphanBlocks.count(pblock->hashPrevBlock)) + pblock = mapOrphanBlocks[pblock->hashPrevBlock]; + return pblock->GetHash(); +} + +int64 GetBlockValue(int nHeight, int64 nFees) +{ + int64 nSubsidy = 50 * COIN; + + // Subsidy is cut in half every 4 years + nSubsidy >>= (nHeight / 210000); + + return nSubsidy + nFees; +} + +unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast) +{ + const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks + const int64 nTargetSpacing = 10 * 60; + const int64 nInterval = nTargetTimespan / nTargetSpacing; + + // Genesis block + if (pindexLast == NULL) + return bnProofOfWorkLimit.GetCompact(); + + // Only change once per interval + if ((pindexLast->nHeight+1) % nInterval != 0) + return pindexLast->nBits; + + // Go back by what we want to be 14 days worth of blocks + const CBlockIndex* pindexFirst = pindexLast; + for (int i = 0; pindexFirst && i < nInterval-1; i++) + pindexFirst = pindexFirst->pprev; + assert(pindexFirst); + + // Limit adjustment step + int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime(); + printf(" nActualTimespan = %"PRI64d" before bounds\n", nActualTimespan); + if (nActualTimespan < nTargetTimespan/4) + nActualTimespan = nTargetTimespan/4; + if (nActualTimespan > nTargetTimespan*4) + nActualTimespan = nTargetTimespan*4; + + // Retarget + CBigNum bnNew; + bnNew.SetCompact(pindexLast->nBits); + bnNew *= nActualTimespan; + bnNew /= nTargetTimespan; + + if (bnNew > bnProofOfWorkLimit) + bnNew = bnProofOfWorkLimit; + + /// debug print + printf("GetNextWorkRequired RETARGET\n"); + printf("nTargetTimespan = %"PRI64d" nActualTimespan = %"PRI64d"\n", nTargetTimespan, nActualTimespan); + printf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str()); + printf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str()); + + return bnNew.GetCompact(); +} + +bool CheckProofOfWork(uint256 hash, unsigned int nBits) +{ + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + + // Check range + if (bnTarget <= 0 || bnTarget > bnProofOfWorkLimit) + return error("CheckProofOfWork() : nBits below minimum work"); + + // Check proof of work matches claimed amount + if (hash > bnTarget.getuint256()) + return error("CheckProofOfWork() : hash doesn't match nBits"); + + return true; +} + +bool IsInitialBlockDownload() +{ + if (pindexBest == NULL || (!fTestNet && nBestHeight < 118000)) + return true; + static int64 nLastUpdate; + static CBlockIndex* pindexLastBest; + if (pindexBest != pindexLastBest) + { + pindexLastBest = pindexBest; + nLastUpdate = GetTime(); + } + return (GetTime() - nLastUpdate < 10 && + pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); +} + +void InvalidChainFound(CBlockIndex* pindexNew) +{ + if (pindexNew->bnChainWork > bnBestInvalidWork) + { + bnBestInvalidWork = pindexNew->bnChainWork; + CTxDB().WriteBestInvalidWork(bnBestInvalidWork); + MainFrameRepaint(); + } + printf("InvalidChainFound: invalid block=%s height=%d work=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, pindexNew->bnChainWork.ToString().c_str()); + printf("InvalidChainFound: current best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str()); + if (pindexBest && bnBestInvalidWork > bnBestChainWork + pindexBest->GetBlockWork() * 6) + printf("InvalidChainFound: WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); +} + + + + + + + + + + + +bool CTransaction::DisconnectInputs(CTxDB& txdb) +{ + // Relinquish previous transactions' spent pointers + if (!IsCoinBase()) + { + BOOST_FOREACH(const CTxIn& txin, vin) + { + COutPoint prevout = txin.prevout; + + // Get prev txindex from disk + CTxIndex txindex; + if (!txdb.ReadTxIndex(prevout.hash, txindex)) + return error("DisconnectInputs() : ReadTxIndex failed"); + + if (prevout.n >= txindex.vSpent.size()) + return error("DisconnectInputs() : prevout.n out of range"); + + // Mark outpoint as not spent + txindex.vSpent[prevout.n].SetNull(); + + // Write back + if (!txdb.UpdateTxIndex(prevout.hash, txindex)) + return error("DisconnectInputs() : UpdateTxIndex failed"); + } + } + + // Remove transaction from index + if (!txdb.EraseTxIndex(*this)) + return error("DisconnectInputs() : EraseTxPos failed"); + + return true; +} + + +bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPool, CDiskTxPos posThisTx, + CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee) +{ + // Take over previous transactions' spent pointers + if (!IsCoinBase()) + { + int64 nValueIn = 0; + for (int i = 0; i < vin.size(); i++) + { + COutPoint prevout = vin[i].prevout; + + // Read txindex + CTxIndex txindex; + bool fFound = true; + if (fMiner && mapTestPool.count(prevout.hash)) + { + // Get txindex from current proposed changes + txindex = mapTestPool[prevout.hash]; + } + else + { + // Read txindex from txdb + fFound = txdb.ReadTxIndex(prevout.hash, txindex); + } + if (!fFound && (fBlock || fMiner)) + return fMiner ? false : error("ConnectInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + + // Read txPrev + CTransaction txPrev; + if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) + { + // Get prev tx from single transactions in memory + CRITICAL_BLOCK(cs_mapTransactions) + { + if (!mapTransactions.count(prevout.hash)) + return error("ConnectInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + txPrev = mapTransactions[prevout.hash]; + } + if (!fFound) + txindex.vSpent.resize(txPrev.vout.size()); + } + else + { + // Get prev tx from disk + if (!txPrev.ReadFromDisk(txindex.pos)) + return error("ConnectInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + } + + if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) + return error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()); + + // If prev is coinbase, check that it's matured + if (txPrev.IsCoinBase()) + for (CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev) + if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) + return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight); + + // Verify signature + if (!VerifySignature(txPrev, *this, i)) + return error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); + + // Check for conflicts + if (!txindex.vSpent[prevout.n].IsNull()) + return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str()); + + // Check for negative or overflow input values + nValueIn += txPrev.vout[prevout.n].nValue; + if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) + return error("ConnectInputs() : txin values out of range"); + + // Mark outpoints as spent + txindex.vSpent[prevout.n] = posThisTx; + + // Write back + if (fBlock) + { + if (!txdb.UpdateTxIndex(prevout.hash, txindex)) + return error("ConnectInputs() : UpdateTxIndex failed"); + } + else if (fMiner) + { + mapTestPool[prevout.hash] = txindex; + } + } + + if (nValueIn < GetValueOut()) + return error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()); + + // Tally transaction fees + int64 nTxFee = nValueIn - GetValueOut(); + if (nTxFee < 0) + return error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()); + if (nTxFee < nMinFee) + return false; + nFees += nTxFee; + if (!MoneyRange(nFees)) + return error("ConnectInputs() : nFees out of range"); + } + + if (fBlock) + { + // Add transaction to disk index + if (!txdb.AddTxIndex(*this, posThisTx, pindexBlock->nHeight)) + return error("ConnectInputs() : AddTxPos failed"); + } + else if (fMiner) + { + // Add transaction to test pool + mapTestPool[GetHash()] = CTxIndex(CDiskTxPos(1,1,1), vout.size()); + } + + return true; +} + + +bool CTransaction::ClientConnectInputs() +{ + if (IsCoinBase()) + return false; + + // Take over previous transactions' spent pointers + CRITICAL_BLOCK(cs_mapTransactions) + { + int64 nValueIn = 0; + for (int i = 0; i < vin.size(); i++) + { + // Get prev tx from single transactions in memory + COutPoint prevout = vin[i].prevout; + if (!mapTransactions.count(prevout.hash)) + return false; + CTransaction& txPrev = mapTransactions[prevout.hash]; + + if (prevout.n >= txPrev.vout.size()) + return false; + + // Verify signature + if (!VerifySignature(txPrev, *this, i)) + return error("ConnectInputs() : VerifySignature failed"); + + ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of + ///// this has to go away now that posNext is gone + // // Check for conflicts + // if (!txPrev.vout[prevout.n].posNext.IsNull()) + // return error("ConnectInputs() : prev tx already used"); + // + // // Flag outpoints as used + // txPrev.vout[prevout.n].posNext = posThisTx; + + nValueIn += txPrev.vout[prevout.n].nValue; + + if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) + return error("ClientConnectInputs() : txin values out of range"); + } + if (GetValueOut() > nValueIn) + return false; + } + + return true; +} + + + + +bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) +{ + // Disconnect in reverse order + for (int i = vtx.size()-1; i >= 0; i--) + if (!vtx[i].DisconnectInputs(txdb)) + return false; + + // Update block index on disk without changing it in memory. + // The memory index structure will be changed after the db commits. + if (pindex->pprev) + { + CDiskBlockIndex blockindexPrev(pindex->pprev); + blockindexPrev.hashNext = 0; + if (!txdb.WriteBlockIndex(blockindexPrev)) + return error("DisconnectBlock() : WriteBlockIndex failed"); + } + + return true; +} + +bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) +{ + // Check it again in case a previous version let a bad block in + if (!CheckBlock()) + return false; + + //// issue here: it doesn't know the version + unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size()); + + map mapUnused; + int64 nFees = 0; + BOOST_FOREACH(CTransaction& tx, vtx) + { + CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); + nTxPos += ::GetSerializeSize(tx, SER_DISK); + + if (!tx.ConnectInputs(txdb, mapUnused, posThisTx, pindex, nFees, true, false)) + return false; + } + + if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) + return false; + + // Update block index on disk without changing it in memory. + // The memory index structure will be changed after the db commits. + if (pindex->pprev) + { + CDiskBlockIndex blockindexPrev(pindex->pprev); + blockindexPrev.hashNext = pindex->GetBlockHash(); + if (!txdb.WriteBlockIndex(blockindexPrev)) + return error("ConnectBlock() : WriteBlockIndex failed"); + } + + // Watch for transactions paying to me + BOOST_FOREACH(CTransaction& tx, vtx) + AddToWalletIfInvolvingMe(tx, this, true); + + return true; +} + +bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) +{ + printf("REORGANIZE\n"); + + // Find the fork + CBlockIndex* pfork = pindexBest; + CBlockIndex* plonger = pindexNew; + while (pfork != plonger) + { + while (plonger->nHeight > pfork->nHeight) + if (!(plonger = plonger->pprev)) + return error("Reorganize() : plonger->pprev is null"); + if (pfork == plonger) + break; + if (!(pfork = pfork->pprev)) + return error("Reorganize() : pfork->pprev is null"); + } + + // List of what to disconnect + vector vDisconnect; + for (CBlockIndex* pindex = pindexBest; pindex != pfork; pindex = pindex->pprev) + vDisconnect.push_back(pindex); + + // List of what to connect + vector vConnect; + for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev) + vConnect.push_back(pindex); + reverse(vConnect.begin(), vConnect.end()); + + // Disconnect shorter branch + vector vResurrect; + BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) + { + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("Reorganize() : ReadFromDisk for disconnect failed"); + if (!block.DisconnectBlock(txdb, pindex)) + return error("Reorganize() : DisconnectBlock failed"); + + // Queue memory transactions to resurrect + BOOST_FOREACH(const CTransaction& tx, block.vtx) + if (!tx.IsCoinBase()) + vResurrect.push_back(tx); + } + + // Connect longer branch + vector vDelete; + for (int i = 0; i < vConnect.size(); i++) + { + CBlockIndex* pindex = vConnect[i]; + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("Reorganize() : ReadFromDisk for connect failed"); + if (!block.ConnectBlock(txdb, pindex)) + { + // Invalid block + txdb.TxnAbort(); + return error("Reorganize() : ConnectBlock failed"); + } + + // Queue memory transactions to delete + BOOST_FOREACH(const CTransaction& tx, block.vtx) + vDelete.push_back(tx); + } + if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash())) + return error("Reorganize() : WriteHashBestChain failed"); + + // Make sure it's successfully written to disk before changing memory structure + if (!txdb.TxnCommit()) + return error("Reorganize() : TxnCommit failed"); + + // Disconnect shorter branch + BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) + if (pindex->pprev) + pindex->pprev->pnext = NULL; + + // Connect longer branch + BOOST_FOREACH(CBlockIndex* pindex, vConnect) + if (pindex->pprev) + pindex->pprev->pnext = pindex; + + // Resurrect memory transactions that were in the disconnected branch + BOOST_FOREACH(CTransaction& tx, vResurrect) + tx.AcceptToMemoryPool(txdb, false); + + // Delete redundant memory transactions that are in the connected branch + BOOST_FOREACH(CTransaction& tx, vDelete) + tx.RemoveFromMemoryPool(); + + return true; +} + + +bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) +{ + uint256 hash = GetHash(); + + txdb.TxnBegin(); + if (pindexGenesisBlock == NULL && hash == hashGenesisBlock) + { + txdb.WriteHashBestChain(hash); + if (!txdb.TxnCommit()) + return error("SetBestChain() : TxnCommit failed"); + pindexGenesisBlock = pindexNew; + } + else if (hashPrevBlock == hashBestChain) + { + // Adding to current best branch + if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash)) + { + txdb.TxnAbort(); + InvalidChainFound(pindexNew); + return error("SetBestChain() : ConnectBlock failed"); + } + if (!txdb.TxnCommit()) + return error("SetBestChain() : TxnCommit failed"); + + // Add to current best branch + pindexNew->pprev->pnext = pindexNew; + + // Delete redundant memory transactions + BOOST_FOREACH(CTransaction& tx, vtx) + tx.RemoveFromMemoryPool(); + } + else + { + // New best branch + if (!Reorganize(txdb, pindexNew)) + { + txdb.TxnAbort(); + InvalidChainFound(pindexNew); + return error("SetBestChain() : Reorganize failed"); + } + } + + // Update best block in wallet (so we can detect restored wallets) + if (!IsInitialBlockDownload()) + { + CWalletDB walletdb; + const CBlockLocator locator(pindexNew); + if (!walletdb.WriteBestBlock(locator)) + return error("SetBestChain() : WriteWalletBest failed"); + } + + // New best block + hashBestChain = hash; + pindexBest = pindexNew; + nBestHeight = pindexBest->nHeight; + bnBestChainWork = pindexNew->bnChainWork; + nTimeBestReceived = GetTime(); + nTransactionsUpdated++; + printf("SetBestChain: new best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str()); + + return true; +} + + +bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) +{ + // Check for duplicate + uint256 hash = GetHash(); + if (mapBlockIndex.count(hash)) + return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,20).c_str()); + + // Construct new block index object + CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this); + if (!pindexNew) + return error("AddToBlockIndex() : new CBlockIndex failed"); + map::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + pindexNew->phashBlock = &((*mi).first); + map::iterator miPrev = mapBlockIndex.find(hashPrevBlock); + if (miPrev != mapBlockIndex.end()) + { + pindexNew->pprev = (*miPrev).second; + pindexNew->nHeight = pindexNew->pprev->nHeight + 1; + } + pindexNew->bnChainWork = (pindexNew->pprev ? pindexNew->pprev->bnChainWork : 0) + pindexNew->GetBlockWork(); + + CTxDB txdb; + txdb.TxnBegin(); + txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew)); + if (!txdb.TxnCommit()) + return false; + + // New best + if (pindexNew->bnChainWork > bnBestChainWork) + if (!SetBestChain(txdb, pindexNew)) + return false; + + txdb.Close(); + + if (pindexNew == pindexBest) + { + // Notify UI to display prev block's coinbase if it was ours + static uint256 hashPrevBestCoinBase; + CRITICAL_BLOCK(cs_mapWallet) + vWalletUpdated.push_back(hashPrevBestCoinBase); + hashPrevBestCoinBase = vtx[0].GetHash(); + } + + MainFrameRepaint(); + return true; +} + + + + +bool CBlock::CheckBlock() const +{ + // These are checks that are independent of context + // that can be verified before saving an orphan block. + + // Size limits + if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE) + return error("CheckBlock() : size limits failed"); + + // Check proof of work matches claimed amount + if (!CheckProofOfWork(GetHash(), nBits)) + return error("CheckBlock() : proof of work failed"); + + // Check timestamp + if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) + return error("CheckBlock() : block timestamp too far in the future"); + + // First transaction must be coinbase, the rest must not be + if (vtx.empty() || !vtx[0].IsCoinBase()) + return error("CheckBlock() : first tx is not coinbase"); + for (int i = 1; i < vtx.size(); i++) + if (vtx[i].IsCoinBase()) + return error("CheckBlock() : more than one coinbase"); + + // Check transactions + BOOST_FOREACH(const CTransaction& tx, vtx) + if (!tx.CheckTransaction()) + return error("CheckBlock() : CheckTransaction failed"); + + // Check that it's not full of nonstandard transactions + if (GetSigOpCount() > MAX_BLOCK_SIGOPS) + return error("CheckBlock() : too many nonstandard transactions"); + + // Check merkleroot + if (hashMerkleRoot != BuildMerkleTree()) + return error("CheckBlock() : hashMerkleRoot mismatch"); + + return true; +} + +bool CBlock::AcceptBlock() +{ + // Check for duplicate + uint256 hash = GetHash(); + if (mapBlockIndex.count(hash)) + return error("AcceptBlock() : block already in mapBlockIndex"); + + // Get prev block index + map::iterator mi = mapBlockIndex.find(hashPrevBlock); + if (mi == mapBlockIndex.end()) + return error("AcceptBlock() : prev block not found"); + CBlockIndex* pindexPrev = (*mi).second; + int nHeight = pindexPrev->nHeight+1; + + // Check proof of work + if (nBits != GetNextWorkRequired(pindexPrev)) + return error("AcceptBlock() : incorrect proof of work"); + + // Check timestamp against prev + if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) + return error("AcceptBlock() : block's timestamp is too early"); + + // Check that all transactions are finalized + BOOST_FOREACH(const CTransaction& tx, vtx) + if (!tx.IsFinal(nHeight, GetBlockTime())) + return error("AcceptBlock() : contains a non-final transaction"); + + // Check that the block chain matches the known block chain up to a checkpoint + if (!fTestNet) + if ((nHeight == 11111 && hash != uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")) || + (nHeight == 33333 && hash != uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")) || + (nHeight == 68555 && hash != uint256("0x00000000001e1b4903550a0b96e9a9405c8a95f387162e4944e8d9fbe501cd6a")) || + (nHeight == 70567 && hash != uint256("0x00000000006a49b14bcf27462068f1264c961f11fa2e0eddd2be0791e1d4124a")) || + (nHeight == 74000 && hash != uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")) || + (nHeight == 105000 && hash != uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")) || + (nHeight == 118000 && hash != uint256("0x000000000000774a7f8a7a12dc906ddb9e17e75d684f15e00f8767f9e8f36553"))) + return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight); + + // Write block to history file + if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK))) + return error("AcceptBlock() : out of disk space"); + unsigned int nFile = -1; + unsigned int nBlockPos = 0; + if (!WriteToDisk(nFile, nBlockPos)) + return error("AcceptBlock() : WriteToDisk failed"); + if (!AddToBlockIndex(nFile, nBlockPos)) + return error("AcceptBlock() : AddToBlockIndex failed"); + + // Relay inventory, but don't relay old inventory during initial block download + if (hashBestChain == hash) + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 118000)) + pnode->PushInventory(CInv(MSG_BLOCK, hash)); + + return true; +} + +bool ProcessBlock(CNode* pfrom, CBlock* pblock) +{ + // Check for duplicate + uint256 hash = pblock->GetHash(); + if (mapBlockIndex.count(hash)) + return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().substr(0,20).c_str()); + if (mapOrphanBlocks.count(hash)) + return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str()); + + // Preliminary checks + if (!pblock->CheckBlock()) + return error("ProcessBlock() : CheckBlock FAILED"); + + // If don't already have its previous block, shunt it off to holding area until we get it + if (!mapBlockIndex.count(pblock->hashPrevBlock)) + { + printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str()); + CBlock* pblock2 = new CBlock(*pblock); + mapOrphanBlocks.insert(make_pair(hash, pblock2)); + mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); + + // Ask this guy to fill in what we're missing + if (pfrom) + pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2)); + return true; + } + + // Store to disk + if (!pblock->AcceptBlock()) + return error("ProcessBlock() : AcceptBlock FAILED"); + + // Recursively process any orphan blocks that depended on this one + vector vWorkQueue; + vWorkQueue.push_back(hash); + for (int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hashPrev = vWorkQueue[i]; + for (multimap::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev); + mi != mapOrphanBlocksByPrev.upper_bound(hashPrev); + ++mi) + { + CBlock* pblockOrphan = (*mi).second; + if (pblockOrphan->AcceptBlock()) + vWorkQueue.push_back(pblockOrphan->GetHash()); + mapOrphanBlocks.erase(pblockOrphan->GetHash()); + delete pblockOrphan; + } + mapOrphanBlocksByPrev.erase(hashPrev); + } + + printf("ProcessBlock: ACCEPTED\n"); + return true; +} + + + + + + + + +template +bool ScanMessageStart(Stream& s) +{ + // Scan ahead to the next pchMessageStart, which should normally be immediately + // at the file pointer. Leaves file pointer at end of pchMessageStart. + s.clear(0); + short prevmask = s.exceptions(0); + const char* p = BEGIN(pchMessageStart); + try + { + loop + { + char c; + s.read(&c, 1); + if (s.fail()) + { + s.clear(0); + s.exceptions(prevmask); + return false; + } + if (*p != c) + p = BEGIN(pchMessageStart); + if (*p == c) + { + if (++p == END(pchMessageStart)) + { + s.clear(0); + s.exceptions(prevmask); + return true; + } + } + } + } + catch (...) + { + s.clear(0); + s.exceptions(prevmask); + return false; + } +} + +bool CheckDiskSpace(uint64 nAdditionalBytes) +{ + uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available; + + // Check for 15MB because database could create another 10MB log file at any time + if (nFreeBytesAvailable < (uint64)15000000 + nAdditionalBytes) + { + fShutdown = true; + string strMessage = _("Warning: Disk space is low "); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + ThreadSafeMessageBox(strMessage, "Bitcoin", wxOK | wxICON_EXCLAMATION); + CreateThread(Shutdown, NULL); + return false; + } + return true; +} + +FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode) +{ + if (nFile == -1) + return NULL; + FILE* file = fopen(strprintf("%s/blk%04d.dat", GetDataDir().c_str(), nFile).c_str(), pszMode); + if (!file) + return NULL; + if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w')) + { + if (fseek(file, nBlockPos, SEEK_SET) != 0) + { + fclose(file); + return NULL; + } + } + return file; +} + +static unsigned int nCurrentBlockFile = 1; + +FILE* AppendBlockFile(unsigned int& nFileRet) +{ + nFileRet = 0; + loop + { + FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab"); + if (!file) + return NULL; + if (fseek(file, 0, SEEK_END) != 0) + return NULL; + // FAT32 filesize max 4GB, fseek and ftell max 2GB, so we must stay under 2GB + if (ftell(file) < 0x7F000000 - MAX_SIZE) + { + nFileRet = nCurrentBlockFile; + return file; + } + fclose(file); + nCurrentBlockFile++; + } +} + +bool LoadBlockIndex(bool fAllowNew) +{ + if (fTestNet) + { + hashGenesisBlock = uint256("0x00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"); + bnProofOfWorkLimit = CBigNum(~uint256(0) >> 28); + pchMessageStart[0] = 0xfa; + pchMessageStart[1] = 0xbf; + pchMessageStart[2] = 0xb5; + pchMessageStart[3] = 0xda; + } + + // + // Load block index + // + CTxDB txdb("cr"); + if (!txdb.LoadBlockIndex()) + return false; + txdb.Close(); + + // + // Init with genesis block + // + if (mapBlockIndex.empty()) + { + if (!fAllowNew) + return false; + + // Genesis Block: + // CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1) + // CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) + // CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73) + // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) + // vMerkleTree: 4a5e1e + + // Genesis block + const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"; + CTransaction txNew; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vout[0].nValue = 50 * COIN; + txNew.vout[0].scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; + CBlock block; + block.vtx.push_back(txNew); + block.hashPrevBlock = 0; + block.hashMerkleRoot = block.BuildMerkleTree(); + block.nVersion = 1; + block.nTime = 1231006505; + block.nBits = 0x1d00ffff; + block.nNonce = 2083236893; + + if (fTestNet) + { + block.nTime = 1296688602; + block.nBits = 0x1d07fff8; + block.nNonce = 384568319; + } + + //// debug print + printf("%s\n", block.GetHash().ToString().c_str()); + printf("%s\n", hashGenesisBlock.ToString().c_str()); + printf("%s\n", block.hashMerkleRoot.ToString().c_str()); + assert(block.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); + block.print(); + assert(block.GetHash() == hashGenesisBlock); + + // Start new block file + unsigned int nFile; + unsigned int nBlockPos; + if (!block.WriteToDisk(nFile, nBlockPos)) + return error("LoadBlockIndex() : writing genesis block to disk failed"); + if (!block.AddToBlockIndex(nFile, nBlockPos)) + return error("LoadBlockIndex() : genesis block not accepted"); + } + + return true; +} + + + +void PrintBlockTree() +{ + // precompute tree structure + map > mapNext; + for (map::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) + { + CBlockIndex* pindex = (*mi).second; + mapNext[pindex->pprev].push_back(pindex); + // test + //while (rand() % 3 == 0) + // mapNext[pindex->pprev].push_back(pindex); + } + + vector > vStack; + vStack.push_back(make_pair(0, pindexGenesisBlock)); + + int nPrevCol = 0; + while (!vStack.empty()) + { + int nCol = vStack.back().first; + CBlockIndex* pindex = vStack.back().second; + vStack.pop_back(); + + // print split or gap + if (nCol > nPrevCol) + { + for (int i = 0; i < nCol-1; i++) + printf("| "); + printf("|\\\n"); + } + else if (nCol < nPrevCol) + { + for (int i = 0; i < nCol; i++) + printf("| "); + printf("|\n"); + } + nPrevCol = nCol; + + // print columns + for (int i = 0; i < nCol; i++) + printf("| "); + + // print item + CBlock block; + block.ReadFromDisk(pindex); + printf("%d (%u,%u) %s %s tx %d", + pindex->nHeight, + pindex->nFile, + pindex->nBlockPos, + block.GetHash().ToString().substr(0,20).c_str(), + DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(), + block.vtx.size()); + + CRITICAL_BLOCK(cs_mapWallet) + { + if (mapWallet.count(block.vtx[0].GetHash())) + { + CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; + printf(" mine: %d %d %d", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); + } + } + printf("\n"); + + + // put the main timechain first + vector& vNext = mapNext[pindex]; + for (int i = 0; i < vNext.size(); i++) + { + if (vNext[i]->pnext) + { + swap(vNext[0], vNext[i]); + break; + } + } + + // iterate children + for (int i = 0; i < vNext.size(); i++) + vStack.push_back(make_pair(nCol+i, vNext[i])); + } +} + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CAlert +// + +map mapAlerts; +CCriticalSection cs_mapAlerts; + +string GetWarnings(string strFor) +{ + int nPriority = 0; + string strStatusBar; + string strRPC; + if (GetBoolArg("-testsafemode")) + strRPC = "test"; + + // Misc warnings like out of disk space and clock is wrong + if (strMiscWarning != "") + { + nPriority = 1000; + strStatusBar = strMiscWarning; + } + + // Longer invalid proof-of-work chain + if (pindexBest && bnBestInvalidWork > bnBestChainWork + pindexBest->GetBlockWork() * 6) + { + nPriority = 2000; + strStatusBar = strRPC = "WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade."; + } + + // Alerts + CRITICAL_BLOCK(cs_mapAlerts) + { + BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + { + const CAlert& alert = item.second; + if (alert.AppliesToMe() && alert.nPriority > nPriority) + { + nPriority = alert.nPriority; + strStatusBar = alert.strStatusBar; + } + } + } + + if (strFor == "statusbar") + return strStatusBar; + else if (strFor == "rpc") + return strRPC; + assert(("GetWarnings() : invalid parameter", false)); + return "error"; +} + +bool CAlert::ProcessAlert() +{ + if (!CheckSignature()) + return false; + if (!IsInEffect()) + return false; + + CRITICAL_BLOCK(cs_mapAlerts) + { + // Cancel previous alerts + for (map::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();) + { + const CAlert& alert = (*mi).second; + if (Cancels(alert)) + { + printf("cancelling alert %d\n", alert.nID); + mapAlerts.erase(mi++); + } + else if (!alert.IsInEffect()) + { + printf("expiring alert %d\n", alert.nID); + mapAlerts.erase(mi++); + } + else + mi++; + } + + // Check if this alert has been cancelled + BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + { + const CAlert& alert = item.second; + if (alert.Cancels(*this)) + { + printf("alert already cancelled by %d\n", alert.nID); + return false; + } + } + + // Add to mapAlerts + mapAlerts.insert(make_pair(GetHash(), *this)); + } + + printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe()); + MainFrameRepaint(); + return true; +} + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Messages +// + + +bool AlreadyHave(CTxDB& txdb, const CInv& inv) +{ + switch (inv.type) + { + case MSG_TX: return mapTransactions.count(inv.hash) || mapOrphanTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash); + case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash); + } + // Don't know what it is, just say we already got one + return true; +} + + + + +// The message start string is designed to be unlikely to occur in normal data. +// The characters are rarely used upper ascii, not valid as UTF-8, and produce +// a large 4-byte int at any alignment. +char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; + + +bool ProcessMessages(CNode* pfrom) +{ + CDataStream& vRecv = pfrom->vRecv; + if (vRecv.empty()) + return true; + //if (fDebug) + // printf("ProcessMessages(%u bytes)\n", vRecv.size()); + + // + // Message format + // (4) message start + // (12) command + // (4) size + // (4) checksum + // (x) data + // + + loop + { + // Scan for message start + CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart)); + int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader()); + if (vRecv.end() - pstart < nHeaderSize) + { + if (vRecv.size() > nHeaderSize) + { + printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n"); + vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); + } + break; + } + if (pstart - vRecv.begin() > 0) + printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", pstart - vRecv.begin()); + vRecv.erase(vRecv.begin(), pstart); + + // Read header + vector vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize); + CMessageHeader hdr; + vRecv >> hdr; + if (!hdr.IsValid()) + { + printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); + continue; + } + string strCommand = hdr.GetCommand(); + + // Message size + unsigned int nMessageSize = hdr.nMessageSize; + if (nMessageSize > MAX_SIZE) + { + printf("ProcessMessage(%s, %u bytes) : nMessageSize > MAX_SIZE\n", strCommand.c_str(), nMessageSize); + continue; + } + if (nMessageSize > vRecv.size()) + { + // Rewind and wait for rest of message + vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end()); + break; + } + + // Checksum + if (vRecv.GetVersion() >= 209) + { + uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + if (nChecksum != hdr.nChecksum) + { + printf("ProcessMessage(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", + strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); + continue; + } + } + + // Copy message to its own buffer + CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion); + vRecv.ignore(nMessageSize); + + // Process message + bool fRet = false; + try + { + CRITICAL_BLOCK(cs_main) + fRet = ProcessMessage(pfrom, strCommand, vMsg); + if (fShutdown) + return true; + } + catch (std::ios_base::failure& e) + { + if (strstr(e.what(), "end of data")) + { + // Allow exceptions from underlength message on vRecv + printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what()); + } + else if (strstr(e.what(), "size too large")) + { + // Allow exceptions from overlong size + printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); + } + else + { + PrintExceptionContinue(&e, "ProcessMessage()"); + } + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "ProcessMessage()"); + } catch (...) { + PrintExceptionContinue(NULL, "ProcessMessage()"); + } + + if (!fRet) + printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); + } + + vRecv.Compact(); + return true; +} + + + + +bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) +{ + static map > mapReuseKey; + RandAddSeedPerfmon(); + if (fDebug) + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); + printf("received: %s (%d bytes)\n", strCommand.c_str(), vRecv.size()); + if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) + { + printf("dropmessagestest DROPPING RECV MESSAGE\n"); + return true; + } + + + + + + if (strCommand == "version") + { + // Each connection can only send one version message + if (pfrom->nVersion != 0) + return false; + + int64 nTime; + CAddress addrMe; + CAddress addrFrom; + uint64 nNonce = 1; + vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; + if (pfrom->nVersion == 10300) + pfrom->nVersion = 300; + if (pfrom->nVersion >= 106 && !vRecv.empty()) + vRecv >> addrFrom >> nNonce; + if (pfrom->nVersion >= 106 && !vRecv.empty()) + vRecv >> pfrom->strSubVer; + if (pfrom->nVersion >= 209 && !vRecv.empty()) + vRecv >> pfrom->nStartingHeight; + + if (pfrom->nVersion == 0) + return false; + + // Disconnect if we connected to ourself + if (nNonce == nLocalHostNonce && nNonce > 1) + { + printf("connected to self at %s, disconnecting\n", pfrom->addr.ToString().c_str()); + pfrom->fDisconnect = true; + return true; + } + + // Be shy and don't send version until we hear + if (pfrom->fInbound) + pfrom->PushVersion(); + + pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); + + AddTimeData(pfrom->addr.ip, nTime); + + // Change version + if (pfrom->nVersion >= 209) + pfrom->PushMessage("verack"); + pfrom->vSend.SetVersion(min(pfrom->nVersion, VERSION)); + if (pfrom->nVersion < 209) + pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION)); + + if (!pfrom->fInbound) + { + // Advertise our address + if (addrLocalHost.IsRoutable() && !fUseProxy) + { + CAddress addr(addrLocalHost); + addr.nTime = GetAdjustedTime(); + pfrom->PushAddress(addr); + } + + // Get recent addresses + if (pfrom->nVersion >= 31402 || mapAddresses.size() < 1000) + { + pfrom->PushMessage("getaddr"); + pfrom->fGetAddr = true; + } + } + + // Ask the first connected node for block updates + static int nAskedForBlocks; + if (!pfrom->fClient && (nAskedForBlocks < 1 || vNodes.size() <= 1)) + { + nAskedForBlocks++; + pfrom->PushGetBlocks(pindexBest, uint256(0)); + } + + // Relay alerts + CRITICAL_BLOCK(cs_mapAlerts) + BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + item.second.RelayTo(pfrom); + + pfrom->fSuccessfullyConnected = true; + + printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight); + } + + + else if (pfrom->nVersion == 0) + { + // Must have a version message before anything else + return false; + } + + + else if (strCommand == "verack") + { + pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION)); + } + + + else if (strCommand == "addr") + { + vector vAddr; + vRecv >> vAddr; + + // Don't want addr from older versions unless seeding + if (pfrom->nVersion < 209) + return true; + if (pfrom->nVersion < 31402 && mapAddresses.size() > 1000) + return true; + if (vAddr.size() > 1000) + return error("message addr size() = %d", vAddr.size()); + + // Store the new addresses + int64 nNow = GetAdjustedTime(); + int64 nSince = nNow - 10 * 60; + BOOST_FOREACH(CAddress& addr, vAddr) + { + if (fShutdown) + return true; + // ignore IPv6 for now, since it isn't implemented anyway + if (!addr.IsIPv4()) + continue; + if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) + addr.nTime = nNow - 5 * 24 * 60 * 60; + AddAddress(addr, 2 * 60 * 60); + pfrom->AddAddressKnown(addr); + if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) + { + // Relay to a limited number of other nodes + CRITICAL_BLOCK(cs_vNodes) + { + // Use deterministic randomness to send to the same nodes for 24 hours + // at a time so the setAddrKnowns of the chosen nodes prevent repeats + static uint256 hashSalt; + if (hashSalt == 0) + RAND_bytes((unsigned char*)&hashSalt, sizeof(hashSalt)); + uint256 hashRand = hashSalt ^ (((int64)addr.ip)<<32) ^ ((GetTime()+addr.ip)/(24*60*60)); + hashRand = Hash(BEGIN(hashRand), END(hashRand)); + multimap mapMix; + BOOST_FOREACH(CNode* pnode, vNodes) + { + if (pnode->nVersion < 31402) + continue; + unsigned int nPointer; + memcpy(&nPointer, &pnode, sizeof(nPointer)); + uint256 hashKey = hashRand ^ nPointer; + hashKey = Hash(BEGIN(hashKey), END(hashKey)); + mapMix.insert(make_pair(hashKey, pnode)); + } + int nRelayNodes = 2; + for (multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) + ((*mi).second)->PushAddress(addr); + } + } + } + if (vAddr.size() < 1000) + pfrom->fGetAddr = false; + } + + + else if (strCommand == "inv") + { + vector vInv; + vRecv >> vInv; + if (vInv.size() > 50000) + return error("message inv size() = %d", vInv.size()); + + CTxDB txdb("r"); + BOOST_FOREACH(const CInv& inv, vInv) + { + if (fShutdown) + return true; + pfrom->AddInventoryKnown(inv); + + bool fAlreadyHave = AlreadyHave(txdb, inv); + printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); + + if (!fAlreadyHave) + pfrom->AskFor(inv); + else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) + pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); + + // Track requests for our stuff + CRITICAL_BLOCK(cs_mapRequestCount) + { + map::iterator mi = mapRequestCount.find(inv.hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } + } + } + + + else if (strCommand == "getdata") + { + vector vInv; + vRecv >> vInv; + if (vInv.size() > 50000) + return error("message getdata size() = %d", vInv.size()); + + BOOST_FOREACH(const CInv& inv, vInv) + { + if (fShutdown) + return true; + printf("received getdata for: %s\n", inv.ToString().c_str()); + + if (inv.type == MSG_BLOCK) + { + // Send block from disk + map::iterator mi = mapBlockIndex.find(inv.hash); + if (mi != mapBlockIndex.end()) + { + CBlock block; + block.ReadFromDisk((*mi).second); + pfrom->PushMessage("block", block); + + // Trigger them to send a getblocks request for the next batch of inventory + if (inv.hash == pfrom->hashContinue) + { + // Bypass PushInventory, this must send even if redundant, + // and we want it right after the last block so they don't + // wait for other stuff first. + vector vInv; + vInv.push_back(CInv(MSG_BLOCK, hashBestChain)); + pfrom->PushMessage("inv", vInv); + pfrom->hashContinue = 0; + } + } + } + else if (inv.IsKnownType()) + { + // Send stream from relay memory + CRITICAL_BLOCK(cs_mapRelay) + { + map::iterator mi = mapRelay.find(inv); + if (mi != mapRelay.end()) + pfrom->PushMessage(inv.GetCommand(), (*mi).second); + } + } + + // Track requests for our stuff + CRITICAL_BLOCK(cs_mapRequestCount) + { + map::iterator mi = mapRequestCount.find(inv.hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } + } + } + + + else if (strCommand == "getblocks") + { + CBlockLocator locator; + uint256 hashStop; + vRecv >> locator >> hashStop; + + // Find the last block the caller has in the main chain + CBlockIndex* pindex = locator.GetBlockIndex(); + + // Send the rest of the chain + if (pindex) + pindex = pindex->pnext; + int nLimit = 500 + locator.GetDistanceBack(); + printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit); + for (; pindex; pindex = pindex->pnext) + { + if (pindex->GetBlockHash() == hashStop) + { + printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); + break; + } + pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); + if (--nLimit <= 0) + { + // When this block is requested, we'll send an inv that'll make them + // getblocks the next batch of inventory. + printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); + pfrom->hashContinue = pindex->GetBlockHash(); + break; + } + } + } + + + else if (strCommand == "getheaders") + { + CBlockLocator locator; + uint256 hashStop; + vRecv >> locator >> hashStop; + + CBlockIndex* pindex = NULL; + if (locator.IsNull()) + { + // If locator is null, return the hashStop block + map::iterator mi = mapBlockIndex.find(hashStop); + if (mi == mapBlockIndex.end()) + return true; + pindex = (*mi).second; + } + else + { + // Find the last block the caller has in the main chain + pindex = locator.GetBlockIndex(); + if (pindex) + pindex = pindex->pnext; + } + + vector vHeaders; + int nLimit = 2000 + locator.GetDistanceBack(); + printf("getheaders %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit); + for (; pindex; pindex = pindex->pnext) + { + vHeaders.push_back(pindex->GetBlockHeader()); + if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) + break; + } + pfrom->PushMessage("headers", vHeaders); + } + + + else if (strCommand == "tx") + { + vector vWorkQueue; + CDataStream vMsg(vRecv); + CTransaction tx; + vRecv >> tx; + + CInv inv(MSG_TX, tx.GetHash()); + pfrom->AddInventoryKnown(inv); + + bool fMissingInputs = false; + if (tx.AcceptToMemoryPool(true, &fMissingInputs)) + { + AddToWalletIfInvolvingMe(tx, NULL, true); + RelayMessage(inv, vMsg); + mapAlreadyAskedFor.erase(inv); + vWorkQueue.push_back(inv.hash); + + // Recursively process any orphan transactions that depended on this one + for (int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hashPrev = vWorkQueue[i]; + for (multimap::iterator mi = mapOrphanTransactionsByPrev.lower_bound(hashPrev); + mi != mapOrphanTransactionsByPrev.upper_bound(hashPrev); + ++mi) + { + const CDataStream& vMsg = *((*mi).second); + CTransaction tx; + CDataStream(vMsg) >> tx; + CInv inv(MSG_TX, tx.GetHash()); + + if (tx.AcceptToMemoryPool(true)) + { + printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); + AddToWalletIfInvolvingMe(tx, NULL, true); + RelayMessage(inv, vMsg); + mapAlreadyAskedFor.erase(inv); + vWorkQueue.push_back(inv.hash); + } + } + } + + BOOST_FOREACH(uint256 hash, vWorkQueue) + EraseOrphanTx(hash); + } + else if (fMissingInputs) + { + printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); + AddOrphanTx(vMsg); + } + } + + + else if (strCommand == "block") + { + CBlock block; + vRecv >> block; + + printf("received block %s\n", block.GetHash().ToString().substr(0,20).c_str()); + // block.print(); + + CInv inv(MSG_BLOCK, block.GetHash()); + pfrom->AddInventoryKnown(inv); + + if (ProcessBlock(pfrom, &block)) + mapAlreadyAskedFor.erase(inv); + } + + + else if (strCommand == "getaddr") + { + // Nodes rebroadcast an addr every 24 hours + pfrom->vAddrToSend.clear(); + int64 nSince = GetAdjustedTime() - 3 * 60 * 60; // in the last 3 hours + CRITICAL_BLOCK(cs_mapAddresses) + { + unsigned int nCount = 0; + BOOST_FOREACH(const PAIRTYPE(vector, CAddress)& item, mapAddresses) + { + const CAddress& addr = item.second; + if (addr.nTime > nSince) + nCount++; + } + BOOST_FOREACH(const PAIRTYPE(vector, CAddress)& item, mapAddresses) + { + const CAddress& addr = item.second; + if (addr.nTime > nSince && GetRand(nCount) < 2500) + pfrom->PushAddress(addr); + } + } + } + + + else if (strCommand == "checkorder") + { + uint256 hashReply; + vRecv >> hashReply; + + if (!GetBoolArg("-allowreceivebyip")) + { + pfrom->PushMessage("reply", hashReply, (int)2, string("")); + return true; + } + + CWalletTx order; + vRecv >> order; + + /// we have a chance to check the order here + + // Keep giving the same key to the same ip until they use it + if (!mapReuseKey.count(pfrom->addr.ip)) + mapReuseKey[pfrom->addr.ip] = GetKeyFromKeyPool(); + + // Send back approval of order and pubkey to use + CScript scriptPubKey; + scriptPubKey << mapReuseKey[pfrom->addr.ip] << OP_CHECKSIG; + pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey); + } + + + else if (strCommand == "submitorder") + { + uint256 hashReply; + vRecv >> hashReply; + + if (!GetBoolArg("-allowreceivebyip")) + { + pfrom->PushMessage("reply", hashReply, (int)2); + return true; + } + + CWalletTx wtxNew; + vRecv >> wtxNew; + wtxNew.fFromMe = false; + + // Broadcast + if (!wtxNew.AcceptWalletTransaction()) + { + pfrom->PushMessage("reply", hashReply, (int)1); + return error("submitorder AcceptWalletTransaction() failed, returning error 1"); + } + wtxNew.fTimeReceivedIsTxTime = true; + AddToWallet(wtxNew); + wtxNew.RelayWalletTransaction(); + mapReuseKey.erase(pfrom->addr.ip); + + // Send back confirmation + pfrom->PushMessage("reply", hashReply, (int)0); + } + + + else if (strCommand == "reply") + { + uint256 hashReply; + vRecv >> hashReply; + + CRequestTracker tracker; + CRITICAL_BLOCK(pfrom->cs_mapRequests) + { + map::iterator mi = pfrom->mapRequests.find(hashReply); + if (mi != pfrom->mapRequests.end()) + { + tracker = (*mi).second; + pfrom->mapRequests.erase(mi); + } + } + if (!tracker.IsNull()) + tracker.fn(tracker.param1, vRecv); + } + + + else if (strCommand == "ping") + { + } + + + else if (strCommand == "alert") + { + CAlert alert; + vRecv >> alert; + + if (alert.ProcessAlert()) + { + // Relay + pfrom->setKnown.insert(alert.GetHash()); + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + alert.RelayTo(pnode); + } + } + + + else + { + // Ignore unknown commands for extensibility + } + + + // Update the last seen time for this node's address + if (pfrom->fNetworkNode) + if (strCommand == "version" || strCommand == "addr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping") + AddressCurrentlyConnected(pfrom->addr); + + + return true; +} + + + + + + + + + +bool SendMessages(CNode* pto, bool fSendTrickle) +{ + CRITICAL_BLOCK(cs_main) + { + // Don't send anything until we get their version message + if (pto->nVersion == 0) + return true; + + // Keep-alive ping + if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSend.empty()) + pto->PushMessage("ping"); + + // Resend wallet transactions that haven't gotten in a block yet + ResendWalletTransactions(); + + // Address refresh broadcast + static int64 nLastRebroadcast; + if (GetTime() - nLastRebroadcast > 24 * 60 * 60) + { + nLastRebroadcast = GetTime(); + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodes) + { + // Periodically clear setAddrKnown to allow refresh broadcasts + pnode->setAddrKnown.clear(); + + // Rebroadcast our address + if (addrLocalHost.IsRoutable() && !fUseProxy) + { + CAddress addr(addrLocalHost); + addr.nTime = GetAdjustedTime(); + pnode->PushAddress(addr); + } + } + } + } + + // Clear out old addresses periodically so it's not too much work at once + static int64 nLastClear; + if (nLastClear == 0) + nLastClear = GetTime(); + if (GetTime() - nLastClear > 10 * 60 && vNodes.size() >= 3) + { + nLastClear = GetTime(); + CRITICAL_BLOCK(cs_mapAddresses) + { + CAddrDB addrdb; + int64 nSince = GetAdjustedTime() - 14 * 24 * 60 * 60; + for (map, CAddress>::iterator mi = mapAddresses.begin(); + mi != mapAddresses.end();) + { + const CAddress& addr = (*mi).second; + if (addr.nTime < nSince) + { + if (mapAddresses.size() < 1000 || GetTime() > nLastClear + 20) + break; + addrdb.EraseAddress(addr); + mapAddresses.erase(mi++); + } + else + mi++; + } + } + } + + + // + // Message: addr + // + if (fSendTrickle) + { + vector vAddr; + vAddr.reserve(pto->vAddrToSend.size()); + BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend) + { + // returns true if wasn't already contained in the set + if (pto->setAddrKnown.insert(addr).second) + { + vAddr.push_back(addr); + // receiver rejects addr messages larger than 1000 + if (vAddr.size() >= 1000) + { + pto->PushMessage("addr", vAddr); + vAddr.clear(); + } + } + } + pto->vAddrToSend.clear(); + if (!vAddr.empty()) + pto->PushMessage("addr", vAddr); + } + + + // + // Message: inventory + // + vector vInv; + vector vInvWait; + CRITICAL_BLOCK(pto->cs_inventory) + { + vInv.reserve(pto->vInventoryToSend.size()); + vInvWait.reserve(pto->vInventoryToSend.size()); + BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) + { + if (pto->setInventoryKnown.count(inv)) + continue; + + // trickle out tx inv to protect privacy + if (inv.type == MSG_TX && !fSendTrickle) + { + // 1/4 of tx invs blast to all immediately + static uint256 hashSalt; + if (hashSalt == 0) + RAND_bytes((unsigned char*)&hashSalt, sizeof(hashSalt)); + uint256 hashRand = inv.hash ^ hashSalt; + hashRand = Hash(BEGIN(hashRand), END(hashRand)); + bool fTrickleWait = ((hashRand & 3) != 0); + + // always trickle our own transactions + if (!fTrickleWait) + { + TRY_CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(inv.hash); + if (mi != mapWallet.end()) + { + CWalletTx& wtx = (*mi).second; + if (wtx.fFromMe) + fTrickleWait = true; + } + } + } + + if (fTrickleWait) + { + vInvWait.push_back(inv); + continue; + } + } + + // returns true if wasn't already contained in the set + if (pto->setInventoryKnown.insert(inv).second) + { + vInv.push_back(inv); + if (vInv.size() >= 1000) + { + pto->PushMessage("inv", vInv); + vInv.clear(); + } + } + } + pto->vInventoryToSend = vInvWait; + } + if (!vInv.empty()) + pto->PushMessage("inv", vInv); + + + // + // Message: getdata + // + vector vGetData; + int64 nNow = GetTime() * 1000000; + CTxDB txdb("r"); + while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) + { + const CInv& inv = (*pto->mapAskFor.begin()).second; + if (!AlreadyHave(txdb, inv)) + { + printf("sending getdata: %s\n", inv.ToString().c_str()); + vGetData.push_back(inv); + if (vGetData.size() >= 1000) + { + pto->PushMessage("getdata", vGetData); + vGetData.clear(); + } + } + pto->mapAskFor.erase(pto->mapAskFor.begin()); + } + if (!vGetData.empty()) + pto->PushMessage("getdata", vGetData); + + } + return true; +} + + + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// BitcoinMiner +// + +void GenerateBitcoins(bool fGenerate) +{ + if (fGenerateBitcoins != fGenerate) + { + fGenerateBitcoins = fGenerate; + CWalletDB().WriteSetting("fGenerateBitcoins", fGenerateBitcoins); + MainFrameRepaint(); + } + if (fGenerateBitcoins) + { + int nProcessors = boost::thread::hardware_concurrency(); + printf("%d processors\n", nProcessors); + if (nProcessors < 1) + nProcessors = 1; + if (fLimitProcessors && nProcessors > nLimitProcessors) + nProcessors = nLimitProcessors; + int nAddThreads = nProcessors - vnThreadsRunning[3]; + printf("Starting %d BitcoinMiner threads\n", nAddThreads); + for (int i = 0; i < nAddThreads; i++) + { + if (!CreateThread(ThreadBitcoinMiner, NULL)) + printf("Error: CreateThread(ThreadBitcoinMiner) failed\n"); + Sleep(10); + } + } +} + +void ThreadBitcoinMiner(void* parg) +{ + try + { + vnThreadsRunning[3]++; + BitcoinMiner(); + vnThreadsRunning[3]--; + } + catch (std::exception& e) { + vnThreadsRunning[3]--; + PrintException(&e, "ThreadBitcoinMiner()"); + } catch (...) { + vnThreadsRunning[3]--; + PrintException(NULL, "ThreadBitcoinMiner()"); + } + UIThreadCall(boost::bind(CalledSetStatusBar, "", 0)); + nHPSTimerStart = 0; + if (vnThreadsRunning[3] == 0) + dHashesPerSec = 0; + printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[3]); +} + + +int FormatHashBlocks(void* pbuffer, unsigned int len) +{ + unsigned char* pdata = (unsigned char*)pbuffer; + unsigned int blocks = 1 + ((len + 8) / 64); + unsigned char* pend = pdata + 64 * blocks; + memset(pdata + len, 0, 64 * blocks - len); + pdata[len] = 0x80; + unsigned int bits = len * 8; + pend[-1] = (bits >> 0) & 0xff; + pend[-2] = (bits >> 8) & 0xff; + pend[-3] = (bits >> 16) & 0xff; + pend[-4] = (bits >> 24) & 0xff; + return blocks; +} + +using CryptoPP::ByteReverse; + +static const unsigned int pSHA256InitState[8] = +{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + +inline void SHA256Transform(void* pstate, void* pinput, const void* pinit) +{ + memcpy(pstate, pinit, 32); + CryptoPP::SHA256::Transform((CryptoPP::word32*)pstate, (CryptoPP::word32*)pinput); +} + +// +// ScanHash scans nonces looking for a hash with at least some zero bits. +// It operates on big endian data. Caller does the byte reversing. +// All input buffers are 16-byte aligned. nNonce is usually preserved +// between calls, but periodically or if nNonce is 0xffff0000 or above, +// the block is rebuilt and nNonce starts over at zero. +// +unsigned int ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone) +{ + unsigned int& nNonce = *(unsigned int*)(pdata + 12); + for (;;) + { + // Crypto++ SHA-256 + // Hash pdata using pmidstate as the starting state into + // preformatted buffer phash1, then hash phash1 into phash + nNonce++; + SHA256Transform(phash1, pdata, pmidstate); + SHA256Transform(phash, phash1, pSHA256InitState); + + // Return the nonce if the hash has at least some zero bits, + // caller will check if it has enough to reach the target + if (((unsigned short*)phash)[14] == 0) + return nNonce; + + // If nothing found after trying for a while, return -1 + if ((nNonce & 0xffff) == 0) + { + nHashesDone = 0xffff+1; + return -1; + } + } +} + + +class COrphan +{ +public: + CTransaction* ptx; + set setDependsOn; + double dPriority; + + COrphan(CTransaction* ptxIn) + { + ptx = ptxIn; + dPriority = 0; + } + + void print() const + { + printf("COrphan(hash=%s, dPriority=%.1f)\n", ptx->GetHash().ToString().substr(0,10).c_str(), dPriority); + BOOST_FOREACH(uint256 hash, setDependsOn) + printf(" setDependsOn %s\n", hash.ToString().substr(0,10).c_str()); + } +}; + + +CBlock* CreateNewBlock(CReserveKey& reservekey) +{ + CBlockIndex* pindexPrev = pindexBest; + + // Create new block + auto_ptr pblock(new CBlock()); + if (!pblock.get()) + return NULL; + + // Create coinbase tx + CTransaction txNew; + txNew.vin.resize(1); + txNew.vin[0].prevout.SetNull(); + txNew.vout.resize(1); + txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG; + + // Add our coinbase tx as first transaction + pblock->vtx.push_back(txNew); + + // Collect memory pool transactions into the block + int64 nFees = 0; + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapTransactions) + { + CTxDB txdb("r"); + + // Priority order to process transactions + list vOrphan; // list memory doesn't move + map > mapDependers; + multimap mapPriority; + for (map::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi) + { + CTransaction& tx = (*mi).second; + if (tx.IsCoinBase() || !tx.IsFinal()) + continue; + + COrphan* porphan = NULL; + double dPriority = 0; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + // Read prev transaction + CTransaction txPrev; + CTxIndex txindex; + if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) + { + // Has to wait for dependencies + if (!porphan) + { + // Use list for automatic deletion + vOrphan.push_back(COrphan(&tx)); + porphan = &vOrphan.back(); + } + mapDependers[txin.prevout.hash].push_back(porphan); + porphan->setDependsOn.insert(txin.prevout.hash); + continue; + } + int64 nValueIn = txPrev.vout[txin.prevout.n].nValue; + + // Read block header + int nConf = txindex.GetDepthInMainChain(); + + dPriority += (double)nValueIn * nConf; + + if (fDebug && GetBoolArg("-printpriority")) + printf("priority nValueIn=%-12I64d nConf=%-5d dPriority=%-20.1f\n", nValueIn, nConf, dPriority); + } + + // Priority is sum(valuein * age) / txsize + dPriority /= ::GetSerializeSize(tx, SER_NETWORK); + + if (porphan) + porphan->dPriority = dPriority; + else + mapPriority.insert(make_pair(-dPriority, &(*mi).second)); + + if (fDebug && GetBoolArg("-printpriority")) + { + printf("priority %-20.1f %s\n%s", dPriority, tx.GetHash().ToString().substr(0,10).c_str(), tx.ToString().c_str()); + if (porphan) + porphan->print(); + printf("\n"); + } + } + + // Collect transactions into block + map mapTestPool; + uint64 nBlockSize = 1000; + int nBlockSigOps = 100; + while (!mapPriority.empty()) + { + // Take highest priority transaction off priority queue + double dPriority = -(*mapPriority.begin()).first; + CTransaction& tx = *(*mapPriority.begin()).second; + mapPriority.erase(mapPriority.begin()); + + // Size limits + unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK); + if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN) + continue; + int nTxSigOps = tx.GetSigOpCount(); + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) + continue; + + // Transaction fee required depends on block size + bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority)); + int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, true); + + // Connecting shouldn't fail due to dependency on other memory pool transactions + // because we're already processing them in order of dependency + map mapTestPoolTmp(mapTestPool); + if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee)) + continue; + swap(mapTestPool, mapTestPoolTmp); + + // Added + pblock->vtx.push_back(tx); + nBlockSize += nTxSize; + nBlockSigOps += nTxSigOps; + + // Add transactions that depend on this one to the priority queue + uint256 hash = tx.GetHash(); + if (mapDependers.count(hash)) + { + BOOST_FOREACH(COrphan* porphan, mapDependers[hash]) + { + if (!porphan->setDependsOn.empty()) + { + porphan->setDependsOn.erase(hash); + if (porphan->setDependsOn.empty()) + mapPriority.insert(make_pair(-porphan->dPriority, porphan->ptx)); + } + } + } + } + } + pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); + + // Fill in header + pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->nBits = GetNextWorkRequired(pindexPrev); + pblock->nNonce = 0; + + return pblock.release(); +} + + +void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, int64& nPrevTime) +{ + // Update nExtraNonce + int64 nNow = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + if (++nExtraNonce >= 0x7f && nNow > nPrevTime+1) + { + nExtraNonce = 1; + nPrevTime = nNow; + } + pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); +} + + +void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1) +{ + // + // Prebuild hash buffers + // + struct + { + struct unnamed2 + { + int nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + } + block; + unsigned char pchPadding0[64]; + uint256 hash1; + unsigned char pchPadding1[64]; + } + tmp; + memset(&tmp, 0, sizeof(tmp)); + + tmp.block.nVersion = pblock->nVersion; + tmp.block.hashPrevBlock = pblock->hashPrevBlock; + tmp.block.hashMerkleRoot = pblock->hashMerkleRoot; + tmp.block.nTime = pblock->nTime; + tmp.block.nBits = pblock->nBits; + tmp.block.nNonce = pblock->nNonce; + + FormatHashBlocks(&tmp.block, sizeof(tmp.block)); + FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1)); + + // Byte swap all the input buffer + for (int i = 0; i < sizeof(tmp)/4; i++) + ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]); + + // Precalc the first half of the first hash, which stays constant + SHA256Transform(pmidstate, &tmp.block, pSHA256InitState); + + memcpy(pdata, &tmp.block, 128); + memcpy(phash1, &tmp.hash1, 64); +} + + +bool CheckWork(CBlock* pblock, CReserveKey& reservekey) +{ + uint256 hash = pblock->GetHash(); + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + if (hash > hashTarget) + return false; + + //// debug print + printf("BitcoinMiner:\n"); + printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); + pblock->print(); + printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str()); + printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); + + // Found a solution + CRITICAL_BLOCK(cs_main) + { + if (pblock->hashPrevBlock != hashBestChain) + return error("BitcoinMiner : generated block is stale"); + + // Remove key from key pool + reservekey.KeepKey(); + + // Track how many getdata requests this block gets + CRITICAL_BLOCK(cs_mapRequestCount) + mapRequestCount[pblock->GetHash()] = 0; + + // Process this block the same as if we had received it from another node + if (!ProcessBlock(NULL, pblock)) + return error("BitcoinMiner : ProcessBlock, block not accepted"); + } + + Sleep(2000); + return true; +} + + +void BitcoinMiner() +{ + printf("BitcoinMiner started\n"); + SetThreadPriority(THREAD_PRIORITY_LOWEST); + + // Each thread has its own key and counter + CReserveKey reservekey; + unsigned int nExtraNonce = 0; + int64 nPrevTime = 0; + + while (fGenerateBitcoins) + { + if (AffinityBugWorkaround(ThreadBitcoinMiner)) + return; + if (fShutdown) + return; + while (vNodes.empty() || IsInitialBlockDownload()) + { + Sleep(1000); + if (fShutdown) + return; + if (!fGenerateBitcoins) + return; + } + + + // + // Create new block + // + unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; + CBlockIndex* pindexPrev = pindexBest; + + auto_ptr pblock(CreateNewBlock(reservekey)); + if (!pblock.get()) + return; + IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce, nPrevTime); + + printf("Running BitcoinMiner with %d transactions in block\n", pblock->vtx.size()); + + + // + // Prebuild hash buffers + // + char pmidstatebuf[32+16]; char* pmidstate = alignup<16>(pmidstatebuf); + char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf); + char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf); + + FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1); + + unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4); + unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12); + + + // + // Search + // + int64 nStart = GetTime(); + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + uint256 hashbuf[2]; + uint256& hash = *alignup<16>(hashbuf); + loop + { + unsigned int nHashesDone = 0; + unsigned int nNonceFound; + + // Crypto++ SHA-256 + nNonceFound = ScanHash_CryptoPP(pmidstate, pdata + 64, phash1, + (char*)&hash, nHashesDone); + + // Check if something found + if (nNonceFound != -1) + { + for (int i = 0; i < sizeof(hash)/4; i++) + ((unsigned int*)&hash)[i] = ByteReverse(((unsigned int*)&hash)[i]); + + if (hash <= hashTarget) + { + // Found a solution + pblock->nNonce = ByteReverse(nNonceFound); + assert(hash == pblock->GetHash()); + + SetThreadPriority(THREAD_PRIORITY_NORMAL); + CheckWork(pblock.get(), reservekey); + SetThreadPriority(THREAD_PRIORITY_LOWEST); + break; + } + } + + // Meter hashes/sec + static int64 nHashCounter; + if (nHPSTimerStart == 0) + { + nHPSTimerStart = GetTimeMillis(); + nHashCounter = 0; + } + else + nHashCounter += nHashesDone; + if (GetTimeMillis() - nHPSTimerStart > 4000) + { + static CCriticalSection cs; + CRITICAL_BLOCK(cs) + { + if (GetTimeMillis() - nHPSTimerStart > 4000) + { + dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart); + nHPSTimerStart = GetTimeMillis(); + nHashCounter = 0; + string strStatus = strprintf(" %.0f khash/s", dHashesPerSec/1000.0); + UIThreadCall(boost::bind(CalledSetStatusBar, strStatus, 0)); + static int64 nLogTime; + if (GetTime() - nLogTime > 30 * 60) + { + nLogTime = GetTime(); + printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str()); + printf("hashmeter %3d CPUs %6.0f khash/s\n", vnThreadsRunning[3], dHashesPerSec/1000.0); + } + } + } + } + + // Check for stop or if block needs to be rebuilt + if (fShutdown) + return; + if (!fGenerateBitcoins) + return; + if (fLimitProcessors && vnThreadsRunning[3] > nLimitProcessors) + return; + if (vNodes.empty()) + break; + if (nBlockNonce >= 0xffff0000) + break; + if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60) + break; + if (pindexPrev != pindexBest) + break; + + // Update nTime every few seconds + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + nBlockTime = ByteReverse(pblock->nTime); + } + } +} + + + + + + + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Actions +// + + +int64 GetBalance() +{ + int64 nStart = GetTimeMillis(); + + int64 nTotal = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* pcoin = &(*it).second; + if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + continue; + nTotal += pcoin->GetAvailableCredit(); + } + } + + //printf("GetBalance() %"PRI64d"ms\n", GetTimeMillis() - nStart); + return nTotal; +} + + +bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) +{ + setCoinsRet.clear(); + nValueRet = 0; + + // List of values less than target + pair > coinLowestLarger; + coinLowestLarger.first = INT64_MAX; + coinLowestLarger.second.first = NULL; + vector > > vValue; + int64 nTotalLower = 0; + + CRITICAL_BLOCK(cs_mapWallet) + { + vector vCoins; + vCoins.reserve(mapWallet.size()); + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + vCoins.push_back(&(*it).second); + random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + + BOOST_FOREACH(CWalletTx* pcoin, vCoins) + { + if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) + continue; + + for (int i = 0; i < pcoin->vout.size(); i++) + { + if (pcoin->IsSpent(i) || !pcoin->vout[i].IsMine()) + continue; + + int64 n = pcoin->vout[i].nValue; + + if (n <= 0) + continue; + + pair > coin = make_pair(n,make_pair(pcoin,i)); + + if (n == nTargetValue) + { + setCoinsRet.insert(coin.second); + nValueRet += coin.first; + return true; + } + else if (n < nTargetValue + CENT) + { + vValue.push_back(coin); + nTotalLower += n; + } + else if (n < coinLowestLarger.first) + { + coinLowestLarger = coin; + } + } + } + } + + if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT) + { + for (int i = 0; i < vValue.size(); ++i) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + return true; + } + + if (nTotalLower < nTargetValue + (coinLowestLarger.second.first ? CENT : 0)) + { + if (coinLowestLarger.second.first == NULL) + return false; + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + return true; + } + + if (nTotalLower >= nTargetValue + CENT) + nTargetValue += CENT; + + // Solve subset sum by stochastic approximation + sort(vValue.rbegin(), vValue.rend()); + vector vfIncluded; + vector vfBest(vValue.size(), true); + int64 nBest = nTotalLower; + + for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++) + { + vfIncluded.assign(vValue.size(), false); + int64 nTotal = 0; + bool fReachedTarget = false; + for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) + { + for (int i = 0; i < vValue.size(); i++) + { + if (nPass == 0 ? rand() % 2 : !vfIncluded[i]) + { + nTotal += vValue[i].first; + vfIncluded[i] = true; + if (nTotal >= nTargetValue) + { + fReachedTarget = true; + if (nTotal < nBest) + { + nBest = nTotal; + vfBest = vfIncluded; + } + nTotal -= vValue[i].first; + vfIncluded[i] = false; + } + } + } + } + } + + // If the next larger is still closer, return it + if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue) + { + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + } + else { + for (int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + + //// debug print + printf("SelectCoins() best subset: "); + for (int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + printf("%s ", FormatMoney(vValue[i].first).c_str()); + printf("total %s\n", FormatMoney(nBest).c_str()); + } + + return true; +} + +bool SelectCoins(int64 nTargetValue, set >& setCoinsRet, int64& nValueRet) +{ + return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet)); +} + + + + +bool CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +{ + int64 nValue = 0; + BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + { + if (nValue < 0) + return false; + nValue += s.second; + } + if (vecSend.empty() || nValue < 0) + return false; + + CRITICAL_BLOCK(cs_main) + { + // txdb must be opened before the mapWallet lock + CTxDB txdb("r"); + CRITICAL_BLOCK(cs_mapWallet) + { + nFeeRet = nTransactionFee; + loop + { + wtxNew.vin.clear(); + wtxNew.vout.clear(); + wtxNew.fFromMe = true; + + int64 nTotalValue = nValue + nFeeRet; + double dPriority = 0; + // vouts to the payees + BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + wtxNew.vout.push_back(CTxOut(s.second, s.first)); + + // Choose coins to use + set > setCoins; + int64 nValueIn = 0; + if (!SelectCoins(nTotalValue, setCoins, nValueIn)) + return false; + BOOST_FOREACH(PAIRTYPE(CWalletTx*, unsigned int) pcoin, setCoins) + { + int64 nCredit = pcoin.first->vout[pcoin.second].nValue; + dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(); + } + + int64 nChange = nValueIn - nValue - nFeeRet; + + // if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE + // or until nChange becomes zero + if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT) + { + int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet); + nChange -= nMoveToFee; + nFeeRet += nMoveToFee; + } + + if (nChange > 0) + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + vector vchPubKey = reservekey.GetReservedKey(); + assert(mapKeys.count(vchPubKey)); + + // Fill a vout to ourself, using same address type as the payment + CScript scriptChange; + if (vecSend[0].first.GetBitcoinAddressHash160() != 0) + scriptChange.SetBitcoinAddress(vchPubKey); + else + scriptChange << vchPubKey << OP_CHECKSIG; + + // Insert change txn at random position: + vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); + wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); + } + else + reservekey.ReturnKey(); + + // Fill vin + BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) + wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); + + // Sign + int nIn = 0; + BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) + if (!SignSignature(*coin.first, wtxNew, nIn++)) + return false; + + // Limit size + unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK); + if (nBytes >= MAX_BLOCK_SIZE_GEN/5) + return false; + dPriority /= nBytes; + + // Check that enough fee is included + int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); + bool fAllowFree = CTransaction::AllowFree(dPriority); + int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree); + if (nFeeRet < max(nPayFee, nMinFee)) + { + nFeeRet = max(nPayFee, nMinFee); + continue; + } + + // Fill vtxPrev by copying from previous transactions vtxPrev + wtxNew.AddSupportingTransactions(txdb); + wtxNew.fTimeReceivedIsTxTime = true; + + break; + } + } + } + return true; +} + +bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +{ + vector< pair > vecSend; + vecSend.push_back(make_pair(scriptPubKey, nValue)); + return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); +} + +// Call after CreateTransaction unless you want to abort +bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) +{ + CRITICAL_BLOCK(cs_main) + { + printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); + CRITICAL_BLOCK(cs_mapWallet) + { + // This is only to keep the database open to defeat the auto-flush for the + // duration of this scope. This is the only place where this optimization + // maybe makes sense; please don't do it anywhere else. + CWalletDB walletdb("r"); + + // Take key pair from key pool so it won't be used again + reservekey.KeepKey(); + + // Add tx to wallet, because if it has change it's also ours, + // otherwise just for transaction history. + AddToWallet(wtxNew); + + // Mark old coins as spent + set setCoins; + BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) + { + CWalletTx &pcoin = mapWallet[txin.prevout.hash]; + pcoin.MarkSpent(txin.prevout.n); + pcoin.WriteToDisk(); + vWalletUpdated.push_back(pcoin.GetHash()); + } + } + + // Track how many getdata requests our transaction gets + CRITICAL_BLOCK(cs_mapRequestCount) + mapRequestCount[wtxNew.GetHash()] = 0; + + // Broadcast + if (!wtxNew.AcceptToMemoryPool()) + { + // This must not fail. The transaction has already been signed and recorded. + printf("CommitTransaction() : Error: Transaction not valid"); + return false; + } + wtxNew.RelayWalletTransaction(); + } + MainFrameRepaint(); + return true; +} + + + + +// requires cs_main lock +string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +{ + CReserveKey reservekey; + int64 nFeeRequired; + if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired)) + { + string strError; + if (nValue + nFeeRequired > 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("SendMoney() : %s", strError.c_str()); + return strError; + } + + if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL)) + return "ABORTED"; + + if (!CommitTransaction(wtxNew, reservekey)) + return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); + + MainFrameRepaint(); + return ""; +} + + + +// requires cs_main lock +string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +{ + // Check amount + if (nValue <= 0) + return _("Invalid amount"); + if (nValue + nTransactionFee > GetBalance()) + return _("Insufficient funds"); + + // Parse bitcoin address + CScript scriptPubKey; + if (!scriptPubKey.SetBitcoinAddress(strAddress)) + return _("Invalid bitcoin address"); + + return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000000..8d9b39f718 --- /dev/null +++ b/src/main.h @@ -0,0 +1,2066 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_MAIN_H +#define BITCOIN_MAIN_H + +#include "bignum.h" +#include "net.h" +#include "key.h" +#include "db.h" +#include "script.h" + +#include + +class COutPoint; +class CInPoint; +class CDiskTxPos; +class CCoinBase; +class CTxIn; +class CTxOut; +class CTransaction; +class CBlock; +class CBlockIndex; +class CWalletTx; +class CKeyItem; + +static const unsigned int MAX_BLOCK_SIZE = 1000000; +static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; +static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; +static const int64 COIN = 100000000; +static const int64 CENT = 1000000; +static const int64 MIN_TX_FEE = CENT; +static const int64 MIN_RELAY_TX_FEE = 50000; +static const int64 MAX_MONEY = 21000000 * COIN; +inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } +static const int COINBASE_MATURITY = 100; +#ifdef USE_UPNP +static const int fHaveUPnP = true; +#else +static const int fHaveUPnP = false; +#endif + + + + + + +extern CCriticalSection cs_main; +extern std::map mapBlockIndex; +extern uint256 hashGenesisBlock; +extern CBigNum bnProofOfWorkLimit; +extern CBlockIndex* pindexGenesisBlock; +extern int nBestHeight; +extern CBigNum bnBestChainWork; +extern CBigNum bnBestInvalidWork; +extern uint256 hashBestChain; +extern CBlockIndex* pindexBest; +extern unsigned int nTransactionsUpdated; +extern std::map mapRequestCount; +extern CCriticalSection cs_mapRequestCount; +extern std::map mapAddressBook; +extern CCriticalSection cs_mapAddressBook; +extern std::vector vchDefaultKey; +extern double dHashesPerSec; +extern int64 nHPSTimerStart; + +// Settings +extern int fGenerateBitcoins; +extern int64 nTransactionFee; +extern CAddress addrIncoming; +extern int fLimitProcessors; +extern int nLimitProcessors; +extern int fMinimizeToTray; +extern int fMinimizeOnClose; +extern int fUseUPnP; + + + + + + + +bool CheckDiskSpace(uint64 nAdditionalBytes=0); +FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); +FILE* AppendBlockFile(unsigned int& nFileRet); +bool AddKey(const CKey& key); +std::vector GenerateNewKey(); +bool AddToWallet(const CWalletTx& wtxIn); +void WalletUpdateSpent(const COutPoint& prevout); +int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); +void ReacceptWalletTransactions(); +bool LoadBlockIndex(bool fAllowNew=true); +void PrintBlockTree(); +bool ProcessMessages(CNode* pfrom); +bool ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vRecv); +bool SendMessages(CNode* pto, bool fSendTrickle); +int64 GetBalance(); +bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); +bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); +bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); +bool BroadcastTransaction(CWalletTx& wtxNew); +std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); +std::string SendMoneyToBitcoinAddress(std::string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); +void GenerateBitcoins(bool fGenerate); +void ThreadBitcoinMiner(void* parg); +CBlock* CreateNewBlock(CReserveKey& reservekey); +void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, int64& nPrevTime); +void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); +bool CheckWork(CBlock* pblock, CReserveKey& reservekey); +void BitcoinMiner(); +bool CheckProofOfWork(uint256 hash, unsigned int nBits); +bool IsInitialBlockDownload(); +std::string GetWarnings(std::string strFor); + + + + + + + + + + + + +class CDiskTxPos +{ +public: + unsigned int nFile; + unsigned int nBlockPos; + unsigned int nTxPos; + + CDiskTxPos() + { + SetNull(); + } + + CDiskTxPos(unsigned int nFileIn, unsigned int nBlockPosIn, unsigned int nTxPosIn) + { + nFile = nFileIn; + nBlockPos = nBlockPosIn; + nTxPos = nTxPosIn; + } + + IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) + void SetNull() { nFile = -1; nBlockPos = 0; nTxPos = 0; } + bool IsNull() const { return (nFile == -1); } + + friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b) + { + return (a.nFile == b.nFile && + a.nBlockPos == b.nBlockPos && + a.nTxPos == b.nTxPos); + } + + friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b) + { + return !(a == b); + } + + std::string ToString() const + { + if (IsNull()) + return strprintf("null"); + else + return strprintf("(nFile=%d, nBlockPos=%d, nTxPos=%d)", nFile, nBlockPos, nTxPos); + } + + void print() const + { + printf("%s", ToString().c_str()); + } +}; + + + + +class CInPoint +{ +public: + CTransaction* ptx; + unsigned int n; + + CInPoint() { SetNull(); } + CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } + void SetNull() { ptx = NULL; n = -1; } + bool IsNull() const { return (ptx == NULL && n == -1); } +}; + + + + +class COutPoint +{ +public: + uint256 hash; + unsigned int n; + + COutPoint() { SetNull(); } + COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; } + IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) + void SetNull() { hash = 0; n = -1; } + bool IsNull() const { return (hash == 0 && n == -1); } + + friend bool operator<(const COutPoint& a, const COutPoint& b) + { + return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); + } + + friend bool operator==(const COutPoint& a, const COutPoint& b) + { + return (a.hash == b.hash && a.n == b.n); + } + + friend bool operator!=(const COutPoint& a, const COutPoint& b) + { + return !(a == b); + } + + std::string ToString() const + { + return strprintf("COutPoint(%s, %d)", hash.ToString().substr(0,10).c_str(), n); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +// +// An input of a transaction. It contains the location of the previous +// transaction's output that it claims and a signature that matches the +// output's public key. +// +class CTxIn +{ +public: + COutPoint prevout; + CScript scriptSig; + unsigned int nSequence; + + CTxIn() + { + nSequence = UINT_MAX; + } + + explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX) + { + prevout = prevoutIn; + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX) + { + prevout = COutPoint(hashPrevTx, nOut); + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(prevout); + READWRITE(scriptSig); + READWRITE(nSequence); + ) + + bool IsFinal() const + { + return (nSequence == UINT_MAX); + } + + friend bool operator==(const CTxIn& a, const CTxIn& b) + { + return (a.prevout == b.prevout && + a.scriptSig == b.scriptSig && + a.nSequence == b.nSequence); + } + + friend bool operator!=(const CTxIn& a, const CTxIn& b) + { + return !(a == b); + } + + std::string ToString() const + { + std::string str; + str += strprintf("CTxIn("); + str += prevout.ToString(); + if (prevout.IsNull()) + str += strprintf(", coinbase %s", HexStr(scriptSig).c_str()); + else + str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str()); + if (nSequence != UINT_MAX) + str += strprintf(", nSequence=%u", nSequence); + str += ")"; + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } + + bool IsMine() const; + int64 GetDebit() const; +}; + + + + +// +// An output of a transaction. It contains the public key that the next input +// must be able to sign with to claim it. +// +class CTxOut +{ +public: + int64 nValue; + CScript scriptPubKey; + + CTxOut() + { + SetNull(); + } + + CTxOut(int64 nValueIn, CScript scriptPubKeyIn) + { + nValue = nValueIn; + scriptPubKey = scriptPubKeyIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(nValue); + READWRITE(scriptPubKey); + ) + + void SetNull() + { + nValue = -1; + scriptPubKey.clear(); + } + + bool IsNull() + { + return (nValue == -1); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsMine() const + { + return ::IsMine(scriptPubKey); + } + + int64 GetCredit() const + { + if (!MoneyRange(nValue)) + throw std::runtime_error("CTxOut::GetCredit() : value out of range"); + return (IsMine() ? nValue : 0); + } + + bool IsChange() const + { + // On a debit transaction, a txout that's mine but isn't in the address book is change + std::vector vchPubKey; + if (ExtractPubKey(scriptPubKey, true, vchPubKey)) + CRITICAL_BLOCK(cs_mapAddressBook) + if (!mapAddressBook.count(PubKeyToAddress(vchPubKey))) + return true; + return false; + } + + int64 GetChange() const + { + if (!MoneyRange(nValue)) + throw std::runtime_error("CTxOut::GetChange() : value out of range"); + return (IsChange() ? nValue : 0); + } + + friend bool operator==(const CTxOut& a, const CTxOut& b) + { + return (a.nValue == b.nValue && + a.scriptPubKey == b.scriptPubKey); + } + + friend bool operator!=(const CTxOut& a, const CTxOut& b) + { + return !(a == b); + } + + std::string ToString() const + { + if (scriptPubKey.size() < 6) + return "CTxOut(error)"; + return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +// +// The basic transaction that is broadcasted on the network and contained in +// blocks. A transaction can contain multiple inputs and outputs. +// +class CTransaction +{ +public: + int nVersion; + std::vector vin; + std::vector vout; + unsigned int nLockTime; + + + CTransaction() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(vin); + READWRITE(vout); + READWRITE(nLockTime); + ) + + void SetNull() + { + nVersion = 1; + vin.clear(); + vout.clear(); + nLockTime = 0; + } + + bool IsNull() const + { + return (vin.empty() && vout.empty()); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsFinal(int nBlockHeight=0, int64 nBlockTime=0) const + { + // Time based nLockTime implemented in 0.1.6 + if (nLockTime == 0) + return true; + if (nBlockHeight == 0) + nBlockHeight = nBestHeight; + if (nBlockTime == 0) + nBlockTime = GetAdjustedTime(); + if ((int64)nLockTime < (nLockTime < 500000000 ? (int64)nBlockHeight : nBlockTime)) + return true; + BOOST_FOREACH(const CTxIn& txin, vin) + if (!txin.IsFinal()) + return false; + return true; + } + + bool IsNewerThan(const CTransaction& old) const + { + if (vin.size() != old.vin.size()) + return false; + for (int i = 0; i < vin.size(); i++) + if (vin[i].prevout != old.vin[i].prevout) + return false; + + bool fNewer = false; + unsigned int nLowest = UINT_MAX; + for (int i = 0; i < vin.size(); i++) + { + if (vin[i].nSequence != old.vin[i].nSequence) + { + if (vin[i].nSequence <= nLowest) + { + fNewer = false; + nLowest = vin[i].nSequence; + } + if (old.vin[i].nSequence < nLowest) + { + fNewer = true; + nLowest = old.vin[i].nSequence; + } + } + } + return fNewer; + } + + bool IsCoinBase() const + { + return (vin.size() == 1 && vin[0].prevout.IsNull()); + } + + int GetSigOpCount() const + { + int n = 0; + BOOST_FOREACH(const CTxIn& txin, vin) + n += txin.scriptSig.GetSigOpCount(); + BOOST_FOREACH(const CTxOut& txout, vout) + n += txout.scriptPubKey.GetSigOpCount(); + return n; + } + + bool IsStandard() const + { + BOOST_FOREACH(const CTxIn& txin, vin) + if (!txin.scriptSig.IsPushOnly()) + return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str()); + BOOST_FOREACH(const CTxOut& txout, vout) + if (!::IsStandard(txout.scriptPubKey)) + return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str()); + return true; + } + + bool IsMine() const + { + BOOST_FOREACH(const CTxOut& txout, vout) + if (txout.IsMine()) + return true; + return false; + } + + bool IsFromMe() const + { + return (GetDebit() > 0); + } + + int64 GetDebit() const + { + int64 nDebit = 0; + BOOST_FOREACH(const CTxIn& txin, vin) + { + nDebit += txin.GetDebit(); + if (!MoneyRange(nDebit)) + throw std::runtime_error("CTransaction::GetDebit() : value out of range"); + } + return nDebit; + } + + int64 GetCredit() const + { + int64 nCredit = 0; + BOOST_FOREACH(const CTxOut& txout, vout) + { + nCredit += txout.GetCredit(); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CTransaction::GetCredit() : value out of range"); + } + return nCredit; + } + + int64 GetChange() const + { + if (IsCoinBase()) + return 0; + int64 nChange = 0; + BOOST_FOREACH(const CTxOut& txout, vout) + { + nChange += txout.GetChange(); + if (!MoneyRange(nChange)) + throw std::runtime_error("CTransaction::GetChange() : value out of range"); + } + return nChange; + } + + int64 GetValueOut() const + { + int64 nValueOut = 0; + BOOST_FOREACH(const CTxOut& txout, vout) + { + nValueOut += txout.nValue; + if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) + throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); + } + return nValueOut; + } + + static bool AllowFree(double dPriority) + { + // Large (in bytes) low-priority (new, small-coin) transactions + // need a fee. + return dPriority > COIN * 144 / 250; + } + + int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, bool fForRelay=false) const + { + // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE + int64 nBaseFee = fForRelay ? MIN_RELAY_TX_FEE : MIN_TX_FEE; + + unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK); + unsigned int nNewBlockSize = nBlockSize + nBytes; + int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; + + if (fAllowFree) + { + if (nBlockSize == 1) + { + // Transactions under 10K are free + // (about 4500bc if made of 50bc inputs) + if (nBytes < 10000) + nMinFee = 0; + } + else + { + // Free transaction area + if (nNewBlockSize < 27000) + nMinFee = 0; + } + } + + // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01 + if (nMinFee < nBaseFee) + BOOST_FOREACH(const CTxOut& txout, vout) + if (txout.nValue < CENT) + nMinFee = nBaseFee; + + // Raise the price as the block approaches full + if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) + { + if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) + return MAX_MONEY; + nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); + } + + if (!MoneyRange(nMinFee)) + nMinFee = MAX_MONEY; + return nMinFee; + } + + + bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL) + { + CAutoFile filein = OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb"); + if (!filein) + return error("CTransaction::ReadFromDisk() : OpenBlockFile failed"); + + // Read transaction + if (fseek(filein, pos.nTxPos, SEEK_SET) != 0) + return error("CTransaction::ReadFromDisk() : fseek failed"); + filein >> *this; + + // Return file pointer + if (pfileRet) + { + if (fseek(filein, pos.nTxPos, SEEK_SET) != 0) + return error("CTransaction::ReadFromDisk() : second fseek failed"); + *pfileRet = filein.release(); + } + return true; + } + + friend bool operator==(const CTransaction& a, const CTransaction& b) + { + return (a.nVersion == b.nVersion && + a.vin == b.vin && + a.vout == b.vout && + a.nLockTime == b.nLockTime); + } + + friend bool operator!=(const CTransaction& a, const CTransaction& b) + { + return !(a == b); + } + + + std::string ToString() const + { + std::string str; + str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n", + GetHash().ToString().substr(0,10).c_str(), + nVersion, + vin.size(), + vout.size(), + nLockTime); + for (int i = 0; i < vin.size(); i++) + str += " " + vin[i].ToString() + "\n"; + for (int i = 0; i < vout.size(); i++) + str += " " + vout[i].ToString() + "\n"; + return str; + } + + void print() const + { + printf("%s", ToString().c_str()); + } + + + bool ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet); + bool ReadFromDisk(CTxDB& txdb, COutPoint prevout); + bool ReadFromDisk(COutPoint prevout); + bool DisconnectInputs(CTxDB& txdb); + bool ConnectInputs(CTxDB& txdb, std::map& mapTestPool, CDiskTxPos posThisTx, + CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0); + bool ClientConnectInputs(); + bool CheckTransaction() const; + bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); + bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL) + { + CTxDB txdb("r"); + return AcceptToMemoryPool(txdb, fCheckInputs, pfMissingInputs); + } +protected: + bool AddToMemoryPoolUnchecked(); +public: + bool RemoveFromMemoryPool(); +}; + + + + + +// +// A transaction with a merkle branch linking it to the block chain +// +class CMerkleTx : public CTransaction +{ +public: + uint256 hashBlock; + std::vector vMerkleBranch; + int nIndex; + + // memory only + mutable char fMerkleVerified; + + + CMerkleTx() + { + Init(); + } + + CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) + { + Init(); + } + + void Init() + { + hashBlock = 0; + nIndex = -1; + fMerkleVerified = false; + } + + + IMPLEMENT_SERIALIZE + ( + nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action); + nVersion = this->nVersion; + READWRITE(hashBlock); + READWRITE(vMerkleBranch); + READWRITE(nIndex); + ) + + + int SetMerkleBranch(const CBlock* pblock=NULL); + int GetDepthInMainChain(int& nHeightRet) const; + int GetDepthInMainChain() const { int nHeight; return GetDepthInMainChain(nHeight); } + bool IsInMainChain() const { return GetDepthInMainChain() > 0; } + int GetBlocksToMaturity() const; + bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true); + bool AcceptToMemoryPool() { CTxDB txdb("r"); return AcceptToMemoryPool(txdb); } +}; + + + + +// +// A transaction with a bunch of additional info that only the owner cares +// about. It includes any unrecorded transactions needed to link it back +// to the block chain. +// +class CWalletTx : public CMerkleTx +{ +public: + std::vector vtxPrev; + std::map mapValue; + std::vector > vOrderForm; + unsigned int fTimeReceivedIsTxTime; + unsigned int nTimeReceived; // time received by this node + char fFromMe; + std::string strFromAccount; + std::vector vfSpent; + + // memory only + mutable char fDebitCached; + mutable char fCreditCached; + mutable char fAvailableCreditCached; + mutable char fChangeCached; + mutable int64 nDebitCached; + mutable int64 nCreditCached; + mutable int64 nAvailableCreditCached; + mutable int64 nChangeCached; + + // memory only UI hints + mutable unsigned int nTimeDisplayed; + mutable int nLinesDisplayed; + mutable char fConfirmedDisplayed; + + + CWalletTx() + { + Init(); + } + + CWalletTx(const CMerkleTx& txIn) : CMerkleTx(txIn) + { + Init(); + } + + CWalletTx(const CTransaction& txIn) : CMerkleTx(txIn) + { + Init(); + } + + void Init() + { + vtxPrev.clear(); + mapValue.clear(); + vOrderForm.clear(); + fTimeReceivedIsTxTime = false; + nTimeReceived = 0; + fFromMe = false; + strFromAccount.clear(); + vfSpent.clear(); + fDebitCached = false; + fCreditCached = false; + fAvailableCreditCached = false; + fChangeCached = false; + nDebitCached = 0; + nCreditCached = 0; + nAvailableCreditCached = 0; + nChangeCached = 0; + nTimeDisplayed = 0; + nLinesDisplayed = 0; + fConfirmedDisplayed = false; + } + + IMPLEMENT_SERIALIZE + ( + CWalletTx* pthis = const_cast(this); + if (fRead) + pthis->Init(); + char fSpent = false; + + if (!fRead) + { + pthis->mapValue["fromaccount"] = pthis->strFromAccount; + + std::string str; + BOOST_FOREACH(char f, vfSpent) + { + str += (f ? '1' : '0'); + if (f) + fSpent = true; + } + pthis->mapValue["spent"] = str; + } + + nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action); + READWRITE(vtxPrev); + READWRITE(mapValue); + READWRITE(vOrderForm); + READWRITE(fTimeReceivedIsTxTime); + READWRITE(nTimeReceived); + READWRITE(fFromMe); + READWRITE(fSpent); + + if (fRead) + { + pthis->strFromAccount = pthis->mapValue["fromaccount"]; + + if (mapValue.count("spent")) + BOOST_FOREACH(char c, pthis->mapValue["spent"]) + pthis->vfSpent.push_back(c != '0'); + else + pthis->vfSpent.assign(vout.size(), fSpent); + } + + pthis->mapValue.erase("fromaccount"); + pthis->mapValue.erase("version"); + pthis->mapValue.erase("spent"); + ) + + // marks certain txout's as spent + // returns true if any update took place + bool UpdateSpent(const std::vector& vfNewSpent) + { + bool fReturn = false; + for (int i=0; i < vfNewSpent.size(); i++) + { + if (i == vfSpent.size()) + break; + + if (vfNewSpent[i] && !vfSpent[i]) + { + vfSpent[i] = true; + fReturn = true; + fAvailableCreditCached = false; + } + } + return fReturn; + } + + void MarkDirty() + { + fCreditCached = false; + fAvailableCreditCached = false; + fDebitCached = false; + fChangeCached = false; + } + + void MarkSpent(unsigned int nOut) + { + if (nOut >= vout.size()) + throw std::runtime_error("CWalletTx::MarkSpent() : nOut out of range"); + vfSpent.resize(vout.size()); + if (!vfSpent[nOut]) + { + vfSpent[nOut] = true; + fAvailableCreditCached = false; + } + } + + bool IsSpent(unsigned int nOut) const + { + if (nOut >= vout.size()) + throw std::runtime_error("CWalletTx::IsSpent() : nOut out of range"); + if (nOut >= vfSpent.size()) + return false; + return (!!vfSpent[nOut]); + } + + int64 GetDebit() const + { + if (vin.empty()) + return 0; + if (fDebitCached) + return nDebitCached; + nDebitCached = CTransaction::GetDebit(); + fDebitCached = true; + return nDebitCached; + } + + int64 GetCredit(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + // GetBalance can assume transactions in mapWallet won't change + if (fUseCache && fCreditCached) + return nCreditCached; + nCreditCached = CTransaction::GetCredit(); + fCreditCached = true; + return nCreditCached; + } + + int64 GetAvailableCredit(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableCreditCached) + return nAvailableCreditCached; + + int64 nCredit = 0; + for (int i = 0; i < vout.size(); i++) + { + if (!IsSpent(i)) + { + const CTxOut &txout = vout[i]; + nCredit += txout.GetCredit(); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableCreditCached = nCredit; + fAvailableCreditCached = true; + return nCredit; + } + + + int64 GetChange() const + { + if (fChangeCached) + return nChangeCached; + nChangeCached = CTransaction::GetChange(); + fChangeCached = true; + return nChangeCached; + } + + void GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, std::list >& listReceived, + std::list >& listSent, int64& nFee, std::string& strSentAccount) const; + + void GetAccountAmounts(const std::string& strAccount, int64& nGenerated, int64& nReceived, + int64& nSent, int64& nFee) const; + + bool IsFromMe() const + { + return (GetDebit() > 0); + } + + bool IsConfirmed() const + { + // Quick answer in most cases + if (!IsFinal()) + return false; + if (GetDepthInMainChain() >= 1) + return true; + if (!IsFromMe()) // using wtx's cached debit + return false; + + // If no confirmations but it's from us, we can still + // consider it confirmed if all dependencies are confirmed + std::map mapPrev; + std::vector vWorkQueue; + vWorkQueue.reserve(vtxPrev.size()+1); + vWorkQueue.push_back(this); + for (int i = 0; i < vWorkQueue.size(); i++) + { + const CMerkleTx* ptx = vWorkQueue[i]; + + if (!ptx->IsFinal()) + return false; + if (ptx->GetDepthInMainChain() >= 1) + continue; + if (!ptx->IsFromMe()) + return false; + + if (mapPrev.empty()) + BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) + mapPrev[tx.GetHash()] = &tx; + + BOOST_FOREACH(const CTxIn& txin, ptx->vin) + { + if (!mapPrev.count(txin.prevout.hash)) + return false; + vWorkQueue.push_back(mapPrev[txin.prevout.hash]); + } + } + return true; + } + + bool WriteToDisk() + { + return CWalletDB().WriteTx(GetHash(), *this); + } + + + int64 GetTxTime() const; + int GetRequestCount() const; + + void AddSupportingTransactions(CTxDB& txdb); + + bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true); + bool AcceptWalletTransaction() { CTxDB txdb("r"); return AcceptWalletTransaction(txdb); } + + void RelayWalletTransaction(CTxDB& txdb); + void RelayWalletTransaction() { CTxDB txdb("r"); RelayWalletTransaction(txdb); } +}; + + + + +// +// A txdb record that contains the disk location of a transaction and the +// locations of transactions that spend its outputs. vSpent is really only +// used as a flag, but having the location is very helpful for debugging. +// +class CTxIndex +{ +public: + CDiskTxPos pos; + std::vector vSpent; + + CTxIndex() + { + SetNull(); + } + + CTxIndex(const CDiskTxPos& posIn, unsigned int nOutputs) + { + pos = posIn; + vSpent.resize(nOutputs); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(pos); + READWRITE(vSpent); + ) + + void SetNull() + { + pos.SetNull(); + vSpent.clear(); + } + + bool IsNull() + { + return pos.IsNull(); + } + + friend bool operator==(const CTxIndex& a, const CTxIndex& b) + { + return (a.pos == b.pos && + a.vSpent == b.vSpent); + } + + friend bool operator!=(const CTxIndex& a, const CTxIndex& b) + { + return !(a == b); + } + int GetDepthInMainChain() const; +}; + + + + + +// +// Nodes collect new transactions into a block, hash them into a hash tree, +// and scan through nonce values to make the block's hash satisfy proof-of-work +// requirements. When they solve the proof-of-work, they broadcast the block +// to everyone and the block is added to the block chain. The first transaction +// in the block is a special one that creates a new coin owned by the creator +// of the block. +// +// Blocks are appended to blk0001.dat files on disk. Their location on disk +// is indexed by CBlockIndex objects in memory. +// +class CBlock +{ +public: + // header + int nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + + // network and disk + std::vector vtx; + + // memory only + mutable std::vector vMerkleTree; + + + CBlock() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(hashPrevBlock); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + + // ConnectBlock depends on vtx being last so it can calculate offset + if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY))) + READWRITE(vtx); + else if (fRead) + const_cast(this)->vtx.clear(); + ) + + void SetNull() + { + nVersion = 1; + hashPrevBlock = 0; + hashMerkleRoot = 0; + nTime = 0; + nBits = 0; + nNonce = 0; + vtx.clear(); + vMerkleTree.clear(); + } + + bool IsNull() const + { + return (nBits == 0); + } + + uint256 GetHash() const + { + return Hash(BEGIN(nVersion), END(nNonce)); + } + + int64 GetBlockTime() const + { + return (int64)nTime; + } + + int GetSigOpCount() const + { + int n = 0; + BOOST_FOREACH(const CTransaction& tx, vtx) + n += tx.GetSigOpCount(); + return n; + } + + + uint256 BuildMerkleTree() const + { + vMerkleTree.clear(); + BOOST_FOREACH(const CTransaction& tx, vtx) + vMerkleTree.push_back(tx.GetHash()); + int j = 0; + for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + for (int i = 0; i < nSize; i += 2) + { + int i2 = std::min(i+1, nSize-1); + vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), + BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); + } + j += nSize; + } + return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); + } + + std::vector GetMerkleBranch(int nIndex) const + { + if (vMerkleTree.empty()) + BuildMerkleTree(); + std::vector vMerkleBranch; + int j = 0; + for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + int i = std::min(nIndex^1, nSize-1); + vMerkleBranch.push_back(vMerkleTree[j+i]); + nIndex >>= 1; + j += nSize; + } + return vMerkleBranch; + } + + static uint256 CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) + { + if (nIndex == -1) + return 0; + BOOST_FOREACH(const uint256& otherside, vMerkleBranch) + { + if (nIndex & 1) + hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); + else + hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); + nIndex >>= 1; + } + return hash; + } + + + bool WriteToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet) + { + // Open history file to append + CAutoFile fileout = AppendBlockFile(nFileRet); + if (!fileout) + return error("CBlock::WriteToDisk() : AppendBlockFile failed"); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(*this); + fileout << FLATDATA(pchMessageStart) << nSize; + + // Write block + nBlockPosRet = ftell(fileout); + if (nBlockPosRet == -1) + return error("CBlock::WriteToDisk() : ftell failed"); + fileout << *this; + + // Flush stdio buffers and commit to disk before returning + fflush(fileout); + if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0) + { +#ifdef __WXMSW__ + _commit(_fileno(fileout)); +#else + fsync(fileno(fileout)); +#endif + } + + return true; + } + + bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true) + { + SetNull(); + + // Open history file to read + CAutoFile filein = OpenBlockFile(nFile, nBlockPos, "rb"); + if (!filein) + return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); + if (!fReadTransactions) + filein.nType |= SER_BLOCKHEADERONLY; + + // Read block + filein >> *this; + + // Check the header + if (!CheckProofOfWork(GetHash(), nBits)) + return error("CBlock::ReadFromDisk() : errors in block header"); + + return true; + } + + + + void print() const + { + printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d)\n", + GetHash().ToString().substr(0,20).c_str(), + nVersion, + hashPrevBlock.ToString().substr(0,20).c_str(), + hashMerkleRoot.ToString().substr(0,10).c_str(), + nTime, nBits, nNonce, + vtx.size()); + for (int i = 0; i < vtx.size(); i++) + { + printf(" "); + vtx[i].print(); + } + printf(" vMerkleTree: "); + for (int i = 0; i < vMerkleTree.size(); i++) + printf("%s ", vMerkleTree[i].ToString().substr(0,10).c_str()); + printf("\n"); + } + + + bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex); + bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex); + bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true); + bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew); + bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); + bool CheckBlock() const; + bool AcceptBlock(); +}; + + + + + + +// +// The block chain is a tree shaped structure starting with the +// genesis block at the root, with each block potentially having multiple +// candidates to be the next block. pprev and pnext link a path through the +// main/longest chain. A blockindex may have multiple pprev pointing back +// to it, but pnext will only point forward to the longest branch, or will +// be null if the block is not part of the longest chain. +// +class CBlockIndex +{ +public: + const uint256* phashBlock; + CBlockIndex* pprev; + CBlockIndex* pnext; + unsigned int nFile; + unsigned int nBlockPos; + int nHeight; + CBigNum bnChainWork; + + // block header + int nVersion; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + + + CBlockIndex() + { + phashBlock = NULL; + pprev = NULL; + pnext = NULL; + nFile = 0; + nBlockPos = 0; + nHeight = 0; + bnChainWork = 0; + + nVersion = 0; + hashMerkleRoot = 0; + nTime = 0; + nBits = 0; + nNonce = 0; + } + + CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block) + { + phashBlock = NULL; + pprev = NULL; + pnext = NULL; + nFile = nFileIn; + nBlockPos = nBlockPosIn; + nHeight = 0; + bnChainWork = 0; + + nVersion = block.nVersion; + hashMerkleRoot = block.hashMerkleRoot; + nTime = block.nTime; + nBits = block.nBits; + nNonce = block.nNonce; + } + + CBlock GetBlockHeader() const + { + CBlock block; + block.nVersion = nVersion; + if (pprev) + block.hashPrevBlock = pprev->GetBlockHash(); + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block; + } + + uint256 GetBlockHash() const + { + return *phashBlock; + } + + int64 GetBlockTime() const + { + return (int64)nTime; + } + + CBigNum GetBlockWork() const + { + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + if (bnTarget <= 0) + return 0; + return (CBigNum(1)<<256) / (bnTarget+1); + } + + bool IsInMainChain() const + { + return (pnext || this == pindexBest); + } + + bool CheckIndex() const + { + return CheckProofOfWork(GetBlockHash(), nBits); + } + + bool EraseBlockFromDisk() + { + // Open history file + CAutoFile fileout = OpenBlockFile(nFile, nBlockPos, "rb+"); + if (!fileout) + return false; + + // Overwrite with empty null block + CBlock block; + block.SetNull(); + fileout << block; + + return true; + } + + enum { nMedianTimeSpan=11 }; + + int64 GetMedianTimePast() const + { + int64 pmedian[nMedianTimeSpan]; + int64* pbegin = &pmedian[nMedianTimeSpan]; + int64* pend = &pmedian[nMedianTimeSpan]; + + const CBlockIndex* pindex = this; + for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev) + *(--pbegin) = pindex->GetBlockTime(); + + std::sort(pbegin, pend); + return pbegin[(pend - pbegin)/2]; + } + + int64 GetMedianTime() const + { + const CBlockIndex* pindex = this; + for (int i = 0; i < nMedianTimeSpan/2; i++) + { + if (!pindex->pnext) + return GetBlockTime(); + pindex = pindex->pnext; + } + return pindex->GetMedianTimePast(); + } + + + + std::string ToString() const + { + return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, merkle=%s, hashBlock=%s)", + pprev, pnext, nFile, nBlockPos, nHeight, + hashMerkleRoot.ToString().substr(0,10).c_str(), + GetBlockHash().ToString().substr(0,20).c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + +// +// Used to marshal pointers into hashes for db storage. +// +class CDiskBlockIndex : public CBlockIndex +{ +public: + uint256 hashPrev; + uint256 hashNext; + + CDiskBlockIndex() + { + hashPrev = 0; + hashNext = 0; + } + + explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) + { + hashPrev = (pprev ? pprev->GetBlockHash() : 0); + hashNext = (pnext ? pnext->GetBlockHash() : 0); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + + READWRITE(hashNext); + READWRITE(nFile); + READWRITE(nBlockPos); + READWRITE(nHeight); + + // block header + READWRITE(this->nVersion); + READWRITE(hashPrev); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + ) + + uint256 GetBlockHash() const + { + CBlock block; + block.nVersion = nVersion; + block.hashPrevBlock = hashPrev; + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block.GetHash(); + } + + + std::string ToString() const + { + std::string str = "CDiskBlockIndex("; + str += CBlockIndex::ToString(); + str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)", + GetBlockHash().ToString().c_str(), + hashPrev.ToString().substr(0,20).c_str(), + hashNext.ToString().substr(0,20).c_str()); + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + + + + + +// +// Describes a place in the block chain to another node such that if the +// other node doesn't have the same branch, it can find a recent common trunk. +// The further back it is, the further before the fork it may be. +// +class CBlockLocator +{ +protected: + std::vector vHave; +public: + + CBlockLocator() + { + } + + explicit CBlockLocator(const CBlockIndex* pindex) + { + Set(pindex); + } + + explicit CBlockLocator(uint256 hashBlock) + { + std::map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end()) + Set((*mi).second); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vHave); + ) + + void SetNull() + { + vHave.clear(); + } + + bool IsNull() + { + return vHave.empty(); + } + + void Set(const CBlockIndex* pindex) + { + vHave.clear(); + int nStep = 1; + while (pindex) + { + vHave.push_back(pindex->GetBlockHash()); + + // Exponentially larger steps back + for (int i = 0; pindex && i < nStep; i++) + pindex = pindex->pprev; + if (vHave.size() > 10) + nStep *= 2; + } + vHave.push_back(hashGenesisBlock); + } + + int GetDistanceBack() + { + // Retrace how far back it was in the sender's branch + int nDistance = 0; + int nStep = 1; + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return nDistance; + } + nDistance += nStep; + if (nDistance > 10) + nStep *= 2; + } + return nDistance; + } + + CBlockIndex* GetBlockIndex() + { + // Find the first block the caller has in the main chain + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return pindex; + } + } + return pindexGenesisBlock; + } + + uint256 GetBlockHash() + { + // Find the first block the caller has in the main chain + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return hash; + } + } + return hashGenesisBlock; + } + + int GetHeight() + { + CBlockIndex* pindex = GetBlockIndex(); + if (!pindex) + return 0; + return pindex->nHeight; + } +}; + + + + + + +// +// Private key that includes an expiration date in case it never gets used. +// +class CWalletKey +{ +public: + CPrivKey vchPrivKey; + int64 nTimeCreated; + int64 nTimeExpires; + std::string strComment; + //// todo: add something to note what created it (user, getnewaddress, change) + //// maybe should have a map property map + + CWalletKey(int64 nExpires=0) + { + nTimeCreated = (nExpires ? GetTime() : 0); + nTimeExpires = nExpires; + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPrivKey); + READWRITE(nTimeCreated); + READWRITE(nTimeExpires); + READWRITE(strComment); + ) +}; + + + + + + +// +// Account information. +// Stored in wallet with key "acc"+string account name +// +class CAccount +{ +public: + std::vector vchPubKey; + + CAccount() + { + SetNull(); + } + + void SetNull() + { + vchPubKey.clear(); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPubKey); + ) +}; + + + +// +// Internal transfers. +// Database key is acentry +// +class CAccountingEntry +{ +public: + std::string strAccount; + int64 nCreditDebit; + int64 nTime; + std::string strOtherAccount; + std::string strComment; + + CAccountingEntry() + { + SetNull(); + } + + void SetNull() + { + nCreditDebit = 0; + nTime = 0; + strAccount.clear(); + strOtherAccount.clear(); + strComment.clear(); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + // Note: strAccount is serialized as part of the key, not here. + READWRITE(nCreditDebit); + READWRITE(nTime); + READWRITE(strOtherAccount); + READWRITE(strComment); + ) +}; + + + + + + + + + +// +// Alerts are for notifying old versions if they become too obsolete and +// need to upgrade. The message is displayed in the status bar. +// Alert messages are broadcast as a vector of signed data. Unserializing may +// not read the entire buffer if the alert is for a newer version, but older +// versions can still relay the original data. +// +class CUnsignedAlert +{ +public: + int nVersion; + int64 nRelayUntil; // when newer nodes stop relaying to newer nodes + int64 nExpiration; + int nID; + int nCancel; + std::set setCancel; + int nMinVer; // lowest version inclusive + int nMaxVer; // highest version inclusive + std::set setSubVer; // empty matches all + int nPriority; + + // Actions + std::string strComment; + std::string strStatusBar; + std::string strReserved; + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(nRelayUntil); + READWRITE(nExpiration); + READWRITE(nID); + READWRITE(nCancel); + READWRITE(setCancel); + READWRITE(nMinVer); + READWRITE(nMaxVer); + READWRITE(setSubVer); + READWRITE(nPriority); + + READWRITE(strComment); + READWRITE(strStatusBar); + READWRITE(strReserved); + ) + + void SetNull() + { + nVersion = 1; + nRelayUntil = 0; + nExpiration = 0; + nID = 0; + nCancel = 0; + setCancel.clear(); + nMinVer = 0; + nMaxVer = 0; + setSubVer.clear(); + nPriority = 0; + + strComment.clear(); + strStatusBar.clear(); + strReserved.clear(); + } + + std::string ToString() const + { + std::string strSetCancel; + BOOST_FOREACH(int n, setCancel) + strSetCancel += strprintf("%d ", n); + std::string strSetSubVer; + BOOST_FOREACH(std::string str, setSubVer) + strSetSubVer += "\"" + str + "\" "; + return strprintf( + "CAlert(\n" + " nVersion = %d\n" + " nRelayUntil = %"PRI64d"\n" + " nExpiration = %"PRI64d"\n" + " nID = %d\n" + " nCancel = %d\n" + " setCancel = %s\n" + " nMinVer = %d\n" + " nMaxVer = %d\n" + " setSubVer = %s\n" + " nPriority = %d\n" + " strComment = \"%s\"\n" + " strStatusBar = \"%s\"\n" + ")\n", + nVersion, + nRelayUntil, + nExpiration, + nID, + nCancel, + strSetCancel.c_str(), + nMinVer, + nMaxVer, + strSetSubVer.c_str(), + nPriority, + strComment.c_str(), + strStatusBar.c_str()); + } + + void print() const + { + printf("%s", ToString().c_str()); + } +}; + +class CAlert : public CUnsignedAlert +{ +public: + std::vector vchMsg; + std::vector vchSig; + + CAlert() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(vchMsg); + READWRITE(vchSig); + ) + + void SetNull() + { + CUnsignedAlert::SetNull(); + vchMsg.clear(); + vchSig.clear(); + } + + bool IsNull() const + { + return (nExpiration == 0); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsInEffect() const + { + return (GetAdjustedTime() < nExpiration); + } + + bool Cancels(const CAlert& alert) const + { + if (!IsInEffect()) + return false; // this was a no-op before 31403 + return (alert.nID <= nCancel || setCancel.count(alert.nID)); + } + + bool AppliesTo(int nVersion, std::string strSubVerIn) const + { + return (IsInEffect() && + nMinVer <= nVersion && nVersion <= nMaxVer && + (setSubVer.empty() || setSubVer.count(strSubVerIn))); + } + + bool AppliesToMe() const + { + return AppliesTo(VERSION, ::pszSubVer); + } + + bool RelayTo(CNode* pnode) const + { + if (!IsInEffect()) + return false; + // returns true if wasn't already contained in the set + if (pnode->setKnown.insert(GetHash()).second) + { + if (AppliesTo(pnode->nVersion, pnode->strSubVer) || + AppliesToMe() || + GetAdjustedTime() < nRelayUntil) + { + pnode->PushMessage("alert", *this); + return true; + } + } + return false; + } + + bool CheckSignature() + { + CKey key; + if (!key.SetPubKey(ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284"))) + return error("CAlert::CheckSignature() : SetPubKey failed"); + if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) + return error("CAlert::CheckSignature() : verify signature failed"); + + // Now unserialize the data + CDataStream sMsg(vchMsg); + sMsg >> *(CUnsignedAlert*)this; + return true; + } + + bool ProcessAlert(); +}; + + + + + + + + + + +extern std::map mapTransactions; +extern std::map mapWallet; +extern std::vector vWalletUpdated; +extern CCriticalSection cs_mapWallet; +extern std::map, CPrivKey> mapKeys; +extern std::map > mapPubKeys; +extern CCriticalSection cs_mapKeys; +extern CKey keyUser; + +#endif diff --git a/src/net.cpp b/src/net.cpp new file mode 100644 index 0000000000..39360a334c --- /dev/null +++ b/src/net.cpp @@ -0,0 +1,1667 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" + +#ifdef USE_UPNP +#include +#include +#include +#include +#endif + +using namespace std; +using namespace boost; + +static const int MAX_OUTBOUND_CONNECTIONS = 8; + +void ThreadMessageHandler2(void* parg); +void ThreadSocketHandler2(void* parg); +void ThreadOpenConnections2(void* parg); +#ifdef USE_UPNP +void ThreadMapPort2(void* parg); +#endif +bool OpenNetworkConnection(const CAddress& addrConnect); + + + + + +// +// Global state variables +// +bool fClient = false; +bool fAllowDNS = false; +uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); +CAddress addrLocalHost("0.0.0.0", 0, false, nLocalServices); +CNode* pnodeLocalHost = NULL; +uint64 nLocalHostNonce = 0; +array vnThreadsRunning; +SOCKET hListenSocket = INVALID_SOCKET; + +vector vNodes; +CCriticalSection cs_vNodes; +map, CAddress> mapAddresses; +CCriticalSection cs_mapAddresses; +map mapRelay; +deque > vRelayExpiration; +CCriticalSection cs_mapRelay; +map mapAlreadyAskedFor; + +// Settings +int fUseProxy = false; +CAddress addrProxy("127.0.0.1",9050); + + + + +unsigned short GetListenPort() +{ + return (unsigned short)(GetArg("-port", GetDefaultPort())); +} + +void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) +{ + // Filter out duplicate requests + if (pindexBegin == pindexLastGetBlocksBegin && hashEnd == hashLastGetBlocksEnd) + return; + pindexLastGetBlocksBegin = pindexBegin; + hashLastGetBlocksEnd = hashEnd; + + PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); +} + + + + + +bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) +{ + hSocketRet = INVALID_SOCKET; + + SOCKET hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (hSocket == INVALID_SOCKET) + return false; +#ifdef BSD + int set = 1; + setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)); +#endif + + bool fProxy = (fUseProxy && addrConnect.IsRoutable()); + struct sockaddr_in sockaddr = (fProxy ? addrProxy.GetSockAddr() : addrConnect.GetSockAddr()); + + if (connect(hSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) + { + closesocket(hSocket); + return false; + } + + if (fProxy) + { + printf("proxy connecting %s\n", addrConnect.ToString().c_str()); + char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; + memcpy(pszSocks4IP + 2, &addrConnect.port, 2); + memcpy(pszSocks4IP + 4, &addrConnect.ip, 4); + char* pszSocks4 = pszSocks4IP; + int nSize = sizeof(pszSocks4IP); + + int ret = send(hSocket, pszSocks4, nSize, MSG_NOSIGNAL); + if (ret != nSize) + { + closesocket(hSocket); + return error("Error sending to proxy"); + } + char pchRet[8]; + if (recv(hSocket, pchRet, 8, 0) != 8) + { + closesocket(hSocket); + return error("Error reading proxy response"); + } + if (pchRet[1] != 0x5a) + { + closesocket(hSocket); + if (pchRet[1] != 0x5b) + printf("ERROR: Proxy returned error %d\n", pchRet[1]); + return false; + } + printf("proxy connected %s\n", addrConnect.ToString().c_str()); + } + + hSocketRet = hSocket; + return true; +} + +// portDefault is in host order +bool Lookup(const char *pszName, vector& vaddr, int nServices, int nMaxSolutions, bool fAllowLookup, int portDefault, bool fAllowPort) +{ + vaddr.clear(); + if (pszName[0] == 0) + return false; + int port = portDefault; + char psz[256]; + char *pszHost = psz; + strlcpy(psz, pszName, sizeof(psz)); + if (fAllowPort) + { + char* pszColon = strrchr(psz+1,':'); + char *pszPortEnd = NULL; + int portParsed = pszColon ? strtoul(pszColon+1, &pszPortEnd, 10) : 0; + if (pszColon && pszPortEnd && pszPortEnd[0] == 0) + { + if (psz[0] == '[' && pszColon[-1] == ']') + { + // Future: enable IPv6 colon-notation inside [] + pszHost = psz+1; + pszColon[-1] = 0; + } + else + pszColon[0] = 0; + port = portParsed; + if (port < 0 || port > USHRT_MAX) + port = USHRT_MAX; + } + } + + unsigned int addrIP = inet_addr(pszHost); + if (addrIP != INADDR_NONE) + { + // valid IP address passed + vaddr.push_back(CAddress(addrIP, port, nServices)); + return true; + } + + if (!fAllowLookup) + return false; + + struct hostent* phostent = gethostbyname(pszHost); + if (!phostent) + return false; + + if (phostent->h_addrtype != AF_INET) + return false; + + char** ppAddr = phostent->h_addr_list; + while (*ppAddr != NULL && vaddr.size() != nMaxSolutions) + { + CAddress addr(((struct in_addr*)ppAddr[0])->s_addr, port, nServices); + if (addr.IsValid()) + vaddr.push_back(addr); + ppAddr++; + } + + return (vaddr.size() > 0); +} + +// portDefault is in host order +bool Lookup(const char *pszName, CAddress& addr, int nServices, bool fAllowLookup, int portDefault, bool fAllowPort) +{ + vector vaddr; + bool fRet = Lookup(pszName, vaddr, nServices, 1, fAllowLookup, portDefault, fAllowPort); + if (fRet) + addr = vaddr[0]; + return fRet; +} + +bool GetMyExternalIP2(const CAddress& addrConnect, const char* pszGet, const char* pszKeyword, unsigned int& ipRet) +{ + SOCKET hSocket; + if (!ConnectSocket(addrConnect, hSocket)) + return error("GetMyExternalIP() : connection to %s failed", addrConnect.ToString().c_str()); + + send(hSocket, pszGet, strlen(pszGet), MSG_NOSIGNAL); + + string strLine; + while (RecvLine(hSocket, strLine)) + { + if (strLine.empty()) // HTTP response is separated from headers by blank line + { + loop + { + if (!RecvLine(hSocket, strLine)) + { + closesocket(hSocket); + return false; + } + if (pszKeyword == NULL) + break; + if (strLine.find(pszKeyword) != -1) + { + strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword)); + break; + } + } + closesocket(hSocket); + if (strLine.find("<") != -1) + strLine = strLine.substr(0, strLine.find("<")); + strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r")); + while (strLine.size() > 0 && isspace(strLine[strLine.size()-1])) + strLine.resize(strLine.size()-1); + CAddress addr(strLine,0,true); + printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); + if (addr.ip == 0 || addr.ip == INADDR_NONE || !addr.IsRoutable()) + return false; + ipRet = addr.ip; + return true; + } + } + closesocket(hSocket); + return error("GetMyExternalIP() : connection closed"); +} + +// We now get our external IP from the IRC server first and only use this as a backup +bool GetMyExternalIP(unsigned int& ipRet) +{ + CAddress addrConnect; + const char* pszGet; + const char* pszKeyword; + + if (fUseProxy) + return false; + + for (int nLookup = 0; nLookup <= 1; nLookup++) + for (int nHost = 1; nHost <= 2; nHost++) + { + // We should be phasing out our use of sites like these. If we need + // replacements, we should ask for volunteers to put this simple + // php file on their webserver that prints the client IP: + // + if (nHost == 1) + { + addrConnect = CAddress("91.198.22.70",80); // checkip.dyndns.org + + if (nLookup == 1) + { + CAddress addrIP("checkip.dyndns.org", 80, true); + if (addrIP.IsValid()) + addrConnect = addrIP; + } + + pszGet = "GET / HTTP/1.1\r\n" + "Host: checkip.dyndns.org\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" + "Connection: close\r\n" + "\r\n"; + + pszKeyword = "Address:"; + } + else if (nHost == 2) + { + addrConnect = CAddress("74.208.43.192", 80); // www.showmyip.com + + if (nLookup == 1) + { + CAddress addrIP("www.showmyip.com", 80, true); + if (addrIP.IsValid()) + addrConnect = addrIP; + } + + pszGet = "GET /simple/ HTTP/1.1\r\n" + "Host: www.showmyip.com\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" + "Connection: close\r\n" + "\r\n"; + + pszKeyword = NULL; // Returns just IP address + } + + if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet)) + return true; + } + + return false; +} + +void ThreadGetMyExternalIP(void* parg) +{ + // Wait for IRC to get it first + if (!GetBoolArg("-noirc")) + { + for (int i = 0; i < 2 * 60; i++) + { + Sleep(1000); + if (fGotExternalIP || fShutdown) + return; + } + } + + // Fallback in case IRC fails to get it + if (GetMyExternalIP(addrLocalHost.ip)) + { + printf("GetMyExternalIP() returned %s\n", addrLocalHost.ToStringIP().c_str()); + if (addrLocalHost.IsRoutable()) + { + // If we already connected to a few before we had our IP, go back and addr them. + // setAddrKnown automatically filters any duplicate sends. + CAddress addr(addrLocalHost); + addr.nTime = GetAdjustedTime(); + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + pnode->PushAddress(addr); + } + } +} + + + + + +bool AddAddress(CAddress addr, int64 nTimePenalty) +{ + if (!addr.IsRoutable()) + return false; + if (addr.ip == addrLocalHost.ip) + return false; + addr.nTime = max((int64)0, (int64)addr.nTime - nTimePenalty); + CRITICAL_BLOCK(cs_mapAddresses) + { + map, CAddress>::iterator it = mapAddresses.find(addr.GetKey()); + if (it == mapAddresses.end()) + { + // New address + printf("AddAddress(%s)\n", addr.ToString().c_str()); + mapAddresses.insert(make_pair(addr.GetKey(), addr)); + CAddrDB().WriteAddress(addr); + return true; + } + else + { + bool fUpdated = false; + CAddress& addrFound = (*it).second; + if ((addrFound.nServices | addr.nServices) != addrFound.nServices) + { + // Services have been added + addrFound.nServices |= addr.nServices; + fUpdated = true; + } + bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60); + int64 nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); + if (addrFound.nTime < addr.nTime - nUpdateInterval) + { + // Periodically update most recently seen time + addrFound.nTime = addr.nTime; + fUpdated = true; + } + if (fUpdated) + CAddrDB().WriteAddress(addrFound); + } + } + return false; +} + +void AddressCurrentlyConnected(const CAddress& addr) +{ + CRITICAL_BLOCK(cs_mapAddresses) + { + // Only if it's been published already + map, CAddress>::iterator it = mapAddresses.find(addr.GetKey()); + if (it != mapAddresses.end()) + { + CAddress& addrFound = (*it).second; + int64 nUpdateInterval = 20 * 60; + if (addrFound.nTime < GetAdjustedTime() - nUpdateInterval) + { + // Periodically update most recently seen time + addrFound.nTime = GetAdjustedTime(); + CAddrDB addrdb; + addrdb.WriteAddress(addrFound); + } + } + } +} + + + + + +void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) +{ + // If the dialog might get closed before the reply comes back, + // call this in the destructor so it doesn't get called after it's deleted. + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodes) + { + CRITICAL_BLOCK(pnode->cs_mapRequests) + { + for (map::iterator mi = pnode->mapRequests.begin(); mi != pnode->mapRequests.end();) + { + CRequestTracker& tracker = (*mi).second; + if (tracker.fn == fn && tracker.param1 == param1) + pnode->mapRequests.erase(mi++); + else + mi++; + } + } + } + } +} + + + + + + + +// +// Subscription methods for the broadcast and subscription system. +// Channel numbers are message numbers, i.e. MSG_TABLE and MSG_PRODUCT. +// +// The subscription system uses a meet-in-the-middle strategy. +// With 100,000 nodes, if senders broadcast to 1000 random nodes and receivers +// subscribe to 1000 random nodes, 99.995% (1 - 0.99^1000) of messages will get through. +// + +bool AnySubscribed(unsigned int nChannel) +{ + if (pnodeLocalHost->IsSubscribed(nChannel)) + return true; + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->IsSubscribed(nChannel)) + return true; + return false; +} + +bool CNode::IsSubscribed(unsigned int nChannel) +{ + if (nChannel >= vfSubscribe.size()) + return false; + return vfSubscribe[nChannel]; +} + +void CNode::Subscribe(unsigned int nChannel, unsigned int nHops) +{ + if (nChannel >= vfSubscribe.size()) + return; + + if (!AnySubscribed(nChannel)) + { + // Relay subscribe + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode != this) + pnode->PushMessage("subscribe", nChannel, nHops); + } + + vfSubscribe[nChannel] = true; +} + +void CNode::CancelSubscribe(unsigned int nChannel) +{ + if (nChannel >= vfSubscribe.size()) + return; + + // Prevent from relaying cancel if wasn't subscribed + if (!vfSubscribe[nChannel]) + return; + vfSubscribe[nChannel] = false; + + if (!AnySubscribed(nChannel)) + { + // Relay subscription cancel + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode != this) + pnode->PushMessage("sub-cancel", nChannel); + } +} + + + + + + + + + +CNode* FindNode(unsigned int ip) +{ + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->addr.ip == ip) + return (pnode); + } + return NULL; +} + +CNode* FindNode(CAddress addr) +{ + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->addr == addr) + return (pnode); + } + return NULL; +} + +CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) +{ + if (addrConnect.ip == addrLocalHost.ip) + return NULL; + + // Look for an existing connection + CNode* pnode = FindNode(addrConnect.ip); + if (pnode) + { + if (nTimeout != 0) + pnode->AddRef(nTimeout); + else + pnode->AddRef(); + return pnode; + } + + /// debug print + printf("trying connection %s lastseen=%.1fhrs lasttry=%.1fhrs\n", + addrConnect.ToString().c_str(), + (double)(addrConnect.nTime - GetAdjustedTime())/3600.0, + (double)(addrConnect.nLastTry - GetAdjustedTime())/3600.0); + + CRITICAL_BLOCK(cs_mapAddresses) + mapAddresses[addrConnect.GetKey()].nLastTry = GetAdjustedTime(); + + // Connect + SOCKET hSocket; + if (ConnectSocket(addrConnect, hSocket)) + { + /// debug print + printf("connected %s\n", addrConnect.ToString().c_str()); + + // Set to nonblocking +#ifdef __WXMSW__ + u_long nOne = 1; + if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) + printf("ConnectSocket() : ioctlsocket nonblocking setting failed, error %d\n", WSAGetLastError()); +#else + if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) + printf("ConnectSocket() : fcntl nonblocking setting failed, error %d\n", errno); +#endif + + // Add node + CNode* pnode = new CNode(hSocket, addrConnect, false); + if (nTimeout != 0) + pnode->AddRef(nTimeout); + else + pnode->AddRef(); + CRITICAL_BLOCK(cs_vNodes) + vNodes.push_back(pnode); + + pnode->nTimeConnected = GetTime(); + return pnode; + } + else + { + return NULL; + } +} + +void CNode::CloseSocketDisconnect() +{ + fDisconnect = true; + if (hSocket != INVALID_SOCKET) + { + if (fDebug) + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); + printf("disconnecting node %s\n", addr.ToString().c_str()); + closesocket(hSocket); + hSocket = INVALID_SOCKET; + } +} + +void CNode::Cleanup() +{ + // All of a nodes broadcasts and subscriptions are automatically torn down + // when it goes down, so a node has to stay up to keep its broadcast going. + + // Cancel subscriptions + for (unsigned int nChannel = 0; nChannel < vfSubscribe.size(); nChannel++) + if (vfSubscribe[nChannel]) + CancelSubscribe(nChannel); +} + + + + + + + + + + + + + +void ThreadSocketHandler(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadSocketHandler(parg)); + try + { + vnThreadsRunning[0]++; + ThreadSocketHandler2(parg); + vnThreadsRunning[0]--; + } + catch (std::exception& e) { + vnThreadsRunning[0]--; + PrintException(&e, "ThreadSocketHandler()"); + } catch (...) { + vnThreadsRunning[0]--; + throw; // support pthread_cancel() + } + printf("ThreadSocketHandler exiting\n"); +} + +void ThreadSocketHandler2(void* parg) +{ + printf("ThreadSocketHandler started\n"); + list vNodesDisconnected; + int nPrevNodeCount = 0; + + loop + { + // + // Disconnect nodes + // + CRITICAL_BLOCK(cs_vNodes) + { + // Disconnect unused nodes + vector vNodesCopy = vNodes; + BOOST_FOREACH(CNode* pnode, vNodesCopy) + { + if (pnode->fDisconnect || + (pnode->GetRefCount() <= 0 && pnode->vRecv.empty() && pnode->vSend.empty())) + { + // remove from vNodes + vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); + + // close socket and cleanup + pnode->CloseSocketDisconnect(); + pnode->Cleanup(); + + // hold in disconnected pool until all refs are released + pnode->nReleaseTime = max(pnode->nReleaseTime, GetTime() + 15 * 60); + if (pnode->fNetworkNode || pnode->fInbound) + pnode->Release(); + vNodesDisconnected.push_back(pnode); + } + } + + // Delete disconnected nodes + list vNodesDisconnectedCopy = vNodesDisconnected; + BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy) + { + // wait until threads are done using it + if (pnode->GetRefCount() <= 0) + { + bool fDelete = false; + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + TRY_CRITICAL_BLOCK(pnode->cs_mapRequests) + TRY_CRITICAL_BLOCK(pnode->cs_inventory) + fDelete = true; + if (fDelete) + { + vNodesDisconnected.remove(pnode); + delete pnode; + } + } + } + } + if (vNodes.size() != nPrevNodeCount) + { + nPrevNodeCount = vNodes.size(); + MainFrameRepaint(); + } + + + // + // Find which sockets have data to receive + // + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 50000; // frequency to poll pnode->vSend + + fd_set fdsetRecv; + fd_set fdsetSend; + fd_set fdsetError; + FD_ZERO(&fdsetRecv); + FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); + SOCKET hSocketMax = 0; + + if(hListenSocket != INVALID_SOCKET) + FD_SET(hListenSocket, &fdsetRecv); + hSocketMax = max(hSocketMax, hListenSocket); + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodes) + { + if (pnode->hSocket == INVALID_SOCKET || pnode->hSocket < 0) + continue; + FD_SET(pnode->hSocket, &fdsetRecv); + FD_SET(pnode->hSocket, &fdsetError); + hSocketMax = max(hSocketMax, pnode->hSocket); + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + if (!pnode->vSend.empty()) + FD_SET(pnode->hSocket, &fdsetSend); + } + } + + vnThreadsRunning[0]--; + int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); + vnThreadsRunning[0]++; + if (fShutdown) + return; + if (nSelect == SOCKET_ERROR) + { + int nErr = WSAGetLastError(); + printf("socket select error %d\n", nErr); + for (int i = 0; i <= hSocketMax; i++) + FD_SET(i, &fdsetRecv); + FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); + Sleep(timeout.tv_usec/1000); + } + + + // + // Accept new connections + // + if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) + { + struct sockaddr_in sockaddr; + socklen_t len = sizeof(sockaddr); + SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); + CAddress addr(sockaddr); + int nInbound = 0; + + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->fInbound) + nInbound++; + if (hSocket == INVALID_SOCKET) + { + if (WSAGetLastError() != WSAEWOULDBLOCK) + printf("socket error accept failed: %d\n", WSAGetLastError()); + } + else if (nInbound >= GetArg("-maxconnections", 125) - MAX_OUTBOUND_CONNECTIONS) + { + closesocket(hSocket); + } + else + { + printf("accepted connection %s\n", addr.ToString().c_str()); + CNode* pnode = new CNode(hSocket, addr, true); + pnode->AddRef(); + CRITICAL_BLOCK(cs_vNodes) + vNodes.push_back(pnode); + } + } + + + // + // Service each socket + // + vector vNodesCopy; + CRITICAL_BLOCK(cs_vNodes) + { + vNodesCopy = vNodes; + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->AddRef(); + } + BOOST_FOREACH(CNode* pnode, vNodesCopy) + { + if (fShutdown) + return; + + // + // Receive + // + if (pnode->hSocket == INVALID_SOCKET) + continue; + if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) + { + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + { + CDataStream& vRecv = pnode->vRecv; + unsigned int nPos = vRecv.size(); + + if (nPos > 1000*GetArg("-maxreceivebuffer", 10*1000)) { + if (!pnode->fDisconnect) + printf("socket recv flood control disconnect (%d bytes)\n", vRecv.size()); + pnode->CloseSocketDisconnect(); + } + else { + // typical socket buffer is 8K-64K + char pchBuf[0x10000]; + int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); + if (nBytes > 0) + { + vRecv.resize(nPos + nBytes); + memcpy(&vRecv[nPos], pchBuf, nBytes); + pnode->nLastRecv = GetTime(); + } + else if (nBytes == 0) + { + // socket closed gracefully + if (!pnode->fDisconnect) + printf("socket closed\n"); + pnode->CloseSocketDisconnect(); + } + else if (nBytes < 0) + { + // error + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) + { + if (!pnode->fDisconnect) + printf("socket recv error %d\n", nErr); + pnode->CloseSocketDisconnect(); + } + } + } + } + } + + // + // Send + // + if (pnode->hSocket == INVALID_SOCKET) + continue; + if (FD_ISSET(pnode->hSocket, &fdsetSend)) + { + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + { + CDataStream& vSend = pnode->vSend; + if (!vSend.empty()) + { + int nBytes = send(pnode->hSocket, &vSend[0], vSend.size(), MSG_NOSIGNAL | MSG_DONTWAIT); + if (nBytes > 0) + { + vSend.erase(vSend.begin(), vSend.begin() + nBytes); + pnode->nLastSend = GetTime(); + } + else if (nBytes < 0) + { + // error + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) + { + printf("socket send error %d\n", nErr); + pnode->CloseSocketDisconnect(); + } + } + if (vSend.size() > 1000*GetArg("-maxsendbuffer", 10*1000)) { + if (!pnode->fDisconnect) + printf("socket send flood control disconnect (%d bytes)\n", vSend.size()); + pnode->CloseSocketDisconnect(); + } + } + } + } + + // + // Inactivity checking + // + if (pnode->vSend.empty()) + pnode->nLastSendEmpty = GetTime(); + if (GetTime() - pnode->nTimeConnected > 60) + { + if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) + { + printf("socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0); + pnode->fDisconnect = true; + } + else if (GetTime() - pnode->nLastSend > 90*60 && GetTime() - pnode->nLastSendEmpty > 90*60) + { + printf("socket not sending\n"); + pnode->fDisconnect = true; + } + else if (GetTime() - pnode->nLastRecv > 90*60) + { + printf("socket inactivity timeout\n"); + pnode->fDisconnect = true; + } + } + } + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->Release(); + } + + Sleep(10); + } +} + + + + + + + + + +#ifdef USE_UPNP +void ThreadMapPort(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadMapPort(parg)); + try + { + vnThreadsRunning[5]++; + ThreadMapPort2(parg); + vnThreadsRunning[5]--; + } + catch (std::exception& e) { + vnThreadsRunning[5]--; + PrintException(&e, "ThreadMapPort()"); + } catch (...) { + vnThreadsRunning[5]--; + PrintException(NULL, "ThreadMapPort()"); + } + printf("ThreadMapPort exiting\n"); +} + +void ThreadMapPort2(void* parg) +{ + printf("ThreadMapPort started\n"); + + char port[6]; + sprintf(port, "%d", GetListenPort()); + + const char * rootdescurl = 0; + const char * multicastif = 0; + const char * minissdpdpath = 0; + struct UPNPDev * devlist = 0; + char lanaddr[64]; + + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); + + struct UPNPUrls urls; + struct IGDdatas data; + int r; + + r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); + if (r == 1) + { + char intClient[16]; + char intPort[6]; + +#ifndef __WXMSW__ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port, port, lanaddr, 0, "TCP", 0); +#else + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port, port, lanaddr, 0, "TCP", 0, "0"); +#endif + if(r!=UPNPCOMMAND_SUCCESS) + printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + port, port, lanaddr, r, strupnperror(r)); + else + printf("UPnP Port Mapping successful.\n"); + loop { + if (fShutdown || !fUseUPnP) + { + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port, "TCP", 0); + printf("UPNP_DeletePortMapping() returned : %d\n", r); + freeUPNPDevlist(devlist); devlist = 0; + FreeUPNPUrls(&urls); + return; + } + Sleep(2000); + } + } else { + printf("No valid UPnP IGDs found\n"); + freeUPNPDevlist(devlist); devlist = 0; + if (r != 0) + FreeUPNPUrls(&urls); + loop { + if (fShutdown || !fUseUPnP) + return; + Sleep(2000); + } + } +} + +void MapPort(bool fMapPort) +{ + if (fUseUPnP != fMapPort) + { + fUseUPnP = fMapPort; + CWalletDB().WriteSetting("fUseUPnP", fUseUPnP); + } + if (fUseUPnP && vnThreadsRunning[5] < 1) + { + if (!CreateThread(ThreadMapPort, NULL)) + printf("Error: ThreadMapPort(ThreadMapPort) failed\n"); + } +} +#endif + + + + + + + + + + +static const char *strDNSSeed[] = { + "bitseed.xf2.org", + "bitseed.bitcoin.org.uk", +}; + +void DNSAddressSeed() +{ + int found = 0; + + printf("Loading addresses from DNS seeds (could take a while)\n"); + + for (int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) { + vector vaddr; + if (Lookup(strDNSSeed[seed_idx], vaddr, NODE_NETWORK, -1, true)) + { + BOOST_FOREACH (CAddress& addr, vaddr) + { + if (addr.GetByte(3) != 127) + { + addr.nTime = 0; + AddAddress(addr); + found++; + } + } + } + } + + printf("%d addresses found from DNS seeds\n", found); +} + + + +unsigned int pnSeed[] = +{ + 0x1ddb1032, 0x6242ce40, 0x52d6a445, 0x2dd7a445, 0x8a53cd47, 0x73263750, 0xda23c257, 0xecd4ed57, + 0x0a40ec59, 0x75dce160, 0x7df76791, 0x89370bad, 0xa4f214ad, 0x767700ae, 0x638b0418, 0x868a1018, + 0xcd9f332e, 0x0129653e, 0xcc92dc3e, 0x96671640, 0x56487e40, 0x5b66f440, 0xb1d01f41, 0xf1dc6041, + 0xc1d12b42, 0x86ba1243, 0x6be4df43, 0x6d4cef43, 0xd18e0644, 0x1ab0b344, 0x6584a345, 0xe7c1a445, + 0x58cea445, 0xc5daa445, 0x21dda445, 0x3d3b5346, 0x13e55347, 0x1080d24a, 0x8e611e4b, 0x81518e4b, + 0x6c839e4b, 0xe2ad0a4c, 0xfbbc0a4c, 0x7f5b6e4c, 0x7244224e, 0x1300554e, 0x20690652, 0x5a48b652, + 0x75c5c752, 0x4335cc54, 0x340fd154, 0x87c07455, 0x087b2b56, 0x8a133a57, 0xac23c257, 0x70374959, + 0xfb63d45b, 0xb9a1685c, 0x180d765c, 0x674f645d, 0x04d3495e, 0x1de44b5e, 0x4ee8a362, 0x0ded1b63, + 0xc1b04b6d, 0x8d921581, 0x97b7ea82, 0x1cf83a8e, 0x91490bad, 0x09dc75ae, 0x9a6d79ae, 0xa26d79ae, + 0x0fd08fae, 0x0f3e3fb2, 0x4f944fb2, 0xcca448b8, 0x3ecd6ab8, 0xa9d5a5bc, 0x8d0119c1, 0x045997d5, + 0xca019dd9, 0x0d526c4d, 0xabf1ba44, 0x66b1ab55, 0x1165f462, 0x3ed7cbad, 0xa38fae6e, 0x3bd2cbad, + 0xd36f0547, 0x20df7840, 0x7a337742, 0x549f8e4b, 0x9062365c, 0xd399f562, 0x2b5274a1, 0x8edfa153, + 0x3bffb347, 0x7074bf58, 0xb74fcbad, 0x5b5a795b, 0x02fa29ce, 0x5a6738d4, 0xe8a1d23e, 0xef98c445, + 0x4b0f494c, 0xa2bc1e56, 0x7694ad63, 0xa4a800c3, 0x05fda6cd, 0x9f22175e, 0x364a795b, 0x536285d5, + 0xac44c9d4, 0x0b06254d, 0x150c2fd4, 0x32a50dcc, 0xfd79ce48, 0xf15cfa53, 0x66c01e60, 0x6bc26661, + 0xc03b47ae, 0x4dda1b81, 0x3285a4c1, 0x883ca96d, 0x35d60a4c, 0xdae09744, 0x2e314d61, 0x84e247cf, + 0x6c814552, 0x3a1cc658, 0x98d8f382, 0xe584cb5b, 0x15e86057, 0x7b01504e, 0xd852dd48, 0x56382f56, + 0x0a5df454, 0xa0d18d18, 0x2e89b148, 0xa79c114c, 0xcbdcd054, 0x5523bc43, 0xa9832640, 0x8a066144, + 0x3894c3bc, 0xab76bf58, 0x6a018ac1, 0xfebf4f43, 0x2f26c658, 0x31102f4e, 0x85e929d5, 0x2a1c175e, + 0xfc6c2cd1, 0x27b04b6d, 0xdf024650, 0x161748b8, 0x28be6580, 0x57be6580, 0x1cee677a, 0xaa6bb742, + 0x9a53964b, 0x0a5a2d4d, 0x2434c658, 0x9a494f57, 0x1ebb0e48, 0xf610b85d, 0x077ecf44, 0x085128bc, + 0x5ba17a18, 0x27ca1b42, 0xf8a00b56, 0xfcd4c257, 0xcf2fc15e, 0xd897e052, 0x4cada04f, 0x2f35f6d5, + 0x382ce8c9, 0xe523984b, 0x3f946846, 0x60c8be43, 0x41da6257, 0xde0be142, 0xae8a544b, 0xeff0c254, + 0x1e0f795b, 0xaeb28890, 0xca16acd9, 0x1e47ddd8, 0x8c8c4829, 0xd27dc747, 0xd53b1663, 0x4096b163, + 0x9c8dd958, 0xcb12f860, 0x9e79305c, 0x40c1a445, 0x4a90c2bc, 0x2c3a464d, 0x2727f23c, 0x30b04b6d, + 0x59024cb8, 0xa091e6ad, 0x31b04b6d, 0xc29d46a6, 0x63934fb2, 0xd9224dbe, 0x9f5910d8, 0x7f530a6b, + 0x752e9c95, 0x65453548, 0xa484be46, 0xce5a1b59, 0x710e0718, 0x46a13d18, 0xdaaf5318, 0xc4a8ff53, + 0x87abaa52, 0xb764cf51, 0xb2025d4a, 0x6d351e41, 0xc035c33e, 0xa432c162, 0x61ef34ae, 0xd16fddbc, + 0x0870e8c1, 0x3070e8c1, 0x9c71e8c1, 0xa4992363, 0x85a1f663, 0x4184e559, 0x18d96ed8, 0x17b8dbd5, + 0x60e7cd18, 0xe5ee104c, 0xab17ac62, 0x1e786e1b, 0x5d23b762, 0xf2388fae, 0x88270360, 0x9e5b3d80, + 0x7da518b2, 0xb5613b45, 0x1ad41f3e, 0xd550854a, 0x8617e9a9, 0x925b229c, 0xf2e92542, 0x47af0544, + 0x73b5a843, 0xb9b7a0ad, 0x03a748d0, 0x0a6ff862, 0x6694df62, 0x3bfac948, 0x8e098f4f, 0x746916c3, + 0x02f38e4f, 0x40bb1243, 0x6a54d162, 0x6008414b, 0xa513794c, 0x514aa343, 0x63781747, 0xdbb6795b, + 0xed065058, 0x42d24b46, 0x1518794c, 0x9b271681, 0x73e4ffad, 0x0654784f, 0x438dc945, 0x641846a6, + 0x2d1b0944, 0x94b59148, 0x8d369558, 0xa5a97662, 0x8b705b42, 0xce9204ae, 0x8d584450, 0x2df61555, + 0xeebff943, 0x2e75fb4d, 0x3ef8fc57, 0x9921135e, 0x8e31042e, 0xb5afad43, 0x89ecedd1, 0x9cfcc047, + 0x8fcd0f4c, 0xbe49f5ad, 0x146a8d45, 0x98669ab8, 0x98d9175e, 0xd1a8e46d, 0x839a3ab8, 0x40a0016c, + 0x6d27c257, 0x977fffad, 0x7baa5d5d, 0x1213be43, 0xb167e5a9, 0x640fe8ca, 0xbc9ea655, 0x0f820a4c, + 0x0f097059, 0x69ac957c, 0x366d8453, 0xb1ba2844, 0x8857f081, 0x70b5be63, 0xc545454b, 0xaf36ded1, + 0xb5a4b052, 0x21f062d1, 0x72ab89b2, 0x74a45318, 0x8312e6bc, 0xb916965f, 0x8aa7c858, 0xfe7effad, +}; + + + +void ThreadOpenConnections(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadOpenConnections(parg)); + try + { + vnThreadsRunning[1]++; + ThreadOpenConnections2(parg); + vnThreadsRunning[1]--; + } + catch (std::exception& e) { + vnThreadsRunning[1]--; + PrintException(&e, "ThreadOpenConnections()"); + } catch (...) { + vnThreadsRunning[1]--; + PrintException(NULL, "ThreadOpenConnections()"); + } + printf("ThreadOpenConnections exiting\n"); +} + +void ThreadOpenConnections2(void* parg) +{ + printf("ThreadOpenConnections started\n"); + + // Connect to specific addresses + if (mapArgs.count("-connect")) + { + for (int64 nLoop = 0;; nLoop++) + { + BOOST_FOREACH(string strAddr, mapMultiArgs["-connect"]) + { + CAddress addr(strAddr, fAllowDNS); + if (addr.IsValid()) + OpenNetworkConnection(addr); + for (int i = 0; i < 10 && i < nLoop; i++) + { + Sleep(500); + if (fShutdown) + return; + } + } + } + } + + // Connect to manually added nodes first + if (mapArgs.count("-addnode")) + { + BOOST_FOREACH(string strAddr, mapMultiArgs["-addnode"]) + { + CAddress addr(strAddr, fAllowDNS); + if (addr.IsValid()) + { + OpenNetworkConnection(addr); + Sleep(500); + if (fShutdown) + return; + } + } + } + + // Initiate network connections + int64 nStart = GetTime(); + loop + { + // Limit outbound connections + vnThreadsRunning[1]--; + Sleep(500); + loop + { + int nOutbound = 0; + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (!pnode->fInbound) + nOutbound++; + int nMaxOutboundConnections = MAX_OUTBOUND_CONNECTIONS; + nMaxOutboundConnections = min(nMaxOutboundConnections, (int)GetArg("-maxconnections", 125)); + if (nOutbound < nMaxOutboundConnections) + break; + Sleep(2000); + if (fShutdown) + return; + } + vnThreadsRunning[1]++; + if (fShutdown) + return; + + CRITICAL_BLOCK(cs_mapAddresses) + { + // Add seed nodes if IRC isn't working + static bool fSeedUsed; + bool fTOR = (fUseProxy && addrProxy.port == htons(9050)); + if (mapAddresses.empty() && (GetTime() - nStart > 60 || fTOR) && !fTestNet) + { + for (int i = 0; i < ARRAYLEN(pnSeed); i++) + { + // It'll only connect to one or two seed nodes because once it connects, + // it'll get a pile of addresses with newer timestamps. + CAddress addr; + addr.ip = pnSeed[i]; + addr.nTime = 0; + AddAddress(addr); + } + fSeedUsed = true; + } + + if (fSeedUsed && mapAddresses.size() > ARRAYLEN(pnSeed) + 100) + { + // Disconnect seed nodes + set setSeed(pnSeed, pnSeed + ARRAYLEN(pnSeed)); + static int64 nSeedDisconnected; + if (nSeedDisconnected == 0) + { + nSeedDisconnected = GetTime(); + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (setSeed.count(pnode->addr.ip)) + pnode->fDisconnect = true; + } + + // Keep setting timestamps to 0 so they won't reconnect + if (GetTime() - nSeedDisconnected < 60 * 60) + { + BOOST_FOREACH(PAIRTYPE(const vector, CAddress)& item, mapAddresses) + { + if (setSeed.count(item.second.ip) && item.second.nTime != 0) + { + item.second.nTime = 0; + CAddrDB().WriteAddress(item.second); + } + } + } + } + } + + + // + // Choose an address to connect to based on most recently seen + // + CAddress addrConnect; + int64 nBest = INT64_MIN; + + // Only connect to one address per a.b.?.? range. + // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. + set setConnected; + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + setConnected.insert(pnode->addr.ip & 0x0000ffff); + + CRITICAL_BLOCK(cs_mapAddresses) + { + BOOST_FOREACH(const PAIRTYPE(vector, CAddress)& item, mapAddresses) + { + const CAddress& addr = item.second; + if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.ip & 0x0000ffff)) + continue; + int64 nSinceLastSeen = GetAdjustedTime() - addr.nTime; + int64 nSinceLastTry = GetAdjustedTime() - addr.nLastTry; + + // Randomize the order in a deterministic way, putting the standard port first + int64 nRandomizer = (uint64)(nStart * 4951 + addr.nLastTry * 9567851 + addr.ip * 7789) % (2 * 60 * 60); + if (addr.port != htons(GetDefaultPort())) + nRandomizer += 2 * 60 * 60; + + // Last seen Base retry frequency + // <1 hour 10 min + // 1 hour 1 hour + // 4 hours 2 hours + // 24 hours 5 hours + // 48 hours 7 hours + // 7 days 13 hours + // 30 days 27 hours + // 90 days 46 hours + // 365 days 93 hours + int64 nDelay = (int64)(3600.0 * sqrt(fabs((double)nSinceLastSeen) / 3600.0) + nRandomizer); + + // Fast reconnect for one hour after last seen + if (nSinceLastSeen < 60 * 60) + nDelay = 10 * 60; + + // Limit retry frequency + if (nSinceLastTry < nDelay) + continue; + + // If we have IRC, we'll be notified when they first come online, + // and again every 24 hours by the refresh broadcast. + if (nGotIRCAddresses > 0 && vNodes.size() >= 2 && nSinceLastSeen > 24 * 60 * 60) + continue; + + // Only try the old stuff if we don't have enough connections + if (vNodes.size() >= 8 && nSinceLastSeen > 24 * 60 * 60) + continue; + + // If multiple addresses are ready, prioritize by time since + // last seen and time since last tried. + int64 nScore = min(nSinceLastTry, (int64)24 * 60 * 60) - nSinceLastSeen - nRandomizer; + if (nScore > nBest) + { + nBest = nScore; + addrConnect = addr; + } + } + } + + if (addrConnect.IsValid()) + OpenNetworkConnection(addrConnect); + } +} + +bool OpenNetworkConnection(const CAddress& addrConnect) +{ + // + // Initiate outbound network connection + // + if (fShutdown) + return false; + if (addrConnect.ip == addrLocalHost.ip || !addrConnect.IsIPv4() || FindNode(addrConnect.ip)) + return false; + + vnThreadsRunning[1]--; + CNode* pnode = ConnectNode(addrConnect); + vnThreadsRunning[1]++; + if (fShutdown) + return false; + if (!pnode) + return false; + pnode->fNetworkNode = true; + + return true; +} + + + + + + + + +void ThreadMessageHandler(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadMessageHandler(parg)); + try + { + vnThreadsRunning[2]++; + ThreadMessageHandler2(parg); + vnThreadsRunning[2]--; + } + catch (std::exception& e) { + vnThreadsRunning[2]--; + PrintException(&e, "ThreadMessageHandler()"); + } catch (...) { + vnThreadsRunning[2]--; + PrintException(NULL, "ThreadMessageHandler()"); + } + printf("ThreadMessageHandler exiting\n"); +} + +void ThreadMessageHandler2(void* parg) +{ + printf("ThreadMessageHandler started\n"); + SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); + while (!fShutdown) + { + vector vNodesCopy; + CRITICAL_BLOCK(cs_vNodes) + { + vNodesCopy = vNodes; + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->AddRef(); + } + + // Poll the connected nodes for messages + CNode* pnodeTrickle = NULL; + if (!vNodesCopy.empty()) + pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())]; + BOOST_FOREACH(CNode* pnode, vNodesCopy) + { + // Receive messages + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + ProcessMessages(pnode); + if (fShutdown) + return; + + // Send messages + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + SendMessages(pnode, pnode == pnodeTrickle); + if (fShutdown) + return; + } + + CRITICAL_BLOCK(cs_vNodes) + { + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->Release(); + } + + // Wait and allow messages to bunch up. + // Reduce vnThreadsRunning so StopNode has permission to exit while + // we're sleeping, but we must always check fShutdown after doing this. + vnThreadsRunning[2]--; + Sleep(100); + if (fRequestShutdown) + Shutdown(NULL); + vnThreadsRunning[2]++; + if (fShutdown) + return; + } +} + + + + + + +bool BindListenPort(string& strError) +{ + strError = ""; + int nOne = 1; + addrLocalHost.port = htons(GetListenPort()); + +#ifdef __WXMSW__ + // Initialize Windows Sockets + WSADATA wsadata; + int ret = WSAStartup(MAKEWORD(2,2), &wsadata); + if (ret != NO_ERROR) + { + strError = strprintf("Error: TCP/IP socket library failed to start (WSAStartup returned error %d)", ret); + printf("%s\n", strError.c_str()); + return false; + } +#endif + + // Create socket for listening for incoming connections + hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (hListenSocket == INVALID_SOCKET) + { + strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + +#ifdef BSD + // Different way of disabling SIGPIPE on BSD + setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int)); +#endif + +#ifndef __WXMSW__ + // Allow binding if the port is still in TIME_WAIT state after + // the program was closed and restarted. Not an issue on windows. + setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); +#endif + +#ifdef __WXMSW__ + // Set to nonblocking, incoming connections will also inherit this + if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR) +#else + if (fcntl(hListenSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) +#endif + { + strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + + // The sockaddr_in structure specifies the address family, + // IP address, and port for the socket that is being bound + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = INADDR_ANY; // bind to all IPs on this computer + sockaddr.sin_port = htons(GetListenPort()); + if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) + { + int nErr = WSAGetLastError(); + if (nErr == WSAEADDRINUSE) + strError = strprintf(_("Unable to bind to port %d on this computer. Bitcoin is probably already running."), ntohs(sockaddr.sin_port)); + else + strError = strprintf("Error: Unable to bind to port %d on this computer (bind returned error %d)", ntohs(sockaddr.sin_port), nErr); + printf("%s\n", strError.c_str()); + return false; + } + printf("Bound to port %d\n", ntohs(sockaddr.sin_port)); + + // Listen for incoming connections + if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) + { + strError = strprintf("Error: Listening for incoming connections failed (listen returned error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + + return true; +} + +void StartNode(void* parg) +{ + if (pnodeLocalHost == NULL) + pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress("127.0.0.1", 0, false, nLocalServices)); + +#ifdef __WXMSW__ + // Get local host ip + char pszHostName[1000] = ""; + if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) + { + vector vaddr; + if (Lookup(pszHostName, vaddr, nLocalServices, -1, true)) + BOOST_FOREACH (const CAddress &addr, vaddr) + if (addr.GetByte(3) != 127) + { + addrLocalHost = addr; + break; + } + } +#else + // Get local host ip + struct ifaddrs* myaddrs; + if (getifaddrs(&myaddrs) == 0) + { + for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == NULL) continue; + if ((ifa->ifa_flags & IFF_UP) == 0) continue; + if (strcmp(ifa->ifa_name, "lo") == 0) continue; + if (strcmp(ifa->ifa_name, "lo0") == 0) continue; + char pszIP[100]; + if (ifa->ifa_addr->sa_family == AF_INET) + { + struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr); + if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s4->sin_addr), pszIP, sizeof(pszIP)) != NULL) + printf("ipv4 %s: %s\n", ifa->ifa_name, pszIP); + + // Take the first IP that isn't loopback 127.x.x.x + CAddress addr(*(unsigned int*)&s4->sin_addr, GetListenPort(), nLocalServices); + if (addr.IsValid() && addr.GetByte(3) != 127) + { + addrLocalHost = addr; + break; + } + } + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr); + if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s6->sin6_addr), pszIP, sizeof(pszIP)) != NULL) + printf("ipv6 %s: %s\n", ifa->ifa_name, pszIP); + } + } + freeifaddrs(myaddrs); + } +#endif + printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); + + if (fUseProxy || mapArgs.count("-connect") || fNoListen) + { + // Proxies can't take incoming connections + addrLocalHost.ip = CAddress("0.0.0.0").ip; + printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); + } + else + { + CreateThread(ThreadGetMyExternalIP, NULL); + } + + // + // Start threads + // + + // Map ports with UPnP + if (fHaveUPnP) + MapPort(fUseUPnP); + + // Get addresses from IRC and advertise ours + if (!CreateThread(ThreadIRCSeed, NULL)) + printf("Error: CreateThread(ThreadIRCSeed) failed\n"); + + // Send and receive from sockets, accept connections + pthread_t hThreadSocketHandler = CreateThread(ThreadSocketHandler, NULL, true); + + // Initiate outbound connections + if (!CreateThread(ThreadOpenConnections, NULL)) + printf("Error: CreateThread(ThreadOpenConnections) failed\n"); + + // Process messages + if (!CreateThread(ThreadMessageHandler, NULL)) + printf("Error: CreateThread(ThreadMessageHandler) failed\n"); + + // Generate coins in the background + GenerateBitcoins(fGenerateBitcoins); +} + +bool StopNode() +{ + printf("StopNode()\n"); + fShutdown = true; + nTransactionsUpdated++; + int64 nStart = GetTime(); + while (vnThreadsRunning[0] > 0 || vnThreadsRunning[2] > 0 || vnThreadsRunning[3] > 0 || vnThreadsRunning[4] > 0 +#ifdef USE_UPNP + || vnThreadsRunning[5] > 0 +#endif + ) + { + if (GetTime() - nStart > 20) + break; + Sleep(20); + } + if (vnThreadsRunning[0] > 0) printf("ThreadSocketHandler still running\n"); + if (vnThreadsRunning[1] > 0) printf("ThreadOpenConnections still running\n"); + if (vnThreadsRunning[2] > 0) printf("ThreadMessageHandler still running\n"); + if (vnThreadsRunning[3] > 0) printf("ThreadBitcoinMiner still running\n"); + if (vnThreadsRunning[4] > 0) printf("ThreadRPCServer still running\n"); + if (fHaveUPnP && vnThreadsRunning[5] > 0) printf("ThreadMapPort still running\n"); + while (vnThreadsRunning[2] > 0 || vnThreadsRunning[4] > 0) + Sleep(20); + Sleep(50); + + return true; +} + +class CNetCleanup +{ +public: + CNetCleanup() + { + } + ~CNetCleanup() + { + // Close sockets + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->hSocket != INVALID_SOCKET) + closesocket(pnode->hSocket); + if (hListenSocket != INVALID_SOCKET) + if (closesocket(hListenSocket) == SOCKET_ERROR) + printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError()); + +#ifdef __WXMSW__ + // Shutdown Windows Sockets + WSACleanup(); +#endif + } +} +instance_of_cnetcleanup; diff --git a/src/net.h b/src/net.h new file mode 100644 index 0000000000..d1ded87232 --- /dev/null +++ b/src/net.h @@ -0,0 +1,1065 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_NET_H +#define BITCOIN_NET_H + +#include +#include +#include + +#ifndef __WXMSW__ +#include +#endif + +class CMessageHeader; +class CAddress; +class CInv; +class CRequestTracker; +class CNode; +class CBlockIndex; +extern int nBestHeight; + + + +inline unsigned short GetDefaultPort() { return fTestNet ? 18333 : 8333; } +static const unsigned int PUBLISH_HOPS = 5; +enum +{ + NODE_NETWORK = (1 << 0), +}; + + + + +bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet); +bool Lookup(const char *pszName, std::vector& vaddr, int nServices, int nMaxSolutions, bool fAllowLookup = false, int portDefault = 0, bool fAllowPort = false); +bool Lookup(const char *pszName, CAddress& addr, int nServices, bool fAllowLookup = false, int portDefault = 0, bool fAllowPort = false); +bool GetMyExternalIP(unsigned int& ipRet); +bool AddAddress(CAddress addr, int64 nTimePenalty=0); +void AddressCurrentlyConnected(const CAddress& addr); +CNode* FindNode(unsigned int ip); +CNode* ConnectNode(CAddress addrConnect, int64 nTimeout=0); +void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); +bool AnySubscribed(unsigned int nChannel); +void MapPort(bool fMapPort); +void DNSAddressSeed(); +bool BindListenPort(std::string& strError=REF(std::string())); +void StartNode(void* parg); +bool StopNode(); + + + + + + + + +// +// Message header +// (4) message start +// (12) command +// (4) size +// (4) checksum + +extern char pchMessageStart[4]; + +class CMessageHeader +{ +public: + enum { COMMAND_SIZE=12 }; + char pchMessageStart[sizeof(::pchMessageStart)]; + char pchCommand[COMMAND_SIZE]; + unsigned int nMessageSize; + unsigned int nChecksum; + + CMessageHeader() + { + memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + memset(pchCommand, 0, sizeof(pchCommand)); + pchCommand[1] = 1; + nMessageSize = -1; + nChecksum = 0; + } + + CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn) + { + memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + strncpy(pchCommand, pszCommand, COMMAND_SIZE); + nMessageSize = nMessageSizeIn; + nChecksum = 0; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(FLATDATA(pchMessageStart)); + READWRITE(FLATDATA(pchCommand)); + READWRITE(nMessageSize); + if (nVersion >= 209) + READWRITE(nChecksum); + ) + + std::string GetCommand() + { + if (pchCommand[COMMAND_SIZE-1] == 0) + return std::string(pchCommand, pchCommand + strlen(pchCommand)); + else + return std::string(pchCommand, pchCommand + COMMAND_SIZE); + } + + bool IsValid() + { + // Check start string + if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0) + return false; + + // Check the command string for errors + for (char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++) + { + if (*p1 == 0) + { + // Must be all zeros after the first zero + for (; p1 < pchCommand + COMMAND_SIZE; p1++) + if (*p1 != 0) + return false; + } + else if (*p1 < ' ' || *p1 > 0x7E) + return false; + } + + // Message size + if (nMessageSize > MAX_SIZE) + { + printf("CMessageHeader::IsValid() : (%s, %u bytes) nMessageSize > MAX_SIZE\n", GetCommand().c_str(), nMessageSize); + return false; + } + + return true; + } +}; + + + + + + +static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + +class CAddress +{ +public: + uint64 nServices; + unsigned char pchReserved[12]; + unsigned int ip; + unsigned short port; + + // disk and network only + unsigned int nTime; + + // memory only + unsigned int nLastTry; + + CAddress() + { + Init(); + } + + CAddress(unsigned int ipIn, unsigned short portIn=0, uint64 nServicesIn=NODE_NETWORK) + { + Init(); + ip = ipIn; + port = htons(portIn == 0 ? GetDefaultPort() : portIn); + nServices = nServicesIn; + } + + explicit CAddress(const struct sockaddr_in& sockaddr, uint64 nServicesIn=NODE_NETWORK) + { + Init(); + ip = sockaddr.sin_addr.s_addr; + port = sockaddr.sin_port; + nServices = nServicesIn; + } + + explicit CAddress(const char* pszIn, int portIn, bool fNameLookup = false, uint64 nServicesIn=NODE_NETWORK) + { + Init(); + Lookup(pszIn, *this, nServicesIn, fNameLookup, portIn); + } + + explicit CAddress(const char* pszIn, bool fNameLookup = false, uint64 nServicesIn=NODE_NETWORK) + { + Init(); + Lookup(pszIn, *this, nServicesIn, fNameLookup, 0, true); + } + + explicit CAddress(std::string strIn, int portIn, bool fNameLookup = false, uint64 nServicesIn=NODE_NETWORK) + { + Init(); + Lookup(strIn.c_str(), *this, nServicesIn, fNameLookup, portIn); + } + + explicit CAddress(std::string strIn, bool fNameLookup = false, uint64 nServicesIn=NODE_NETWORK) + { + Init(); + Lookup(strIn.c_str(), *this, nServicesIn, fNameLookup, 0, true); + } + + void Init() + { + nServices = NODE_NETWORK; + memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); + ip = INADDR_NONE; + port = htons(GetDefaultPort()); + nTime = 100000000; + nLastTry = 0; + } + + IMPLEMENT_SERIALIZE + ( + if (fRead) + const_cast(this)->Init(); + if (nType & SER_DISK) + READWRITE(nVersion); + if ((nType & SER_DISK) || (nVersion >= 31402 && !(nType & SER_GETHASH))) + READWRITE(nTime); + READWRITE(nServices); + READWRITE(FLATDATA(pchReserved)); // for IPv6 + READWRITE(ip); + READWRITE(port); + ) + + friend inline bool operator==(const CAddress& a, const CAddress& b) + { + return (memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)) == 0 && + a.ip == b.ip && + a.port == b.port); + } + + friend inline bool operator!=(const CAddress& a, const CAddress& b) + { + return (!(a == b)); + } + + friend inline bool operator<(const CAddress& a, const CAddress& b) + { + int ret = memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)); + if (ret < 0) + return true; + else if (ret == 0) + { + if (ntohl(a.ip) < ntohl(b.ip)) + return true; + else if (a.ip == b.ip) + return ntohs(a.port) < ntohs(b.port); + } + return false; + } + + std::vector GetKey() const + { + CDataStream ss; + ss.reserve(18); + ss << FLATDATA(pchReserved) << ip << port; + + #if defined(_MSC_VER) && _MSC_VER < 1300 + return std::vector((unsigned char*)&ss.begin()[0], (unsigned char*)&ss.end()[0]); + #else + return std::vector(ss.begin(), ss.end()); + #endif + } + + struct sockaddr_in GetSockAddr() const + { + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = ip; + sockaddr.sin_port = port; + return sockaddr; + } + + bool IsIPv4() const + { + return (memcmp(pchReserved, pchIPv4, sizeof(pchIPv4)) == 0); + } + + bool IsRFC1918() const + { + return IsIPv4() && (GetByte(3) == 10 || + (GetByte(3) == 192 && GetByte(2) == 168) || + (GetByte(3) == 172 && + (GetByte(2) >= 16 && GetByte(2) <= 31))); + } + + bool IsRFC3927() const + { + return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254); + } + + bool IsLocal() const + { + return IsIPv4() && (GetByte(3) == 127 || + GetByte(3) == 0); + } + + bool IsRoutable() const + { + return IsValid() && + !(IsRFC1918() || IsRFC3927() || IsLocal()); + } + + bool IsValid() const + { + // Clean up 3-byte shifted addresses caused by garbage in size field + // of addr messages from versions before 0.2.9 checksum. + // Two consecutive addr messages look like this: + // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26... + // so if the first length field is garbled, it reads the second batch + // of addr misaligned by 3 bytes. + if (memcmp(pchReserved, pchIPv4+3, sizeof(pchIPv4)-3) == 0) + return false; + + return (ip != 0 && ip != INADDR_NONE && port != htons(USHRT_MAX)); + } + + unsigned char GetByte(int n) const + { + return ((unsigned char*)&ip)[3-n]; + } + + std::string ToStringIPPort() const + { + return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port)); + } + + std::string ToStringIP() const + { + return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0)); + } + + std::string ToStringPort() const + { + return strprintf("%u", ntohs(port)); + } + + std::string ToString() const + { + return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port)); + } + + void print() const + { + printf("CAddress(%s)\n", ToString().c_str()); + } +}; + + + + + + + +enum +{ + MSG_TX = 1, + MSG_BLOCK, +}; + +static const char* ppszTypeName[] = +{ + "ERROR", + "tx", + "block", +}; + +class CInv +{ +public: + int type; + uint256 hash; + + CInv() + { + type = 0; + hash = 0; + } + + CInv(int typeIn, const uint256& hashIn) + { + type = typeIn; + hash = hashIn; + } + + CInv(const std::string& strType, const uint256& hashIn) + { + int i; + for (i = 1; i < ARRAYLEN(ppszTypeName); i++) + { + if (strType == ppszTypeName[i]) + { + type = i; + break; + } + } + if (i == ARRAYLEN(ppszTypeName)) + throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType.c_str())); + hash = hashIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(type); + READWRITE(hash); + ) + + friend inline bool operator<(const CInv& a, const CInv& b) + { + return (a.type < b.type || (a.type == b.type && a.hash < b.hash)); + } + + bool IsKnownType() const + { + return (type >= 1 && type < ARRAYLEN(ppszTypeName)); + } + + const char* GetCommand() const + { + if (!IsKnownType()) + throw std::out_of_range(strprintf("CInv::GetCommand() : type=%d unknown type", type)); + return ppszTypeName[type]; + } + + std::string ToString() const + { + return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,20).c_str()); + } + + void print() const + { + printf("CInv(%s)\n", ToString().c_str()); + } +}; + + + + + +class CRequestTracker +{ +public: + void (*fn)(void*, CDataStream&); + void* param1; + + explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL) + { + fn = fnIn; + param1 = param1In; + } + + bool IsNull() + { + return fn == NULL; + } +}; + + + + + +extern bool fClient; +extern bool fAllowDNS; +extern uint64 nLocalServices; +extern CAddress addrLocalHost; +extern CNode* pnodeLocalHost; +extern uint64 nLocalHostNonce; +extern boost::array vnThreadsRunning; +extern SOCKET hListenSocket; + +extern std::vector vNodes; +extern CCriticalSection cs_vNodes; +extern std::map, CAddress> mapAddresses; +extern CCriticalSection cs_mapAddresses; +extern std::map mapRelay; +extern std::deque > vRelayExpiration; +extern CCriticalSection cs_mapRelay; +extern std::map mapAlreadyAskedFor; + +// Settings +extern int fUseProxy; +extern CAddress addrProxy; + + + + + + +class CNode +{ +public: + // socket + uint64 nServices; + SOCKET hSocket; + CDataStream vSend; + CDataStream vRecv; + CCriticalSection cs_vSend; + CCriticalSection cs_vRecv; + int64 nLastSend; + int64 nLastRecv; + int64 nLastSendEmpty; + int64 nTimeConnected; + unsigned int nHeaderStart; + unsigned int nMessageStart; + CAddress addr; + int nVersion; + std::string strSubVer; + bool fClient; + bool fInbound; + bool fNetworkNode; + bool fSuccessfullyConnected; + bool fDisconnect; +protected: + int nRefCount; +public: + int64 nReleaseTime; + std::map mapRequests; + CCriticalSection cs_mapRequests; + uint256 hashContinue; + CBlockIndex* pindexLastGetBlocksBegin; + uint256 hashLastGetBlocksEnd; + int nStartingHeight; + + // flood relay + std::vector vAddrToSend; + std::set setAddrKnown; + bool fGetAddr; + std::set setKnown; + + // inventory based relay + std::set setInventoryKnown; + std::vector vInventoryToSend; + CCriticalSection cs_inventory; + std::multimap mapAskFor; + + // publish and subscription + std::vector vfSubscribe; + + + CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false) + { + nServices = 0; + hSocket = hSocketIn; + vSend.SetType(SER_NETWORK); + vSend.SetVersion(0); + vRecv.SetType(SER_NETWORK); + vRecv.SetVersion(0); + // Version 0.2 obsoletes 20 Feb 2012 + if (GetTime() > 1329696000) + { + vSend.SetVersion(209); + vRecv.SetVersion(209); + } + nLastSend = 0; + nLastRecv = 0; + nLastSendEmpty = GetTime(); + nTimeConnected = GetTime(); + nHeaderStart = -1; + nMessageStart = -1; + addr = addrIn; + nVersion = 0; + strSubVer = ""; + fClient = false; // set by version message + fInbound = fInboundIn; + fNetworkNode = false; + fSuccessfullyConnected = false; + fDisconnect = false; + nRefCount = 0; + nReleaseTime = 0; + hashContinue = 0; + pindexLastGetBlocksBegin = 0; + hashLastGetBlocksEnd = 0; + nStartingHeight = -1; + fGetAddr = false; + vfSubscribe.assign(256, false); + + // Be shy and don't send version until we hear + if (!fInbound) + PushVersion(); + } + + ~CNode() + { + if (hSocket != INVALID_SOCKET) + { + closesocket(hSocket); + hSocket = INVALID_SOCKET; + } + } + +private: + CNode(const CNode&); + void operator=(const CNode&); +public: + + + int GetRefCount() + { + return std::max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0); + } + + CNode* AddRef(int64 nTimeout=0) + { + if (nTimeout != 0) + nReleaseTime = std::max(nReleaseTime, GetTime() + nTimeout); + else + nRefCount++; + return this; + } + + void Release() + { + nRefCount--; + } + + + + void AddAddressKnown(const CAddress& addr) + { + setAddrKnown.insert(addr); + } + + void PushAddress(const CAddress& addr) + { + // Known checking here is only to save space from duplicates. + // SendMessages will filter it again for knowns that were added + // after addresses were pushed. + if (addr.IsValid() && !setAddrKnown.count(addr)) + vAddrToSend.push_back(addr); + } + + + void AddInventoryKnown(const CInv& inv) + { + CRITICAL_BLOCK(cs_inventory) + setInventoryKnown.insert(inv); + } + + void PushInventory(const CInv& inv) + { + CRITICAL_BLOCK(cs_inventory) + if (!setInventoryKnown.count(inv)) + vInventoryToSend.push_back(inv); + } + + void AskFor(const CInv& inv) + { + // We're using mapAskFor as a priority queue, + // the key is the earliest time the request can be sent + int64& nRequestTime = mapAlreadyAskedFor[inv]; + printf("askfor %s %"PRI64d"\n", inv.ToString().c_str(), nRequestTime); + + // Make sure not to reuse time indexes to keep things in the same order + int64 nNow = (GetTime() - 1) * 1000000; + static int64 nLastTime; + nLastTime = nNow = std::max(nNow, ++nLastTime); + + // Each retry is 2 minutes after the last + nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow); + mapAskFor.insert(std::make_pair(nRequestTime, inv)); + } + + + + void BeginMessage(const char* pszCommand) + { + cs_vSend.Enter(); + if (nHeaderStart != -1) + AbortMessage(); + nHeaderStart = vSend.size(); + vSend << CMessageHeader(pszCommand, 0); + nMessageStart = vSend.size(); + if (fDebug) + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); + printf("sending: %s ", pszCommand); + } + + void AbortMessage() + { + if (nHeaderStart == -1) + return; + vSend.resize(nHeaderStart); + nHeaderStart = -1; + nMessageStart = -1; + cs_vSend.Leave(); + printf("(aborted)\n"); + } + + void EndMessage() + { + if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) + { + printf("dropmessages DROPPING SEND MESSAGE\n"); + AbortMessage(); + return; + } + + if (nHeaderStart == -1) + return; + + // Set the size + unsigned int nSize = vSend.size() - nMessageStart; + memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize)); + + // Set the checksum + if (vSend.GetVersion() >= 209) + { + uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end()); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + assert(nMessageStart - nHeaderStart >= offsetof(CMessageHeader, nChecksum) + sizeof(nChecksum)); + memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nChecksum), &nChecksum, sizeof(nChecksum)); + } + + printf("(%d bytes) ", nSize); + printf("\n"); + + nHeaderStart = -1; + nMessageStart = -1; + cs_vSend.Leave(); + } + + void EndMessageAbortIfEmpty() + { + if (nHeaderStart == -1) + return; + int nSize = vSend.size() - nMessageStart; + if (nSize > 0) + EndMessage(); + else + AbortMessage(); + } + + + + void PushVersion() + { + /// when NTP implemented, change to just nTime = GetAdjustedTime() + int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); + CAddress addrYou = (fUseProxy ? CAddress("0.0.0.0") : addr); + CAddress addrMe = (fUseProxy ? CAddress("0.0.0.0") : addrLocalHost); + RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); + PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe, + nLocalHostNonce, std::string(pszSubVer), nBestHeight); + } + + + + + void PushMessage(const char* pszCommand) + { + try + { + BeginMessage(pszCommand); + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1) + { + try + { + BeginMessage(pszCommand); + vSend << a1; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3 << a4; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3 << a4 << a5; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3 << a4 << a5 << a6; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8, const T9& a9) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + + void PushRequest(const char* pszCommand, + void (*fn)(void*, CDataStream&), void* param1) + { + uint256 hashReply; + RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); + + CRITICAL_BLOCK(cs_mapRequests) + mapRequests[hashReply] = CRequestTracker(fn, param1); + + PushMessage(pszCommand, hashReply); + } + + template + void PushRequest(const char* pszCommand, const T1& a1, + void (*fn)(void*, CDataStream&), void* param1) + { + uint256 hashReply; + RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); + + CRITICAL_BLOCK(cs_mapRequests) + mapRequests[hashReply] = CRequestTracker(fn, param1); + + PushMessage(pszCommand, hashReply, a1); + } + + template + void PushRequest(const char* pszCommand, const T1& a1, const T2& a2, + void (*fn)(void*, CDataStream&), void* param1) + { + uint256 hashReply; + RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); + + CRITICAL_BLOCK(cs_mapRequests) + mapRequests[hashReply] = CRequestTracker(fn, param1); + + PushMessage(pszCommand, hashReply, a1, a2); + } + + + + void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd); + bool IsSubscribed(unsigned int nChannel); + void Subscribe(unsigned int nChannel, unsigned int nHops=0); + void CancelSubscribe(unsigned int nChannel); + void CloseSocketDisconnect(); + void Cleanup(); +}; + + + + + + + + + + +inline void RelayInventory(const CInv& inv) +{ + // Put on lists to offer to the other nodes + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + pnode->PushInventory(inv); +} + +template +void RelayMessage(const CInv& inv, const T& a) +{ + CDataStream ss(SER_NETWORK); + ss.reserve(10000); + ss << a; + RelayMessage(inv, ss); +} + +template<> +inline void RelayMessage<>(const CInv& inv, const CDataStream& ss) +{ + CRITICAL_BLOCK(cs_mapRelay) + { + // Expire old relay messages + while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) + { + mapRelay.erase(vRelayExpiration.front().second); + vRelayExpiration.pop_front(); + } + + // Save original serialized message so newer versions are preserved + mapRelay[inv] = ss; + vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv)); + } + + RelayInventory(inv); +} + + + + + + + + +// +// Templates for the publish and subscription system. +// The object being published as T& obj needs to have: +// a set setSources member +// specializations of AdvertInsert and AdvertErase +// Currently implemented for CTable and CProduct. +// + +template +void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) +{ + // Add to sources + obj.setSources.insert(pfrom->addr.ip); + + if (!AdvertInsert(obj)) + return; + + // Relay + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) + pnode->PushMessage("publish", nChannel, nHops, obj); +} + +template +void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) +{ + uint256 hash = obj.GetHash(); + + CRITICAL_BLOCK(cs_vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) + pnode->PushMessage("pub-cancel", nChannel, nHops, hash); + + AdvertErase(obj); +} + +template +void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) +{ + // Remove a source + obj.setSources.erase(pfrom->addr.ip); + + // If no longer supported by any sources, cancel it + if (obj.setSources.empty()) + AdvertStopPublish(pfrom, nChannel, nHops, obj); +} + +#endif diff --git a/src/noui.h b/src/noui.h new file mode 100644 index 0000000000..afb19526c1 --- /dev/null +++ b/src/noui.h @@ -0,0 +1,67 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_NOUI_H +#define BITCOIN_NOUI_H + +#include + +typedef void wxWindow; +#define wxYES 0x00000002 +#define wxOK 0x00000004 +#define wxNO 0x00000008 +#define wxYES_NO (wxYES|wxNO) +#define wxCANCEL 0x00000010 +#define wxAPPLY 0x00000020 +#define wxCLOSE 0x00000040 +#define wxOK_DEFAULT 0x00000000 +#define wxYES_DEFAULT 0x00000000 +#define wxNO_DEFAULT 0x00000080 +#define wxCANCEL_DEFAULT 0x80000000 +#define wxICON_EXCLAMATION 0x00000100 +#define wxICON_HAND 0x00000200 +#define wxICON_WARNING wxICON_EXCLAMATION +#define wxICON_ERROR wxICON_HAND +#define wxICON_QUESTION 0x00000400 +#define wxICON_INFORMATION 0x00000800 +#define wxICON_STOP wxICON_HAND +#define wxICON_ASTERISK wxICON_INFORMATION +#define wxICON_MASK (0x00000100|0x00000200|0x00000400|0x00000800) +#define wxFORWARD 0x00001000 +#define wxBACKWARD 0x00002000 +#define wxRESET 0x00004000 +#define wxHELP 0x00008000 +#define wxMORE 0x00010000 +#define wxSETUP 0x00020000 + +inline int MyMessageBox(const std::string& message, const std::string& caption="Message", int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1) +{ + printf("%s: %s\n", caption.c_str(), message.c_str()); + fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); + return 4; +} +#define wxMessageBox MyMessageBox + +inline int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1) +{ + return MyMessageBox(message, caption, style, parent, x, y); +} + +inline bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent) +{ + return true; +} + +inline void CalledSetStatusBar(const std::string& strText, int nField) +{ +} + +inline void UIThreadCall(boost::function0 fn) +{ +} + +inline void MainFrameRepaint() +{ +} + +#endif diff --git a/src/qt/aboutdialog.cpp b/src/qt/aboutdialog.cpp new file mode 100644 index 0000000000..13347961dd --- /dev/null +++ b/src/qt/aboutdialog.cpp @@ -0,0 +1,22 @@ +#include "aboutdialog.h" +#include "ui_aboutdialog.h" + +#include "util.h" + +AboutDialog::AboutDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AboutDialog) +{ + ui->setupUi(this); + ui->versionLabel->setText(QString::fromStdString(FormatFullVersion())); +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} + +void AboutDialog::on_buttonBox_accepted() +{ + close(); +} diff --git a/src/qt/aboutdialog.h b/src/qt/aboutdialog.h new file mode 100644 index 0000000000..827cc741c3 --- /dev/null +++ b/src/qt/aboutdialog.h @@ -0,0 +1,25 @@ +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +namespace Ui { + class AboutDialog; +} + +class AboutDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AboutDialog(QWidget *parent = 0); + ~AboutDialog(); + +private: + Ui::AboutDialog *ui; + +private slots: + void on_buttonBox_accepted(); +}; + +#endif // ABOUTDIALOG_H diff --git a/src/qt/addressbookdialog.cpp b/src/qt/addressbookdialog.cpp new file mode 100644 index 0000000000..90950a6441 --- /dev/null +++ b/src/qt/addressbookdialog.cpp @@ -0,0 +1,168 @@ +#include "addressbookdialog.h" +#include "ui_addressbookdialog.h" + +#include "addresstablemodel.h" +#include "editaddressdialog.h" + +#include +#include +#include + +AddressBookDialog::AddressBookDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AddressBookDialog), + model(0) +{ + ui->setupUi(this); +} + +AddressBookDialog::~AddressBookDialog() +{ + delete ui; +} + +void AddressBookDialog::setModel(AddressTableModel *model) +{ + this->model = model; + /* Refresh list from core */ + model->updateList(); + + /* Receive filter */ + QSortFilterProxyModel *receive_model = new QSortFilterProxyModel(this); + receive_model->setSourceModel(model); + receive_model->setDynamicSortFilter(true); + receive_model->setFilterRole(AddressTableModel::TypeRole); + receive_model->setFilterFixedString(AddressTableModel::Receive); + ui->receiveTableView->setModel(receive_model); + + /* Send filter */ + QSortFilterProxyModel *send_model = new QSortFilterProxyModel(this); + send_model->setSourceModel(model); + send_model->setDynamicSortFilter(true); + send_model->setFilterRole(AddressTableModel::TypeRole); + send_model->setFilterFixedString(AddressTableModel::Send); + ui->sendTableView->setModel(send_model); + + /* Set column widths */ + ui->receiveTableView->horizontalHeader()->resizeSection( + AddressTableModel::Address, 320); + ui->receiveTableView->horizontalHeader()->setResizeMode( + AddressTableModel::Label, QHeaderView::Stretch); + ui->sendTableView->horizontalHeader()->resizeSection( + AddressTableModel::Address, 320); + ui->sendTableView->horizontalHeader()->setResizeMode( + AddressTableModel::Label, QHeaderView::Stretch); +} + +void AddressBookDialog::setTab(int tab) +{ + ui->tabWidget->setCurrentIndex(tab); +} + +QTableView *AddressBookDialog::getCurrentTable() +{ + switch(ui->tabWidget->currentIndex()) + { + case SendingTab: + return ui->sendTableView; + case ReceivingTab: + return ui->receiveTableView; + default: + return 0; + } +} + +void AddressBookDialog::on_copyToClipboard_clicked() +{ + /* Copy currently selected address to clipboard + (or nothing, if nothing selected) + */ + QTableView *table = getCurrentTable(); + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QVariant address = index.data(); + QApplication::clipboard()->setText(address.toString()); + } +} + +void AddressBookDialog::on_editButton_clicked() +{ + QModelIndexList indexes = getCurrentTable()->selectionModel()->selectedRows(); + if(indexes.isEmpty()) + { + return; + } + /* Map selected index to source address book model */ + QAbstractProxyModel *proxy_model = static_cast(getCurrentTable()->model()); + QModelIndex selected = proxy_model->mapToSource(indexes.at(0)); + + /* Double click also triggers edit button */ + EditAddressDialog dlg( + ui->tabWidget->currentIndex() == SendingTab ? + EditAddressDialog::EditSendingAddress : + EditAddressDialog::EditReceivingAddress); + dlg.setModel(model); + dlg.loadRow(selected.row()); + if(dlg.exec()) + { + dlg.saveCurrentRow(); + } +} + +void AddressBookDialog::on_newAddressButton_clicked() +{ + EditAddressDialog dlg( + ui->tabWidget->currentIndex() == SendingTab ? + EditAddressDialog::NewSendingAddress : + EditAddressDialog::NewReceivingAddress); + dlg.setModel(model); + if(dlg.exec()) + { + dlg.saveCurrentRow(); + } +} + +void AddressBookDialog::on_tabWidget_currentChanged(int index) +{ + switch(index) + { + case SendingTab: + ui->deleteButton->setEnabled(true); + break; + case ReceivingTab: + ui->deleteButton->setEnabled(false); + break; + } +} + +void AddressBookDialog::on_deleteButton_clicked() +{ + QTableView *table = getCurrentTable(); + QModelIndexList indexes = table->selectionModel()->selectedRows(); + if(!indexes.isEmpty()) + { + table->model()->removeRow(indexes.at(0).row()); + } +} + +void AddressBookDialog::on_buttonBox_accepted() +{ + QTableView *table = getCurrentTable(); + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QVariant address = table->model()->data(index); + returnValue = address.toString(); + } + if(!returnValue.isEmpty()) + { + accept(); + } + else + { + reject(); + } +} diff --git a/src/qt/addressbookdialog.h b/src/qt/addressbookdialog.h new file mode 100644 index 0000000000..bf7c2a65a6 --- /dev/null +++ b/src/qt/addressbookdialog.h @@ -0,0 +1,47 @@ +#ifndef ADDRESSBOOKDIALOG_H +#define ADDRESSBOOKDIALOG_H + +#include + +namespace Ui { + class AddressBookDialog; +} +class AddressTableModel; + +QT_BEGIN_NAMESPACE +class QTableView; +QT_END_NAMESPACE + +class AddressBookDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AddressBookDialog(QWidget *parent = 0); + ~AddressBookDialog(); + + enum { + SendingTab = 0, + ReceivingTab = 1 + } Tabs; + + void setModel(AddressTableModel *model); + void setTab(int tab); + const QString &getReturnValue() const { return returnValue; } +private: + Ui::AddressBookDialog *ui; + AddressTableModel *model; + QString returnValue; + + QTableView *getCurrentTable(); + +private slots: + void on_buttonBox_accepted(); + void on_deleteButton_clicked(); + void on_tabWidget_currentChanged(int index); + void on_newAddressButton_clicked(); + void on_editButton_clicked(); + void on_copyToClipboard_clicked(); +}; + +#endif // ADDRESSBOOKDIALOG_H diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp new file mode 100644 index 0000000000..91b87fb7f1 --- /dev/null +++ b/src/qt/addresstablemodel.cpp @@ -0,0 +1,245 @@ +#include "addresstablemodel.h" +#include "guiutil.h" +#include "main.h" + +#include + +const QString AddressTableModel::Send = "S"; +const QString AddressTableModel::Receive = "R"; + +struct AddressTableEntry +{ + enum Type { + Sending, + Receiving + }; + + Type type; + QString label; + QString address; + + AddressTableEntry() {} + AddressTableEntry(Type type, const QString &label, const QString &address): + type(type), label(label), address(address) {} +}; + +/* Private implementation */ +struct AddressTablePriv +{ + QList cachedAddressTable; + + void refreshAddressTable() + { + cachedAddressTable.clear(); + + CRITICAL_BLOCK(cs_mapKeys) + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item, mapAddressBook) + { + std::string strAddress = item.first; + std::string strName = item.second; + uint160 hash160; + bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160)); + cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending, + QString::fromStdString(strName), + QString::fromStdString(strAddress))); + } + } + } + + int size() + { + return cachedAddressTable.size(); + } + + AddressTableEntry *index(int idx) + { + if(idx >= 0 && idx < cachedAddressTable.size()) + { + return &cachedAddressTable[idx]; + } + else + { + return 0; + } + } +}; + +AddressTableModel::AddressTableModel(QObject *parent) : + QAbstractTableModel(parent),priv(0) +{ + columns << tr("Label") << tr("Address"); + priv = new AddressTablePriv(); + priv->refreshAddressTable(); +} + +AddressTableModel::~AddressTableModel() +{ + delete priv; +} + +int AddressTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return priv->size(); +} + +int AddressTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} + +QVariant AddressTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + + AddressTableEntry *rec = static_cast(index.internalPointer()); + + if(role == Qt::DisplayRole || role == Qt::EditRole) + { + switch(index.column()) + { + case Label: + return rec->label; + case Address: + return rec->address; + } + } + else if (role == Qt::FontRole) + { + if(index.column() == Address) + { + return GUIUtil::bitcoinAddressFont(); + } + } + else if (role == TypeRole) + { + switch(rec->type) + { + case AddressTableEntry::Sending: + return Send; + case AddressTableEntry::Receiving: + return Receive; + default: break; + } + } + return QVariant(); +} + +bool AddressTableModel::setData(const QModelIndex & index, const QVariant & value, int role) +{ + if(!index.isValid()) + return false; + AddressTableEntry *rec = static_cast(index.internalPointer()); + + if(role == Qt::EditRole) + { + switch(index.column()) + { + case Label: + SetAddressBookName(rec->address.toStdString(), value.toString().toStdString()); + rec->label = value.toString(); + break; + case Address: + /* Double-check that we're not overwriting receiving address */ + if(rec->type == AddressTableEntry::Sending) + { + /* Remove old entry */ + CWalletDB().EraseName(rec->address.toStdString()); + /* Add new entry with new address */ + SetAddressBookName(value.toString().toStdString(), rec->label.toStdString()); + + rec->address = value.toString(); + } + break; + } + emit dataChanged(index, index); + + return true; + } + return false; +} + +QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal) + { + if(role == Qt::DisplayRole) + { + return columns[section]; + } + } + return QVariant(); +} + +QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & parent) const +{ + Q_UNUSED(parent); + AddressTableEntry *data = priv->index(row); + if(data) + { + return createIndex(row, column, priv->index(row)); + } + else + { + return QModelIndex(); + } +} + +void AddressTableModel::updateList() +{ + /* Update internal model from Bitcoin core */ + beginResetModel(); + priv->refreshAddressTable(); + endResetModel(); +} + +QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address) +{ + std::string strLabel = label.toStdString(); + std::string strAddress = address.toStdString(); + + if(type == Send) + { + /* Check for duplicate */ + CRITICAL_BLOCK(cs_mapAddressBook) + { + if(mapAddressBook.count(strAddress)) + { + return QString(); + } + } + } + else if(type == Receive) + { + /* Generate a new address to associate with given label */ + strAddress = PubKeyToAddress(GetKeyFromKeyPool()); + } + else + { + return QString(); + } + /* Add entry and update list */ + SetAddressBookName(strAddress, strLabel); + updateList(); + return QString::fromStdString(strAddress); +} + +bool AddressTableModel::removeRows(int row, int count, const QModelIndex & parent) +{ + Q_UNUSED(parent); + AddressTableEntry *rec = priv->index(row); + if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving) + { + /* Can only remove one row at a time, and cannot remove rows not in model. + Also refuse to remove receiving addresses. + */ + return false; + } + CWalletDB().EraseName(rec->address.toStdString()); + updateList(); + return true; +} diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h new file mode 100644 index 0000000000..8799414334 --- /dev/null +++ b/src/qt/addresstablemodel.h @@ -0,0 +1,54 @@ +#ifndef ADDRESSTABLEMODEL_H +#define ADDRESSTABLEMODEL_H + +#include +#include + +class AddressTablePriv; + +class AddressTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit AddressTableModel(QObject *parent = 0); + ~AddressTableModel(); + + enum ColumnIndex { + Label = 0, /* User specified label */ + Address = 1 /* Bitcoin address */ + }; + + enum { + TypeRole = Qt::UserRole + } RoleIndex; + + static const QString Send; /* Send addres */ + static const QString Receive; /* Receive address */ + + /* Overridden methods from QAbstractTableModel */ + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex & index, const QVariant & value, int role); + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QModelIndex index(int row, int column, const QModelIndex & parent) const; + bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex()); + + /* Add an address to the model. + Returns the added address on success, and an empty string otherwise. + */ + QString addRow(const QString &type, const QString &label, const QString &address); + + /* Update address list from core. Invalidates any indices. + */ + void updateList(); +private: + AddressTablePriv *priv; + QStringList columns; +signals: + +public slots: + +}; + +#endif // ADDRESSTABLEMODEL_H diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp new file mode 100644 index 0000000000..e003267426 --- /dev/null +++ b/src/qt/bitcoin.cpp @@ -0,0 +1,122 @@ +/* + * W.J. van der Laan 2011 + */ +#include "bitcoingui.h" +#include "clientmodel.h" +#include "util.h" +#include "init.h" +#include "main.h" +#include "externui.h" + +#include +#include +#include + +// Need a global reference for the notifications to find the GUI +BitcoinGUI *guiref; + +int MyMessageBox(const std::string& message, const std::string& caption, int style, wxWindow* parent, int x, int y) +{ + // Message from main thread + if(guiref) + { + guiref->error(QString::fromStdString(caption), + QString::fromStdString(message)); + } + else + { + QMessageBox::critical(0, QString::fromStdString(caption), + QString::fromStdString(message), + QMessageBox::Ok, QMessageBox::Ok); + } + return 4; +} + +int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style, wxWindow* parent, int x, int y) +{ + // Message from network thread + if(guiref) + { + QMetaObject::invokeMethod(guiref, "error", Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(caption)), + Q_ARG(QString, QString::fromStdString(message))); + } + else + { + printf("%s: %s\n", caption.c_str(), message.c_str()); + fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); + } + return 4; +} + +bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent) +{ + if(!guiref) + return false; + if(nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon) + return true; + bool payFee = false; + + /* Call slot on GUI thread. + If called from another thread, use a blocking QueuedConnection. + */ + Qt::ConnectionType connectionType = Qt::DirectConnection; + if(QThread::currentThread() != QCoreApplication::instance()->thread()) + { + connectionType = Qt::BlockingQueuedConnection; + } + + QMetaObject::invokeMethod(guiref, "askFee", connectionType, + Q_ARG(qint64, nFeeRequired), + Q_ARG(bool*, &payFee)); + + return payFee; +} + +void CalledSetStatusBar(const std::string& strText, int nField) +{ + // Only used for built-in mining, which is disabled, simple ignore +} + +void UIThreadCall(boost::function0 fn) +{ + // Only used for built-in mining, which is disabled, simple ignore +} + +void MainFrameRepaint() +{ +} + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + app.setQuitOnLastWindowClosed(false); + + try + { + if(AppInit2(argc, argv)) + { + BitcoinGUI window; + ClientModel model; + guiref = &window; + window.setModel(&model); + + window.show(); + + int retval = app.exec(); + + guiref = 0; + Shutdown(NULL); + + return retval; + } + else + { + return 1; + } + } catch (std::exception& e) { + PrintException(&e, "Runaway exception"); + } catch (...) { + PrintException(NULL, "Runaway exception"); + } +} diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc new file mode 100644 index 0000000000..80904b341c --- /dev/null +++ b/src/qt/bitcoin.qrc @@ -0,0 +1,12 @@ + + + res/icons/address-book.png + res/icons/bitcoin.png + res/icons/quit.png + res/icons/send.png + res/icons/toolbar.png + + + res/images/about.png + + diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp new file mode 100644 index 0000000000..761a266933 --- /dev/null +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -0,0 +1,63 @@ +#include "bitcoinaddressvalidator.h" + +#include + +/* Base58 characters are: + "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + + This is: + - All numbers except for '0' + - All uppercase letters except for 'I' and 'O' + - All lowercase letters except for 'l' + + User friendly Base58 input can map + - 'l' and 'I' to '1' + - '0' and 'O' to 'o' +*/ + +BitcoinAddressValidator::BitcoinAddressValidator(QObject *parent) : + QValidator(parent) +{ +} + +QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) const +{ + /* Correction */ + for(int idx=0; idx= '0' && ch<='9') || + (ch >= 'a' && ch<='z') || + (ch >= 'A' && ch<='Z')) && + ch != 'l' && ch != 'I' && ch != '0' && ch != 'O') + { + /* Alphanumeric and not a 'forbidden' character */ + } + else + { + state = QValidator::Invalid; + } + } + + return state; +} diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h new file mode 100644 index 0000000000..73f6ea1f61 --- /dev/null +++ b/src/qt/bitcoinaddressvalidator.h @@ -0,0 +1,24 @@ +#ifndef BITCOINADDRESSVALIDATOR_H +#define BITCOINADDRESSVALIDATOR_H + +#include + +/* Base48 entry widget validator. + Corrects near-miss characters and refuses characters that are no part of base48. + */ +class BitcoinAddressValidator : public QValidator +{ + Q_OBJECT +public: + explicit BitcoinAddressValidator(QObject *parent = 0); + + State validate(QString &input, int &pos) const; + + static const int MaxAddressLength = 34; +signals: + +public slots: + +}; + +#endif // BITCOINADDRESSVALIDATOR_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp new file mode 100644 index 0000000000..c08476cec3 --- /dev/null +++ b/src/qt/bitcoingui.cpp @@ -0,0 +1,448 @@ +/* + * Qt4 bitcoin GUI. + * + * W.J. van der Laan 2011 + */ +#include "bitcoingui.h" +#include "transactiontablemodel.h" +#include "addressbookdialog.h" +#include "sendcoinsdialog.h" +#include "optionsdialog.h" +#include "aboutdialog.h" +#include "clientmodel.h" +#include "guiutil.h" +#include "editaddressdialog.h" +#include "optionsmodel.h" +#include "transactiondescdialog.h" + +#include "main.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +BitcoinGUI::BitcoinGUI(QWidget *parent): + QMainWindow(parent), trayIcon(0) +{ + resize(850, 550); + setWindowTitle(tr("Bitcoin")); + setWindowIcon(QIcon(":icons/bitcoin")); + + createActions(); + + // Menus + QMenu *file = menuBar()->addMenu("&File"); + file->addAction(sendcoins); + file->addSeparator(); + file->addAction(quit); + + QMenu *settings = menuBar()->addMenu("&Settings"); + settings->addAction(receivingAddresses); + settings->addAction(options); + + QMenu *help = menuBar()->addMenu("&Help"); + help->addAction(about); + + // Toolbar + QToolBar *toolbar = addToolBar("Main toolbar"); + toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolbar->addAction(sendcoins); + toolbar->addAction(addressbook); + + // Address:
: New... : Paste to clipboard + QHBoxLayout *hbox_address = new QHBoxLayout(); + hbox_address->addWidget(new QLabel(tr("Your Bitcoin Address:"))); + address = new QLineEdit(); + address->setReadOnly(true); + address->setFont(GUIUtil::bitcoinAddressFont()); + address->setToolTip(tr("Your current default receiving address")); + hbox_address->addWidget(address); + + QPushButton *button_new = new QPushButton(tr("&New...")); + button_new->setToolTip(tr("Create new receiving address")); + QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); + button_clipboard->setToolTip(tr("Copy current receiving address to the system clipboard")); + hbox_address->addWidget(button_new); + hbox_address->addWidget(button_clipboard); + + // Balance: + QHBoxLayout *hbox_balance = new QHBoxLayout(); + hbox_balance->addWidget(new QLabel(tr("Balance:"))); + hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ + + labelBalance = new QLabel(); + labelBalance->setFont(QFont("Monospace")); + labelBalance->setToolTip(tr("Your current balance")); + hbox_balance->addWidget(labelBalance); + hbox_balance->addStretch(1); + + QVBoxLayout *vbox = new QVBoxLayout(); + vbox->addLayout(hbox_address); + vbox->addLayout(hbox_balance); + + vbox->addWidget(createTabs()); + + QWidget *centralwidget = new QWidget(this); + centralwidget->setLayout(vbox); + setCentralWidget(centralwidget); + + // Create status bar + statusBar(); + + labelConnections = new QLabel(); + labelConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); + labelConnections->setMinimumWidth(130); + labelConnections->setToolTip(tr("Number of connections to other clients")); + + labelBlocks = new QLabel(); + labelBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); + labelBlocks->setMinimumWidth(130); + labelBlocks->setToolTip(tr("Number of blocks in the block chain")); + + labelTransactions = new QLabel(); + labelTransactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); + labelTransactions->setMinimumWidth(130); + labelTransactions->setToolTip(tr("Number of transactions in your wallet")); + + statusBar()->addPermanentWidget(labelConnections); + statusBar()->addPermanentWidget(labelBlocks); + statusBar()->addPermanentWidget(labelTransactions); + + // Action bindings + connect(button_new, SIGNAL(clicked()), this, SLOT(newAddressClicked())); + connect(button_clipboard, SIGNAL(clicked()), this, SLOT(copyClipboardClicked())); + + createTrayIcon(); +} + +void BitcoinGUI::createActions() +{ + quit = new QAction(QIcon(":/icons/quit"), tr("&Exit"), this); + quit->setToolTip(tr("Quit application")); + sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); + sendcoins->setToolTip(tr("Send coins to a bitcoin address")); + addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); + addressbook->setToolTip(tr("Edit the list of stored addresses and labels")); + about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); + about->setToolTip(tr("Show information about Bitcoin")); + receivingAddresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); + receivingAddresses->setToolTip(tr("Show the list of receiving addresses and edit their labels")); + options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); + options->setToolTip(tr("Modify configuration options for bitcoin")); + openBitcoin = new QAction(QIcon(":/icons/bitcoin"), "Open &Bitcoin", this); + openBitcoin->setToolTip(tr("Show the Bitcoin window")); + + connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked())); + connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked())); + connect(receivingAddresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked())); + connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); + connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); + connect(openBitcoin, SIGNAL(triggered()), this, SLOT(show())); +} + +void BitcoinGUI::setModel(ClientModel *model) +{ + this->model = model; + + // Keep up to date with client + setBalance(model->getBalance()); + connect(model, SIGNAL(balanceChanged(qint64)), this, SLOT(setBalance(qint64))); + + setNumConnections(model->getNumConnections()); + connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); + + setNumTransactions(model->getNumTransactions()); + connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); + + setNumBlocks(model->getNumBlocks()); + connect(model, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int))); + + setAddress(model->getAddress()); + connect(model, SIGNAL(addressChanged(QString)), this, SLOT(setAddress(QString))); + + // Report errors from network/worker thread + connect(model, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); + + // Put transaction list in tabs + setTabsModel(model->getTransactionTableModel()); +} + +void BitcoinGUI::createTrayIcon() +{ + QMenu *trayIconMenu = new QMenu(this); + trayIconMenu->addAction(openBitcoin); + trayIconMenu->addAction(sendcoins); + trayIconMenu->addAction(options); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(quit); + + trayIcon = new QSystemTrayIcon(this); + trayIcon->setContextMenu(trayIconMenu); + trayIcon->setIcon(QIcon(":/icons/toolbar")); + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); + trayIcon->show(); +} + +void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if(reason == QSystemTrayIcon::DoubleClick) + { + // Doubleclick on system tray icon triggers "open bitcoin" + openBitcoin->trigger(); + } +} + +QWidget *BitcoinGUI::createTabs() +{ + QStringList tab_labels; + tab_labels << tr("All transactions") + << tr("Sent/Received") + << tr("Sent") + << tr("Received"); + + QTabWidget *tabs = new QTabWidget(this); + for(int i = 0; i < tab_labels.size(); ++i) + { + QTableView *view = new QTableView(this); + tabs->addTab(view, tab_labels.at(i)); + + connect(view, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(transactionDetails(const QModelIndex&))); + transactionViews.append(view); + } + + return tabs; +} + +void BitcoinGUI::setTabsModel(QAbstractItemModel *transaction_model) +{ + QStringList tab_filters; + tab_filters << "^." + << "^["+TransactionTableModel::Sent+TransactionTableModel::Received+"]" + << "^["+TransactionTableModel::Sent+"]" + << "^["+TransactionTableModel::Received+"]"; + + for(int i = 0; i < transactionViews.size(); ++i) + { + QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this); + proxy_model->setSourceModel(transaction_model); + proxy_model->setDynamicSortFilter(true); + proxy_model->setFilterRole(TransactionTableModel::TypeRole); + proxy_model->setFilterRegExp(QRegExp(tab_filters.at(i))); + proxy_model->setSortRole(Qt::EditRole); + + QTableView *transaction_table = transactionViews.at(i); + transaction_table->setModel(proxy_model); + transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); + transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); + transaction_table->setSortingEnabled(true); + transaction_table->sortByColumn(TransactionTableModel::Status, Qt::DescendingOrder); + transaction_table->verticalHeader()->hide(); + + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Status, 120); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Date, 120); + transaction_table->horizontalHeader()->setResizeMode( + TransactionTableModel::Description, QHeaderView::Stretch); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Debit, 79); + transaction_table->horizontalHeader()->resizeSection( + TransactionTableModel::Credit, 79); + } + + connect(transaction_model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(incomingTransaction(const QModelIndex &, int, int))); +} + +void BitcoinGUI::sendcoinsClicked() +{ + SendCoinsDialog dlg; + dlg.setModel(model); + dlg.exec(); +} + +void BitcoinGUI::addressbookClicked() +{ + AddressBookDialog dlg; + dlg.setModel(model->getAddressTableModel()); + dlg.setTab(AddressBookDialog::SendingTab); + dlg.exec(); +} + +void BitcoinGUI::receivingAddressesClicked() +{ + AddressBookDialog dlg; + dlg.setModel(model->getAddressTableModel()); + dlg.setTab(AddressBookDialog::ReceivingTab); + dlg.exec(); +} + +void BitcoinGUI::optionsClicked() +{ + OptionsDialog dlg; + dlg.setModel(model->getOptionsModel()); + dlg.exec(); +} + +void BitcoinGUI::aboutClicked() +{ + AboutDialog dlg; + dlg.exec(); +} + +void BitcoinGUI::newAddressClicked() +{ + EditAddressDialog dlg(EditAddressDialog::NewReceivingAddress); + dlg.setModel(model->getAddressTableModel()); + if(dlg.exec()) + { + QString newAddress = dlg.saveCurrentRow(); + // Set returned address as new default addres + if(!newAddress.isEmpty()) + { + model->setAddress(newAddress); + } + } +} + +void BitcoinGUI::copyClipboardClicked() +{ + // Copy text in address to clipboard + QApplication::clipboard()->setText(address->text()); +} + +void BitcoinGUI::setBalance(qint64 balance) +{ + labelBalance->setText(QString::fromStdString(FormatMoney(balance))); +} + +void BitcoinGUI::setAddress(const QString &addr) +{ + address->setText(addr); +} + +void BitcoinGUI::setNumConnections(int count) +{ + labelConnections->setText(QLocale::system().toString(count)+" "+tr("connections(s)", "", count)); +} + +void BitcoinGUI::setNumBlocks(int count) +{ + labelBlocks->setText(QLocale::system().toString(count)+" "+tr("block(s)", "", count)); +} + +void BitcoinGUI::setNumTransactions(int count) +{ + labelTransactions->setText(QLocale::system().toString(count)+" "+tr("transaction(s)", "", count)); +} + +void BitcoinGUI::error(const QString &title, const QString &message) +{ + // Report errors from network/worker thread + if(trayIcon->supportsMessages()) + { + // Show as "balloon" message if possible + trayIcon->showMessage(title, message, QSystemTrayIcon::Critical); + } + else + { + // Fall back to old fashioned popup dialog if not + QMessageBox::critical(this, title, + message, + QMessageBox::Ok, QMessageBox::Ok); + } +} + +void BitcoinGUI::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::WindowStateChange) + { + if(model->getOptionsModel()->getMinimizeToTray()) + { + if (isMinimized()) + { + hide(); + e->ignore(); + } + else + { + e->accept(); + } + } + } + QMainWindow::changeEvent(e); +} + +void BitcoinGUI::closeEvent(QCloseEvent *event) +{ + if(!model->getOptionsModel()->getMinimizeToTray() && + !model->getOptionsModel()->getMinimizeOnClose()) + { + qApp->quit(); + } + QMainWindow::closeEvent(event); +} + +void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) +{ + QString strMessage = + tr("This transaction is over the size limit. You can still send it for a fee of %1, " + "which goes to the nodes that process your transaction and helps to support the network. " + "Do you want to pay the fee?").arg(QString::fromStdString(FormatMoney(nFeeRequired))); + QMessageBox::StandardButton retval = QMessageBox::question( + this, tr("Sending..."), strMessage, + QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); + *payFee = (retval == QMessageBox::Yes); +} + +void BitcoinGUI::transactionDetails(const QModelIndex& idx) +{ + /* A transaction is doubleclicked */ + TransactionDescDialog dlg(idx); + dlg.exec(); +} + +void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) +{ + TransactionTableModel *ttm = model->getTransactionTableModel(); + qint64 credit = ttm->index(start, TransactionTableModel::Credit, parent) + .data(Qt::EditRole).toULongLong(); + qint64 debit = ttm->index(start, TransactionTableModel::Debit, parent) + .data(Qt::EditRole).toULongLong(); + if((credit+debit)>0) + { + /* On incoming transaction, make an info balloon */ + QString date = ttm->index(start, TransactionTableModel::Date, parent) + .data().toString(); + QString description = ttm->index(start, TransactionTableModel::Description, parent) + .data().toString(); + + trayIcon->showMessage(tr("Incoming transaction"), + "Date: " + date + "\n" + + "Amount: " + QString::fromStdString(FormatMoney(credit+debit, true)) + "\n" + + description, + QSystemTrayIcon::Information); + } +} diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h new file mode 100644 index 0000000000..96452ef18b --- /dev/null +++ b/src/qt/bitcoingui.h @@ -0,0 +1,88 @@ +#ifndef BITCOINGUI_H +#define BITCOINGUI_H + +#include +#include + +class TransactionTableModel; +class ClientModel; + +QT_BEGIN_NAMESPACE +class QLabel; +class QLineEdit; +class QTableView; +class QAbstractItemModel; +class QModelIndex; +QT_END_NAMESPACE + +class BitcoinGUI : public QMainWindow +{ + Q_OBJECT +public: + explicit BitcoinGUI(QWidget *parent = 0); + void setModel(ClientModel *model); + + /* Transaction table tab indices */ + enum { + AllTransactions = 0, + SentReceived = 1, + Sent = 2, + Received = 3 + } TabIndex; + +protected: + void changeEvent(QEvent *e); + void closeEvent(QCloseEvent *event); + +private: + ClientModel *model; + + QLineEdit *address; + QLabel *labelBalance; + QLabel *labelConnections; + QLabel *labelBlocks; + QLabel *labelTransactions; + + QAction *quit; + QAction *sendcoins; + QAction *addressbook; + QAction *about; + QAction *receivingAddresses; + QAction *options; + QAction *openBitcoin; + + QSystemTrayIcon *trayIcon; + QList transactionViews; + + void createActions(); + QWidget *createTabs(); + void createTrayIcon(); + void setTabsModel(QAbstractItemModel *transaction_model); + +public slots: + void setBalance(qint64 balance); + void setAddress(const QString &address); + void setNumConnections(int count); + void setNumBlocks(int count); + void setNumTransactions(int count); + void error(const QString &title, const QString &message); + /* It is currently not possible to pass a return value to another thread through + BlockingQueuedConnection, so use an indirected pointer. + http://bugreports.qt.nokia.com/browse/QTBUG-10440 + */ + void askFee(qint64 nFeeRequired, bool *payFee); + +private slots: + void sendcoinsClicked(); + void addressbookClicked(); + void optionsClicked(); + void receivingAddressesClicked(); + void aboutClicked(); + void newAddressClicked(); + void copyClipboardClicked(); + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); + void transactionDetails(const QModelIndex& idx); + void incomingTransaction(const QModelIndex & parent, int start, int end); +}; + +#endif diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp new file mode 100644 index 0000000000..97391e0938 --- /dev/null +++ b/src/qt/clientmodel.cpp @@ -0,0 +1,156 @@ +#include "clientmodel.h" +#include "main.h" +#include "guiconstants.h" +#include "optionsmodel.h" +#include "addresstablemodel.h" +#include "transactiontablemodel.h" + +#include + +ClientModel::ClientModel(QObject *parent) : + QObject(parent), optionsModel(0), addressTableModel(0), + transactionTableModel(0) +{ + /* Until signal notifications is built into the bitcoin core, + simply update everything after polling using a timer. + */ + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(update())); + timer->start(MODEL_UPDATE_DELAY); + + optionsModel = new OptionsModel(this); + addressTableModel = new AddressTableModel(this); + transactionTableModel = new TransactionTableModel(this); +} + +qint64 ClientModel::getBalance() +{ + return GetBalance(); +} + +QString ClientModel::getAddress() +{ + std::vector vchPubKey; + if (CWalletDB("r").ReadDefaultKey(vchPubKey)) + { + return QString::fromStdString(PubKeyToAddress(vchPubKey)); + } + else + { + return QString(); + } +} + +int ClientModel::getNumConnections() +{ + return vNodes.size(); +} + +int ClientModel::getNumBlocks() +{ + return nBestHeight; +} + +int ClientModel::getNumTransactions() +{ + int numTransactions = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + numTransactions = mapWallet.size(); + } + return numTransactions; +} + +void ClientModel::update() +{ + /* Plainly emit all signals for now. To be precise this should check + wether the values actually changed first. + */ + emit balanceChanged(getBalance()); + emit addressChanged(getAddress()); + emit numConnectionsChanged(getNumConnections()); + emit numBlocksChanged(getNumBlocks()); + emit numTransactionsChanged(getNumTransactions()); +} + +void ClientModel::setAddress(const QString &defaultAddress) +{ + uint160 hash160; + std::string strAddress = defaultAddress.toStdString(); + if (!AddressToHash160(strAddress, hash160)) + return; + if (!mapPubKeys.count(hash160)) + return; + CWalletDB().WriteDefaultKey(mapPubKeys[hash160]); +} + +ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payAmount) +{ + uint160 hash160 = 0; + bool valid = false; + + if(!AddressToHash160(payTo.toUtf8().constData(), hash160)) + { + return InvalidAddress; + } + + if(payAmount <= 0) + { + return InvalidAmount; + } + + if(payAmount > getBalance()) + { + return AmountExceedsBalance; + } + + if((payAmount + nTransactionFee) > getBalance()) + { + return AmountWithFeeExceedsBalance; + } + + CRITICAL_BLOCK(cs_main) + { + // Send to bitcoin address + CWalletTx wtx; + CScript scriptPubKey; + scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; + + std::string strError = SendMoney(scriptPubKey, payAmount, wtx, true); + if (strError == "") + { + return OK; + } + else if (strError == "ABORTED") + { + return Aborted; + } + else + { + emit error(tr("Sending..."), QString::fromStdString(strError)); + return MiscError; + } + } + // Add addresses that we've sent to to the address book + std::string strAddress = payTo.toStdString(); + CRITICAL_BLOCK(cs_mapAddressBook) + if (!mapAddressBook.count(strAddress)) + SetAddressBookName(strAddress, ""); + + return OK; +} + +OptionsModel *ClientModel::getOptionsModel() +{ + return optionsModel; +} + +AddressTableModel *ClientModel::getAddressTableModel() +{ + return addressTableModel; +} + +TransactionTableModel *ClientModel::getTransactionTableModel() +{ + return transactionTableModel; +} diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h new file mode 100644 index 0000000000..09d1fc921e --- /dev/null +++ b/src/qt/clientmodel.h @@ -0,0 +1,61 @@ +#ifndef CLIENTMODEL_H +#define CLIENTMODEL_H + +#include + +class OptionsModel; +class AddressTableModel; +class TransactionTableModel; + +class ClientModel : public QObject +{ + Q_OBJECT +public: + explicit ClientModel(QObject *parent = 0); + + enum StatusCode + { + OK, + InvalidAmount, + InvalidAddress, + AmountExceedsBalance, + AmountWithFeeExceedsBalance, + Aborted, + MiscError + }; + + OptionsModel *getOptionsModel(); + AddressTableModel *getAddressTableModel(); + TransactionTableModel *getTransactionTableModel(); + + qint64 getBalance(); + QString getAddress(); + int getNumConnections(); + int getNumBlocks(); + int getNumTransactions(); + + /* Set default address */ + void setAddress(const QString &defaultAddress); + /* Send coins */ + StatusCode sendCoins(const QString &payTo, qint64 payAmount); +private: + OptionsModel *optionsModel; + AddressTableModel *addressTableModel; + TransactionTableModel *transactionTableModel; + +signals: + void balanceChanged(qint64 balance); + void addressChanged(const QString &address); + void numConnectionsChanged(int count); + void numBlocksChanged(int count); + void numTransactionsChanged(int count); + /* Asynchronous error notification */ + void error(const QString &title, const QString &message); + +public slots: + +private slots: + void update(); +}; + +#endif // CLIENTMODEL_H diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp new file mode 100644 index 0000000000..dd0541760b --- /dev/null +++ b/src/qt/editaddressdialog.cpp @@ -0,0 +1,84 @@ +#include "editaddressdialog.h" +#include "ui_editaddressdialog.h" +#include "addresstablemodel.h" +#include "guiutil.h" + +#include +#include + +EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : + QDialog(parent), + ui(new Ui::EditAddressDialog), mapper(0), mode(mode), model(0) +{ + ui->setupUi(this); + + GUIUtil::setupAddressWidget(ui->addressEdit, this); + + switch(mode) + { + case NewReceivingAddress: + setWindowTitle(tr("New receiving address")); + ui->addressEdit->setEnabled(false); + break; + case NewSendingAddress: + setWindowTitle(tr("New sending address")); + break; + case EditReceivingAddress: + setWindowTitle(tr("Edit receiving address")); + ui->addressEdit->setReadOnly(true); + break; + case EditSendingAddress: + setWindowTitle(tr("Edit sending address")); + break; + } + + mapper = new QDataWidgetMapper(this); + mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); +} + +EditAddressDialog::~EditAddressDialog() +{ + delete ui; +} + +void EditAddressDialog::setModel(AddressTableModel *model) +{ + this->model = model; + mapper->setModel(model); + mapper->addMapping(ui->labelEdit, AddressTableModel::Label); + mapper->addMapping(ui->addressEdit, AddressTableModel::Address); +} + +void EditAddressDialog::loadRow(int row) +{ + mapper->setCurrentIndex(row); +} + +QString EditAddressDialog::saveCurrentRow() +{ + QString address; + switch(mode) + { + case NewReceivingAddress: + case NewSendingAddress: + address = model->addRow( + mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive, + ui->labelEdit->text(), + ui->addressEdit->text()); + if(address.isEmpty()) + { + QMessageBox::warning(this, windowTitle(), + tr("The address %1 is already in the address book.").arg(ui->addressEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); + } + break; + case EditReceivingAddress: + case EditSendingAddress: + if(mapper->submit()) + { + address = ui->addressEdit->text(); + } + break; + } + return address; +} diff --git a/src/qt/editaddressdialog.h b/src/qt/editaddressdialog.h new file mode 100644 index 0000000000..6f396d0457 --- /dev/null +++ b/src/qt/editaddressdialog.h @@ -0,0 +1,41 @@ +#ifndef EDITADDRESSDIALOG_H +#define EDITADDRESSDIALOG_H + +#include + +QT_BEGIN_NAMESPACE +class QDataWidgetMapper; +QT_END_NAMESPACE + +namespace Ui { + class EditAddressDialog; +} +class AddressTableModel; + +class EditAddressDialog : public QDialog +{ + Q_OBJECT + +public: + enum Mode { + NewReceivingAddress, + NewSendingAddress, + EditReceivingAddress, + EditSendingAddress + }; + + explicit EditAddressDialog(Mode mode, QWidget *parent = 0); + ~EditAddressDialog(); + + void setModel(AddressTableModel *model); + void loadRow(int row); + QString saveCurrentRow(); + +private: + Ui::EditAddressDialog *ui; + QDataWidgetMapper *mapper; + Mode mode; + AddressTableModel *model; +}; + +#endif // EDITADDRESSDIALOG_H diff --git a/src/qt/forms/aboutdialog.ui b/src/qt/forms/aboutdialog.ui new file mode 100644 index 0000000000..45915c85bf --- /dev/null +++ b/src/qt/forms/aboutdialog.ui @@ -0,0 +1,162 @@ + + + AboutDialog + + + + 0 + 0 + 593 + 319 + + + + About Bitcoin + + + + + + + 0 + 0 + + + + + + + :/images/about + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + <b>Bitcoin</b> version + + + + + + + 0.3.666-beta + + + Qt::RichText + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Copyright © 2009-2011 Bitcoin Developers + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file license.txt or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 360 + 308 + + + 157 + 274 + + + + + buttonBox + rejected() + AboutDialog + reject() + + + 428 + 308 + + + 286 + 274 + + + + + diff --git a/src/qt/forms/addressbookdialog.ui b/src/qt/forms/addressbookdialog.ui new file mode 100644 index 0000000000..2b3c69fb97 --- /dev/null +++ b/src/qt/forms/addressbookdialog.ui @@ -0,0 +1,196 @@ + + + AddressBookDialog + + + + 0 + 0 + 591 + 347 + + + + Address Book + + + + + + 0 + + + + + + + Sending + + + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + + + + + + + + + Receiving + + + + + + These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. The highlighted address is displayed in the main window. + + + Qt::AutoText + + + true + + + + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Create a new address + + + &New Address... + + + + + + + Copy the currently selected address to the system clipboard + + + &Copy to Clipboard + + + + + + + Edit the currently selected address + + + &Edit... + + + + + + + Delete the currently selected address from the list + + + &Delete + + + + + + + + 0 + 0 + + + + QDialogButtonBox::Ok + + + + + + + + + + + receiveTableView + doubleClicked(QModelIndex) + editButton + click() + + + 334 + 249 + + + 333 + 326 + + + + + sendTableView + doubleClicked(QModelIndex) + editButton + click() + + + 329 + 261 + + + 332 + 326 + + + + + diff --git a/src/qt/forms/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui new file mode 100644 index 0000000000..f0ba28a854 --- /dev/null +++ b/src/qt/forms/editaddressdialog.ui @@ -0,0 +1,105 @@ + + + EditAddressDialog + + + + 0 + 0 + 458 + 113 + + + + Edit Address + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + &Label + + + labelEdit + + + + + + + &Address + + + addressEdit + + + + + + + The label associated with this address book entry + + + + + + + The address associated with this address book entry. This can only be modified for sending addresses. + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + EditAddressDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + EditAddressDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui new file mode 100644 index 0000000000..595b7f40ff --- /dev/null +++ b/src/qt/forms/sendcoinsdialog.ui @@ -0,0 +1,180 @@ + + + SendCoinsDialog + + + + 0 + 0 + 736 + 149 + + + + Send Coins + + + + + + + + &Amount: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + payAmount + + + + + + + Pay &To: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + payTo + + + + + + + The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + + + 34 + + + + + + + + 145 + 16777215 + + + + Amount of bitcoins to send (e.g. 0.05) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Paste address from system clipboard + + + &Paste + + + false + + + + + + + Look up adress in address book + + + Address &Book... + + + false + + + + + + + + 9 + + + + Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Confirm the send action + + + &Send + + + + :/icons/send:/icons/send + + + true + + + + + + + + 0 + 0 + + + + Abort the send action + + + QDialogButtonBox::Cancel + + + + + + + + + + + + diff --git a/src/qt/forms/transactiondescdialog.ui b/src/qt/forms/transactiondescdialog.ui new file mode 100644 index 0000000000..2f70a38214 --- /dev/null +++ b/src/qt/forms/transactiondescdialog.ui @@ -0,0 +1,71 @@ + + + TransactionDescDialog + + + + 0 + 0 + 400 + 300 + + + + Transaction details + + + + + + This pane shows a detailed description of the transaction + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + TransactionDescDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TransactionDescDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h new file mode 100644 index 0000000000..cdd1a74d98 --- /dev/null +++ b/src/qt/guiconstants.h @@ -0,0 +1,11 @@ +#ifndef GUICONSTANTS_H +#define GUICONSTANTS_H + +/* milliseconds between model updates */ +static const int MODEL_UPDATE_DELAY = 250; + +/* size of cache */ +static const unsigned int WALLET_CACHE_SIZE = 100; + + +#endif // GUICONSTANTS_H diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp new file mode 100644 index 0000000000..a81cc14fe8 --- /dev/null +++ b/src/qt/guiutil.cpp @@ -0,0 +1,38 @@ +#include "guiutil.h" +#include "bitcoinaddressvalidator.h" + +#include +#include +#include +#include +#include + +QString GUIUtil::DateTimeStr(qint64 nTime) +{ + QDateTime date = QDateTime::fromMSecsSinceEpoch(nTime*1000); + return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm"); +} + +QFont GUIUtil::bitcoinAddressFont() +{ + QFont font("Monospace"); + font.setStyleHint(QFont::TypeWriter); + return font; +} + +void GUIUtil::setupAddressWidget(QLineEdit *widget, QWidget *parent) +{ + widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength); + widget->setValidator(new BitcoinAddressValidator(parent)); + widget->setFont(bitcoinAddressFont()); +} + +void GUIUtil::setupAmountWidget(QLineEdit *widget, QWidget *parent) +{ + QDoubleValidator *amountValidator = new QDoubleValidator(parent); + amountValidator->setDecimals(8); + amountValidator->setBottom(0.0); + widget->setValidator(amountValidator); + widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter); +} + diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h new file mode 100644 index 0000000000..748e29bf37 --- /dev/null +++ b/src/qt/guiutil.h @@ -0,0 +1,25 @@ +#ifndef GUIUTIL_H +#define GUIUTIL_H + +#include + +QT_BEGIN_NAMESPACE +class QFont; +class QLineEdit; +class QWidget; +QT_END_NAMESPACE + +class GUIUtil +{ +public: + static QString DateTimeStr(qint64 nTime); + + /* Render bitcoin addresses in monospace font */ + static QFont bitcoinAddressFont(); + + static void setupAddressWidget(QLineEdit *widget, QWidget *parent); + + static void setupAmountWidget(QLineEdit *widget, QWidget *parent); +}; + +#endif // GUIUTIL_H diff --git a/src/qt/monitoreddatamapper.cpp b/src/qt/monitoreddatamapper.cpp new file mode 100644 index 0000000000..e70aa7ebf6 --- /dev/null +++ b/src/qt/monitoreddatamapper.cpp @@ -0,0 +1,39 @@ +#include "monitoreddatamapper.h" + +#include +#include +#include +#include + + +MonitoredDataMapper::MonitoredDataMapper(QObject *parent) : + QDataWidgetMapper(parent) +{ +} + + +void MonitoredDataMapper::addMapping(QWidget *widget, int section) +{ + QDataWidgetMapper::addMapping(widget, section); + addChangeMonitor(widget); +} + +void MonitoredDataMapper::addMapping(QWidget *widget, int section, const QByteArray &propertyName) +{ + QDataWidgetMapper::addMapping(widget, section, propertyName); + addChangeMonitor(widget); +} + +void MonitoredDataMapper::addChangeMonitor(QWidget *widget) +{ + /* Watch user property of widget for changes, and connect + the signal to our viewModified signal. + */ + QMetaProperty prop = widget->metaObject()->userProperty(); + int signal = prop.notifySignalIndex(); + int method = this->metaObject()->indexOfMethod("viewModified()"); + if(signal != -1 && method != -1) + { + QMetaObject::connect(widget, signal, this, method); + } +} diff --git a/src/qt/monitoreddatamapper.h b/src/qt/monitoreddatamapper.h new file mode 100644 index 0000000000..4dd2d1a86a --- /dev/null +++ b/src/qt/monitoreddatamapper.h @@ -0,0 +1,32 @@ +#ifndef MONITOREDDATAMAPPER_H +#define MONITOREDDATAMAPPER_H + +#include + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +/* Data <-> Widget mapper that watches for changes, + to be able to notify when 'dirty' (for example, to + enable a commit/apply button). + */ +class MonitoredDataMapper : public QDataWidgetMapper +{ + Q_OBJECT +public: + explicit MonitoredDataMapper(QObject *parent=0); + + void addMapping(QWidget *widget, int section); + void addMapping(QWidget *widget, int section, const QByteArray &propertyName); +private: + void addChangeMonitor(QWidget *widget); + +signals: + void viewModified(); + +}; + + + +#endif // MONITOREDDATAMAPPER_H diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp new file mode 100644 index 0000000000..d46b48fb29 --- /dev/null +++ b/src/qt/optionsdialog.cpp @@ -0,0 +1,234 @@ +#include "optionsdialog.h" +#include "optionsmodel.h" +#include "monitoreddatamapper.h" +#include "guiutil.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* First (currently only) page of options */ +class MainOptionsPage : public QWidget +{ +public: + explicit MainOptionsPage(QWidget *parent=0); + + void setMapper(MonitoredDataMapper *mapper); +private: + QCheckBox *bitcoin_at_startup; + QCheckBox *minimize_to_tray; + QCheckBox *map_port_upnp; + QCheckBox *minimize_on_close; + QCheckBox *connect_socks4; + QLineEdit *proxy_ip; + QLineEdit *proxy_port; + QLineEdit *fee_edit; + +signals: + +public slots: + +}; + +OptionsDialog::OptionsDialog(QWidget *parent): + QDialog(parent), contents_widget(0), pages_widget(0), + main_options_page(0), model(0) +{ + contents_widget = new QListWidget(); + contents_widget->setMaximumWidth(128); + + pages_widget = new QStackedWidget(); + pages_widget->setMinimumWidth(300); + + QListWidgetItem *item_main = new QListWidgetItem(tr("Main")); + contents_widget->addItem(item_main); + main_options_page = new MainOptionsPage(this); + pages_widget->addWidget(main_options_page); + + contents_widget->setCurrentRow(0); + + QHBoxLayout *main_layout = new QHBoxLayout(); + main_layout->addWidget(contents_widget); + main_layout->addWidget(pages_widget, 1); + + QVBoxLayout *layout = new QVBoxLayout(); + layout->addLayout(main_layout); + + QHBoxLayout *buttons = new QHBoxLayout(); + buttons->addStretch(1); + QPushButton *ok_button = new QPushButton(tr("OK")); + buttons->addWidget(ok_button); + QPushButton *cancel_button = new QPushButton(tr("Cancel")); + buttons->addWidget(cancel_button); + apply_button = new QPushButton(tr("Apply")); + apply_button->setEnabled(false); + buttons->addWidget(apply_button); + + layout->addLayout(buttons); + + setLayout(layout); + setWindowTitle(tr("Options")); + + /* Widget-to-option mapper */ + mapper = new MonitoredDataMapper(this); + mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); + mapper->setOrientation(Qt::Vertical); + /* enable apply button when data modified */ + connect(mapper, SIGNAL(viewModified()), this, SLOT(enableApply())); + /* disable apply button when new data loaded */ + connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApply())); + + /* Event bindings */ + connect(ok_button, SIGNAL(clicked()), this, SLOT(okClicked())); + connect(cancel_button, SIGNAL(clicked()), this, SLOT(cancelClicked())); + connect(apply_button, SIGNAL(clicked()), this, SLOT(applyClicked())); +} + +void OptionsDialog::setModel(OptionsModel *model) +{ + this->model = model; + + mapper->setModel(model); + main_options_page->setMapper(mapper); + + mapper->toFirst(); +} + +void OptionsDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) +{ + Q_UNUSED(previous); + if(current) + { + pages_widget->setCurrentIndex(contents_widget->row(current)); + } +} + +void OptionsDialog::okClicked() +{ + mapper->submit(); + accept(); +} + +void OptionsDialog::cancelClicked() +{ + reject(); +} + +void OptionsDialog::applyClicked() +{ + mapper->submit(); + apply_button->setEnabled(false); +} + +void OptionsDialog::enableApply() +{ + apply_button->setEnabled(true); +} + +void OptionsDialog::disableApply() +{ + apply_button->setEnabled(false); +} + +MainOptionsPage::MainOptionsPage(QWidget *parent): + QWidget(parent) +{ + QVBoxLayout *layout = new QVBoxLayout(); + + bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup")); + bitcoin_at_startup->setToolTip(tr("Automatically start Bitcoin after the computer is turned on")); + layout->addWidget(bitcoin_at_startup); + + minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar")); + minimize_to_tray->setToolTip(tr("Show only a tray icon after minimizing the window")); + layout->addWidget(minimize_to_tray); + + map_port_upnp = new QCheckBox(tr("Map port using &UPnP")); + map_port_upnp->setToolTip(tr("Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.")); + layout->addWidget(map_port_upnp); + + minimize_on_close = new QCheckBox(tr("M&inimize on close")); + minimize_on_close->setToolTip(tr("Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu.")); + layout->addWidget(minimize_on_close); + + connect_socks4 = new QCheckBox(tr("&Connect through SOCKS4 proxy:")); + connect_socks4->setToolTip(tr("Connect to the Bitcon network through a SOCKS4 proxy (e.g. when connecting through Tor)")); + layout->addWidget(connect_socks4); + + QHBoxLayout *proxy_hbox = new QHBoxLayout(); + proxy_hbox->addSpacing(18); + QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP: ")); + proxy_hbox->addWidget(proxy_ip_label); + proxy_ip = new QLineEdit(); + proxy_ip->setMaximumWidth(140); + proxy_ip->setEnabled(false); + proxy_ip->setValidator(new QRegExpValidator(QRegExp("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"), this)); + proxy_ip->setToolTip(tr("IP address of the proxy (e.g. 127.0.0.1)")); + proxy_ip_label->setBuddy(proxy_ip); + proxy_hbox->addWidget(proxy_ip); + QLabel *proxy_port_label = new QLabel(tr("&Port: ")); + proxy_hbox->addWidget(proxy_port_label); + proxy_port = new QLineEdit(); + proxy_port->setMaximumWidth(55); + proxy_port->setValidator(new QIntValidator(0, 65535, this)); + proxy_port->setEnabled(false); + proxy_port->setToolTip(tr("Port of the proxy (e.g. 1234)")); + proxy_port_label->setBuddy(proxy_port); + proxy_hbox->addWidget(proxy_port); + proxy_hbox->addStretch(1); + + layout->addLayout(proxy_hbox); + QLabel *fee_help = new QLabel(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended.")); + fee_help->setWordWrap(true); + layout->addWidget(fee_help); + + QHBoxLayout *fee_hbox = new QHBoxLayout(); + fee_hbox->addSpacing(18); + QLabel *fee_label = new QLabel(tr("Pay transaction &fee")); + fee_hbox->addWidget(fee_label); + fee_edit = new QLineEdit(); + fee_edit->setMaximumWidth(100); + fee_edit->setToolTip(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended.")); + + GUIUtil::setupAmountWidget(fee_edit, this); + + fee_label->setBuddy(fee_edit); + fee_hbox->addWidget(fee_edit); + fee_hbox->addStretch(1); + + layout->addLayout(fee_hbox); + + layout->addStretch(1); /* Extra space at bottom */ + + setLayout(layout); + + connect(connect_socks4, SIGNAL(toggled(bool)), proxy_ip, SLOT(setEnabled(bool))); + connect(connect_socks4, SIGNAL(toggled(bool)), proxy_port, SLOT(setEnabled(bool))); + +#ifndef USE_UPNP + map_port_upnp->setDisabled(true); +#endif +} + +void MainOptionsPage::setMapper(MonitoredDataMapper *mapper) +{ + /* Map model to widgets */ + mapper->addMapping(bitcoin_at_startup, OptionsModel::StartAtStartup); + mapper->addMapping(minimize_to_tray, OptionsModel::MinimizeToTray); + mapper->addMapping(map_port_upnp, OptionsModel::MapPortUPnP); + mapper->addMapping(minimize_on_close, OptionsModel::MinimizeOnClose); + mapper->addMapping(connect_socks4, OptionsModel::ConnectSOCKS4); + mapper->addMapping(proxy_ip, OptionsModel::ProxyIP); + mapper->addMapping(proxy_port, OptionsModel::ProxyPort); + mapper->addMapping(fee_edit, OptionsModel::Fee); +} + diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h new file mode 100644 index 0000000000..07e85297d5 --- /dev/null +++ b/src/qt/optionsdialog.h @@ -0,0 +1,45 @@ +#ifndef OPTIONSDIALOG_H +#define OPTIONSDIALOG_H + +#include + +QT_BEGIN_NAMESPACE +class QStackedWidget; +class QListWidget; +class QListWidgetItem; +class QPushButton; +QT_END_NAMESPACE +class OptionsModel; +class MainOptionsPage; +class MonitoredDataMapper; + +class OptionsDialog : public QDialog +{ + Q_OBJECT +public: + explicit OptionsDialog(QWidget *parent=0); + + void setModel(OptionsModel *model); + +signals: + +public slots: + void changePage(QListWidgetItem *current, QListWidgetItem *previous); +private slots: + void okClicked(); + void cancelClicked(); + void applyClicked(); + void enableApply(); + void disableApply(); +private: + QListWidget *contents_widget; + QStackedWidget *pages_widget; + MainOptionsPage *main_options_page; + OptionsModel *model; + MonitoredDataMapper *mapper; + QPushButton *apply_button; + + void setupMainPage(); +}; + +#endif // OPTIONSDIALOG_H diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp new file mode 100644 index 0000000000..1528fdf697 --- /dev/null +++ b/src/qt/optionsmodel.cpp @@ -0,0 +1,140 @@ +#include "optionsmodel.h" +#include "main.h" +#include "net.h" + +#include + +OptionsModel::OptionsModel(QObject *parent) : + QAbstractListModel(parent) +{ +} + +int OptionsModel::rowCount(const QModelIndex & parent) const +{ + return OptionIDRowCount; +} + +QVariant OptionsModel::data(const QModelIndex & index, int role) const +{ + if(role == Qt::EditRole) + { + switch(index.row()) + { + case StartAtStartup: + return QVariant(); + case MinimizeToTray: + return QVariant(fMinimizeToTray); + case MapPortUPnP: + return QVariant(fUseUPnP); + case MinimizeOnClose: + return QVariant(fMinimizeOnClose); + case ConnectSOCKS4: + return QVariant(fUseProxy); + case ProxyIP: + return QVariant(QString::fromStdString(addrProxy.ToStringIP())); + case ProxyPort: + return QVariant(QString::fromStdString(addrProxy.ToStringPort())); + case Fee: + return QVariant(QString::fromStdString(FormatMoney(nTransactionFee))); + default: + return QVariant(); + } + } + return QVariant(); +} + +bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role) +{ + bool successful = true; /* set to false on parse error */ + if(role == Qt::EditRole) + { + CWalletDB walletdb; + switch(index.row()) + { + case StartAtStartup: + successful = false; /*TODO*/ + break; + case MinimizeToTray: + fMinimizeToTray = value.toBool(); + walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray); + break; + case MapPortUPnP: + fUseUPnP = value.toBool(); + walletdb.WriteSetting("fUseUPnP", fUseUPnP); +#ifdef USE_UPNP + MapPort(fUseUPnP); +#endif + break; + case MinimizeOnClose: + fMinimizeOnClose = value.toBool(); + walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose); + break; + case ConnectSOCKS4: + fUseProxy = value.toBool(); + walletdb.WriteSetting("fUseProxy", fUseProxy); + break; + case ProxyIP: + { + /* Use CAddress to parse IP */ + CAddress addr(value.toString().toStdString() + ":1"); + if (addr.ip != INADDR_NONE) + { + addrProxy.ip = addr.ip; + walletdb.WriteSetting("addrProxy", addrProxy); + } + else + { + successful = false; + } + } + break; + case ProxyPort: + { + int nPort = atoi(value.toString().toAscii().data()); + if (nPort > 0 && nPort < USHRT_MAX) + { + addrProxy.port = htons(nPort); + walletdb.WriteSetting("addrProxy", addrProxy); + } + else + { + successful = false; + } + } + break; + case Fee: { + int64 retval; + if(ParseMoney(value.toString().toStdString(), retval)) + { + nTransactionFee = retval; + walletdb.WriteSetting("nTransactionFee", nTransactionFee); + } + else + { + successful = false; /* parse error */ + } + } + break; + default: + break; + } + } + emit dataChanged(index, index); + + return successful; +} + +qint64 OptionsModel::getTransactionFee() +{ + return nTransactionFee; +} + +bool OptionsModel::getMinimizeToTray() +{ + return fMinimizeToTray; +} + +bool OptionsModel::getMinimizeOnClose() +{ + return fMinimizeOnClose; +} diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h new file mode 100644 index 0000000000..0124e2ab47 --- /dev/null +++ b/src/qt/optionsmodel.h @@ -0,0 +1,44 @@ +#ifndef OPTIONSMODEL_H +#define OPTIONSMODEL_H + +#include + +/* Interface from QT to configuration data structure for bitcoin client. + To QT, the options are presented as a list with the different options + laid out vertically. + This can be changed to a tree once the settings become sufficiently + complex. + */ +class OptionsModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit OptionsModel(QObject *parent = 0); + + enum OptionID { + StartAtStartup, + MinimizeToTray, + MapPortUPnP, + MinimizeOnClose, + ConnectSOCKS4, + ProxyIP, + ProxyPort, + Fee, + OptionIDRowCount + }; + + int rowCount(const QModelIndex & parent = QModelIndex()) const; + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); + + /* Explicit getters */ + qint64 getTransactionFee(); + bool getMinimizeToTray(); + bool getMinimizeOnClose(); +signals: + +public slots: + +}; + +#endif // OPTIONSMODEL_H diff --git a/src/qt/res/icons/address-book.png b/src/qt/res/icons/address-book.png new file mode 100644 index 0000000000..abfb3c3a51 Binary files /dev/null and b/src/qt/res/icons/address-book.png differ diff --git a/src/qt/res/icons/bitcoin.png b/src/qt/res/icons/bitcoin.png new file mode 100644 index 0000000000..09520aca23 Binary files /dev/null and b/src/qt/res/icons/bitcoin.png differ diff --git a/src/qt/res/icons/quit.png b/src/qt/res/icons/quit.png new file mode 100644 index 0000000000..fb510fbea6 Binary files /dev/null and b/src/qt/res/icons/quit.png differ diff --git a/src/qt/res/icons/send.png b/src/qt/res/icons/send.png new file mode 100644 index 0000000000..0ba5359d7b Binary files /dev/null and b/src/qt/res/icons/send.png differ diff --git a/src/qt/res/icons/toolbar.png b/src/qt/res/icons/toolbar.png new file mode 100644 index 0000000000..f2dcb20636 Binary files /dev/null and b/src/qt/res/icons/toolbar.png differ diff --git a/src/qt/res/images/about.png b/src/qt/res/images/about.png new file mode 100644 index 0000000000..c9ab9511ef Binary files /dev/null and b/src/qt/res/images/about.png differ diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp new file mode 100644 index 0000000000..721ab14108 --- /dev/null +++ b/src/qt/sendcoinsdialog.cpp @@ -0,0 +1,113 @@ +#include "sendcoinsdialog.h" +#include "ui_sendcoinsdialog.h" +#include "clientmodel.h" +#include "guiutil.h" + +#include "addressbookdialog.h" +#include "optionsmodel.h" + +#include +#include +#include +#include +#include + +#include "util.h" +#include "base58.h" + +SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : + QDialog(parent), + ui(new Ui::SendCoinsDialog), + model(0) +{ + ui->setupUi(this); + + GUIUtil::setupAddressWidget(ui->payTo, this); + GUIUtil::setupAmountWidget(ui->payAmount, this); + + /* Set initial address if provided */ + if(!address.isEmpty()) + { + ui->payTo->setText(address); + ui->payAmount->setFocus(); + } +} + +void SendCoinsDialog::setModel(ClientModel *model) +{ + this->model = model; +} + +SendCoinsDialog::~SendCoinsDialog() +{ + delete ui; +} + +void SendCoinsDialog::on_sendButton_clicked() +{ + bool valid; + QString payAmount = ui->payAmount->text(); + qint64 payAmountParsed; + + valid = ParseMoney(payAmount.toStdString(), payAmountParsed); + + if(!valid) + { + QMessageBox::warning(this, tr("Send Coins"), + tr("The amount to pay must be a valid number."), + QMessageBox::Ok, QMessageBox::Ok); + return; + } + + switch(model->sendCoins(ui->payTo->text(), payAmountParsed)) + { + case ClientModel::InvalidAddress: + QMessageBox::warning(this, tr("Send Coins"), + tr("The recepient address is not valid, please recheck."), + QMessageBox::Ok, QMessageBox::Ok); + ui->payTo->setFocus(); + break; + case ClientModel::InvalidAmount: + QMessageBox::warning(this, tr("Send Coins"), + tr("The amount to pay must be larger than 0."), + QMessageBox::Ok, QMessageBox::Ok); + ui->payAmount->setFocus(); + break; + case ClientModel::AmountExceedsBalance: + QMessageBox::warning(this, tr("Send Coins"), + tr("Amount exceeds your balance"), + QMessageBox::Ok, QMessageBox::Ok); + ui->payAmount->setFocus(); + break; + case ClientModel::AmountWithFeeExceedsBalance: + QMessageBox::warning(this, tr("Send Coins"), + tr("Total exceeds your balance when the %1 transaction fee is included"). + arg(QString::fromStdString(FormatMoney(model->getOptionsModel()->getTransactionFee()))), + QMessageBox::Ok, QMessageBox::Ok); + ui->payAmount->setFocus(); + break; + case ClientModel::OK: + accept(); + break; + } +} + +void SendCoinsDialog::on_pasteButton_clicked() +{ + /* Paste text from clipboard into recipient field */ + ui->payTo->setText(QApplication::clipboard()->text()); +} + +void SendCoinsDialog::on_addressBookButton_clicked() +{ + AddressBookDialog dlg; + dlg.setModel(model->getAddressTableModel()); + dlg.setTab(AddressBookDialog::SendingTab); + dlg.exec(); + ui->payTo->setText(dlg.getReturnValue()); +} + +void SendCoinsDialog::on_buttonBox_rejected() +{ + reject(); +} diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h new file mode 100644 index 0000000000..f73c38d63a --- /dev/null +++ b/src/qt/sendcoinsdialog.h @@ -0,0 +1,32 @@ +#ifndef SENDCOINSDIALOG_H +#define SENDCOINSDIALOG_H + +#include + +namespace Ui { + class SendCoinsDialog; +} +class ClientModel; + +class SendCoinsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SendCoinsDialog(QWidget *parent = 0, const QString &address = ""); + ~SendCoinsDialog(); + + void setModel(ClientModel *model); + +private: + Ui::SendCoinsDialog *ui; + ClientModel *model; + +private slots: + void on_buttonBox_rejected(); + void on_addressBookButton_clicked(); + void on_pasteButton_clicked(); + void on_sendButton_clicked(); +}; + +#endif // SENDCOINSDIALOG_H diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp new file mode 100644 index 0000000000..4d8a55e99a --- /dev/null +++ b/src/qt/transactiondesc.cpp @@ -0,0 +1,310 @@ +#include + +#include "guiutil.h" +#include "main.h" + +#include + +/* Taken straight from ui.cpp + TODO: Convert to use QStrings, Qt::Escape and tr() + */ + +using namespace std; + +static string HtmlEscape(const char* psz, bool fMultiLine=false) +{ + int len = 0; + for (const char* p = psz; *p; p++) + { + if (*p == '<') len += 4; + else if (*p == '>') len += 4; + else if (*p == '&') len += 5; + else if (*p == '"') len += 6; + else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6; + else if (*p == '\n' && fMultiLine) len += 5; + else + len++; + } + string str; + str.reserve(len); + for (const char* p = psz; *p; p++) + { + if (*p == '<') str += "<"; + else if (*p == '>') str += ">"; + else if (*p == '&') str += "&"; + else if (*p == '"') str += """; + else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " "; + else if (*p == '\n' && fMultiLine) str += "
\n"; + else + str += *p; + } + return str; +} + +static string HtmlEscape(const string& str, bool fMultiLine=false) +{ + return HtmlEscape(str.c_str(), fMultiLine); +} + +static string FormatTxStatus(const CWalletTx& wtx) +{ + // Status + if (!wtx.IsFinal()) + { + if (wtx.nLockTime < 500000000) + return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime); + else + return strprintf(_("Open until %s"), GUIUtil::DateTimeStr(wtx.nLockTime).toStdString().c_str()); + } + else + { + int nDepth = wtx.GetDepthInMainChain(); + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + return strprintf(_("%d/offline?"), nDepth); + else if (nDepth < 6) + return strprintf(_("%d/unconfirmed"), nDepth); + else + return strprintf(_("%d confirmations"), nDepth); + } +} + +string TransactionDesc::toHTML(CWalletTx &wtx) +{ + string strHTML; + CRITICAL_BLOCK(cs_mapAddressBook) + { + strHTML.reserve(4000); + strHTML += ""; + + int64 nTime = wtx.GetTxTime(); + int64 nCredit = wtx.GetCredit(); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + + + + strHTML += _("Status: ") + FormatTxStatus(wtx); + int nRequests = wtx.GetRequestCount(); + if (nRequests != -1) + { + if (nRequests == 0) + strHTML += _(", has not been successfully broadcast yet"); + else if (nRequests == 1) + strHTML += strprintf(_(", broadcast through %d node"), nRequests); + else + strHTML += strprintf(_(", broadcast through %d nodes"), nRequests); + } + strHTML += "
"; + + strHTML += _("Date: ") + (nTime ? GUIUtil::DateTimeStr(nTime).toStdString() : "") + "
"; + + + // + // From + // + if (wtx.IsCoinBase()) + { + strHTML += _("Source: Generated
"); + } + else if (!wtx.mapValue["from"].empty()) + { + // Online transaction + if (!wtx.mapValue["from"].empty()) + strHTML += _("From: ") + HtmlEscape(wtx.mapValue["from"]) + "
"; + } + else + { + // Offline transaction + if (nNet > 0) + { + // Credit + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (txout.IsMine()) + { + vector vchPubKey; + if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) + { + string strAddress = PubKeyToAddress(vchPubKey); + if (mapAddressBook.count(strAddress)) + { + strHTML += string() + _("From: ") + _("unknown") + "
"; + strHTML += _("To: "); + strHTML += HtmlEscape(strAddress); + if (!mapAddressBook[strAddress].empty()) + strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")"; + else + strHTML += _(" (yours)"); + strHTML += "
"; + } + } + break; + } + } + } + } + + + // + // To + // + string strAddress; + if (!wtx.mapValue["to"].empty()) + { + // Online transaction + strAddress = wtx.mapValue["to"]; + strHTML += _("To: "); + if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty()) + strHTML += mapAddressBook[strAddress] + " "; + strHTML += HtmlEscape(strAddress) + "
"; + } + + + // + // Amount + // + if (wtx.IsCoinBase() && nCredit == 0) + { + // + // Coinbase + // + int64 nUnmatured = 0; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + nUnmatured += txout.GetCredit(); + strHTML += _("Credit: "); + if (wtx.IsInMainChain()) + strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity()); + else + strHTML += _("(not accepted)"); + strHTML += "
"; + } + else if (nNet > 0) + { + // + // Credit + // + strHTML += _("Credit: ") + FormatMoney(nNet) + "
"; + } + else + { + bool fAllFromMe = true; + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + fAllFromMe = fAllFromMe && txin.IsMine(); + + bool fAllToMe = true; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + fAllToMe = fAllToMe && txout.IsMine(); + + if (fAllFromMe) + { + // + // Debit + // + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (txout.IsMine()) + continue; + + if (wtx.mapValue["to"].empty()) + { + // Offline transaction + uint160 hash160; + if (ExtractHash160(txout.scriptPubKey, hash160)) + { + string strAddress = Hash160ToAddress(hash160); + strHTML += _("To: "); + if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty()) + strHTML += mapAddressBook[strAddress] + " "; + strHTML += strAddress; + strHTML += "
"; + } + } + + strHTML += _("Debit: ") + FormatMoney(-txout.nValue) + "
"; + } + + if (fAllToMe) + { + // Payment to self + int64 nChange = wtx.GetChange(); + int64 nValue = nCredit - nChange; + strHTML += _("Debit: ") + FormatMoney(-nValue) + "
"; + strHTML += _("Credit: ") + FormatMoney(nValue) + "
"; + } + + int64 nTxFee = nDebit - wtx.GetValueOut(); + if (nTxFee > 0) + strHTML += _("Transaction fee: ") + FormatMoney(-nTxFee) + "
"; + } + else + { + // + // Mixed debit transaction + // + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + if (txin.IsMine()) + strHTML += _("Debit: ") + FormatMoney(-txin.GetDebit()) + "
"; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.IsMine()) + strHTML += _("Credit: ") + FormatMoney(txout.GetCredit()) + "
"; + } + } + + strHTML += _("Net amount: ") + FormatMoney(nNet, true) + "
"; + + + // + // Message + // + if (!wtx.mapValue["message"].empty()) + strHTML += string() + "
" + _("Message:") + "
" + HtmlEscape(wtx.mapValue["message"], true) + "
"; + if (!wtx.mapValue["comment"].empty()) + strHTML += string() + "
" + _("Comment:") + "
" + HtmlEscape(wtx.mapValue["comment"], true) + "
"; + + if (wtx.IsCoinBase()) + strHTML += string() + "
" + _("Generated coins must wait 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, it will change to \"not accepted\" and not be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.") + "
"; + + + // + // Debug view + // + if (fDebug) + { + strHTML += "

debug print

"; + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + if (txin.IsMine()) + strHTML += "Debit: " + FormatMoney(-txin.GetDebit()) + "
"; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.IsMine()) + strHTML += "Credit: " + FormatMoney(txout.GetCredit()) + "
"; + + strHTML += "
Transaction:
"; + strHTML += HtmlEscape(wtx.ToString(), true); + + strHTML += "
Inputs:
"; + CRITICAL_BLOCK(cs_mapWallet) + { + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + { + COutPoint prevout = txin.prevout; + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (prevout.n < prev.vout.size()) + { + strHTML += HtmlEscape(prev.ToString(), true); + strHTML += "    " + FormatTxStatus(prev) + ", "; + strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "
"; + } + } + } + } + } + + + + strHTML += "
"; + } + return strHTML; +} diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h new file mode 100644 index 0000000000..5a85949341 --- /dev/null +++ b/src/qt/transactiondesc.h @@ -0,0 +1,15 @@ +#ifndef TRANSACTIONDESC_H +#define TRANSACTIONDESC_H + +#include + +class CWalletTx; + +class TransactionDesc +{ +public: + /* Provide human-readable extended HTML description of a transaction */ + static std::string toHTML(CWalletTx &wtx); +}; + +#endif // TRANSACTIONDESC_H diff --git a/src/qt/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp new file mode 100644 index 0000000000..3bd4808cb6 --- /dev/null +++ b/src/qt/transactiondescdialog.cpp @@ -0,0 +1,20 @@ +#include "transactiondescdialog.h" +#include "ui_transactiondescdialog.h" + +#include "transactiontablemodel.h" + +#include + +TransactionDescDialog::TransactionDescDialog(const QModelIndex &idx, QWidget *parent) : + QDialog(parent), + ui(new Ui::TransactionDescDialog) +{ + ui->setupUi(this); + QString desc = idx.data(TransactionTableModel::LongDescriptionRole).toString(); + ui->detailText->setHtml(desc); +} + +TransactionDescDialog::~TransactionDescDialog() +{ + delete ui; +} diff --git a/src/qt/transactiondescdialog.h b/src/qt/transactiondescdialog.h new file mode 100644 index 0000000000..4f8f754b2b --- /dev/null +++ b/src/qt/transactiondescdialog.h @@ -0,0 +1,25 @@ +#ifndef TRANSACTIONDESCDIALOG_H +#define TRANSACTIONDESCDIALOG_H + +#include + +namespace Ui { + class TransactionDescDialog; +} +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE + +class TransactionDescDialog : public QDialog +{ + Q_OBJECT + +public: + explicit TransactionDescDialog(const QModelIndex &idx, QWidget *parent = 0); + ~TransactionDescDialog(); + +private: + Ui::TransactionDescDialog *ui; +}; + +#endif // TRANSACTIONDESCDIALOG_H diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp new file mode 100644 index 0000000000..6c1f3a5e58 --- /dev/null +++ b/src/qt/transactionrecord.cpp @@ -0,0 +1,254 @@ +#include "transactionrecord.h" + + +/* Return positive answer if transaction should be shown in list. + */ +bool TransactionRecord::showTransaction(const CWalletTx &wtx) +{ + if (wtx.IsCoinBase()) + { + // Don't show generated coin until confirmed by at least one block after it + // so we don't get the user's hopes up until it looks like it's probably accepted. + // + // It is not an error when generated blocks are not accepted. By design, + // some percentage of blocks, like 10% or more, will end up not accepted. + // This is the normal mechanism by which the network copes with latency. + // + // We display regular transactions right away before any confirmation + // because they can always get into some block eventually. Generated coins + // are special because if their block is not accepted, they are not valid. + // + if (wtx.GetDepthInMainChain() < 2) + { + return false; + } + } + return true; +} + +/* Decompose CWallet transaction to model transaction records. + */ +QList TransactionRecord::decomposeTransaction(const CWalletTx &wtx) +{ + QList parts; + int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime(); + int64 nCredit = wtx.GetCredit(true); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + uint256 hash = wtx.GetHash(); + std::map mapValue = wtx.mapValue; + + if (showTransaction(wtx)) + { + if (nNet > 0 || wtx.IsCoinBase()) + { + // + // Credit + // + TransactionRecord sub(hash, nTime); + + sub.credit = nNet; + + if (wtx.IsCoinBase()) + { + // Generated + sub.type = TransactionRecord::Generated; + + if (nCredit == 0) + { + int64 nUnmatured = 0; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + nUnmatured += txout.GetCredit(); + sub.credit = nUnmatured; + } + } + else if (!mapValue["from"].empty() || !mapValue["message"].empty()) + { + // Received by IP connection + sub.type = TransactionRecord::RecvFromIP; + if (!mapValue["from"].empty()) + sub.address = mapValue["from"]; + } + else + { + // Received by Bitcoin Address + sub.type = TransactionRecord::RecvWithAddress; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (txout.IsMine()) + { + std::vector vchPubKey; + if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) + { + sub.address = PubKeyToAddress(vchPubKey); + } + break; + } + } + } + parts.append(sub); + } + else + { + bool fAllFromMe = true; + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + fAllFromMe = fAllFromMe && txin.IsMine(); + + bool fAllToMe = true; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + fAllToMe = fAllToMe && txout.IsMine(); + + if (fAllFromMe && fAllToMe) + { + // Payment to self + int64 nChange = wtx.GetChange(); + + parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", + -(nDebit - nChange), nCredit - nChange)); + } + else if (fAllFromMe) + { + // + // Debit + // + int64 nTxFee = nDebit - wtx.GetValueOut(); + + for (int nOut = 0; nOut < wtx.vout.size(); nOut++) + { + const CTxOut& txout = wtx.vout[nOut]; + TransactionRecord sub(hash, nTime); + sub.idx = parts.size(); + + if (txout.IsMine()) + { + // Ignore parts sent to self, as this is usually the change + // from a transaction sent back to our own address. + continue; + } + else if (!mapValue["to"].empty()) + { + // Sent to IP + sub.type = TransactionRecord::SendToIP; + sub.address = mapValue["to"]; + } + else + { + // Sent to Bitcoin Address + sub.type = TransactionRecord::SendToAddress; + uint160 hash160; + if (ExtractHash160(txout.scriptPubKey, hash160)) + sub.address = Hash160ToAddress(hash160); + } + + int64 nValue = txout.nValue; + /* Add fee to first output */ + if (nTxFee > 0) + { + nValue += nTxFee; + nTxFee = 0; + } + sub.debit = -nValue; + + parts.append(sub); + } + } + else + { + // + // Mixed debit transaction, can't break down payees + // + bool fAllMine = true; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + fAllMine = fAllMine && txout.IsMine(); + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + fAllMine = fAllMine && txin.IsMine(); + + parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0)); + } + } + } + + return parts; +} + +void TransactionRecord::updateStatus(const CWalletTx &wtx) +{ + // Determine transaction status + + // Find the block the tx is in + CBlockIndex* pindex = NULL; + std::map::iterator mi = mapBlockIndex.find(wtx.hashBlock); + if (mi != mapBlockIndex.end()) + pindex = (*mi).second; + + // Sort order, unrecorded transactions sort to the top + status.sortKey = strprintf("%010d-%01d-%010u-%03d", + (pindex ? pindex->nHeight : INT_MAX), + (wtx.IsCoinBase() ? 1 : 0), + wtx.nTimeReceived, + idx); + status.confirmed = wtx.IsConfirmed(); + status.depth = wtx.GetDepthInMainChain(); + status.cur_num_blocks = nBestHeight; + + if (!wtx.IsFinal()) + { + if (wtx.nLockTime < 500000000) + { + status.status = TransactionStatus::OpenUntilBlock; + status.open_for = nBestHeight - wtx.nLockTime; + } + else + { + status.status = TransactionStatus::OpenUntilDate; + status.open_for = wtx.nLockTime; + } + } + else + { + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + { + status.status = TransactionStatus::Offline; + } + else if (status.depth < 6) + { + status.status = TransactionStatus::Unconfirmed; + } + else + { + status.status = TransactionStatus::HaveConfirmations; + } + } + + // For generated transactions, determine maturity + if(type == TransactionRecord::Generated) + { + int64 nCredit = wtx.GetCredit(true); + if (nCredit == 0) + { + status.maturity = TransactionStatus::Immature; + + if (wtx.IsInMainChain()) + { + status.matures_in = wtx.GetBlocksToMaturity(); + + // Check if the block was requested by anyone + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + status.maturity = TransactionStatus::MaturesWarning; + } + else + { + status.maturity = TransactionStatus::NotAccepted; + } + } + else + { + status.maturity = TransactionStatus::Mature; + } + } +} + +bool TransactionRecord::statusUpdateNeeded() +{ + return status.cur_num_blocks != nBestHeight; +} diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h new file mode 100644 index 0000000000..c082fffe9c --- /dev/null +++ b/src/qt/transactionrecord.h @@ -0,0 +1,109 @@ +#ifndef TRANSACTIONRECORD_H +#define TRANSACTIONRECORD_H + +#include "main.h" + +#include + +class TransactionStatus +{ +public: + TransactionStatus(): + confirmed(false), sortKey(""), maturity(Mature), + matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1) + { } + + enum Maturity + { + Immature, + Mature, + MaturesWarning, /* Will likely not mature because no nodes have confirmed */ + NotAccepted + }; + + enum Status { + OpenUntilDate, + OpenUntilBlock, + Offline, + Unconfirmed, + HaveConfirmations + }; + + bool confirmed; + std::string sortKey; + + /* For "Generated" transactions */ + Maturity maturity; + int matures_in; + + /* Reported status */ + Status status; + int64 depth; + int64 open_for; /* Timestamp if status==OpenUntilDate, otherwise number of blocks */ + + /* Current number of blocks (to know whether cached status is still valid. */ + int cur_num_blocks; +}; + +class TransactionRecord +{ +public: + enum Type + { + Other, + Generated, + SendToAddress, + SendToIP, + RecvWithAddress, + RecvFromIP, + SendToSelf + }; + + TransactionRecord(): + hash(), time(0), type(Other), address(""), debit(0), credit(0), idx(0) + { + } + + TransactionRecord(uint256 hash, int64 time): + hash(hash), time(time), type(Other), address(""), debit(0), + credit(0), idx(0) + { + } + + TransactionRecord(uint256 hash, int64 time, + Type type, const std::string &address, + int64 debit, int64 credit): + hash(hash), time(time), type(type), address(address), debit(debit), credit(credit), + idx(0) + { + } + + /* Decompose CWallet transaction to model transaction records. + */ + static bool showTransaction(const CWalletTx &wtx); + static QList decomposeTransaction(const CWalletTx &wtx); + + /* Fixed */ + uint256 hash; + int64 time; + Type type; + std::string address; + int64 debit; + int64 credit; + + /* Subtransaction index, for sort key */ + int idx; + + /* Status: can change with block chain update */ + TransactionStatus status; + + /* Update status from wallet tx. + */ + void updateStatus(const CWalletTx &wtx); + + /* Is a status update needed? + */ + bool statusUpdateNeeded(); +}; + +#endif // TRANSACTIONRECORD_H diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp new file mode 100644 index 0000000000..8fe1839930 --- /dev/null +++ b/src/qt/transactiontablemodel.cpp @@ -0,0 +1,512 @@ +#include "transactiontablemodel.h" +#include "guiutil.h" +#include "transactionrecord.h" +#include "guiconstants.h" +#include "main.h" +#include "transactiondesc.h" + +#include +#include +#include +#include +#include +#include + +const QString TransactionTableModel::Sent = "s"; +const QString TransactionTableModel::Received = "r"; +const QString TransactionTableModel::Other = "o"; + +/* Comparison operator for sort/binary search of model tx list */ +struct TxLessThan +{ + bool operator()(const TransactionRecord &a, const TransactionRecord &b) const + { + return a.hash < b.hash; + } + bool operator()(const TransactionRecord &a, const uint256 &b) const + { + return a.hash < b; + } + bool operator()(const uint256 &a, const TransactionRecord &b) const + { + return a < b.hash; + } +}; + +/* Private implementation */ +struct TransactionTablePriv +{ + TransactionTablePriv(TransactionTableModel *parent): + parent(parent) + { + } + + TransactionTableModel *parent; + + /* Local cache of wallet. + * As it is in the same order as the CWallet, by definition + * this is sorted by sha256. + */ + QList cachedWallet; + + void refreshWallet() + { + qDebug() << "refreshWallet"; + + /* Query entire wallet from core. + */ + cachedWallet.clear(); + CRITICAL_BLOCK(cs_mapWallet) + { + for(std::map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + cachedWallet.append(TransactionRecord::decomposeTransaction(it->second)); + } + } + } + + /* Update our model of the wallet incrementally. + Call with list of hashes of transactions that were added, removed or changed. + */ + void updateWallet(const QList &updated) + { + /* Walk through updated transactions, update model as needed. + */ + qDebug() << "updateWallet"; + + /* Sort update list, and iterate through it in reverse, so that model updates + can be emitted from end to beginning (so that earlier updates will not influence + the indices of latter ones). + */ + QList updated_sorted = updated; + qSort(updated_sorted); + + CRITICAL_BLOCK(cs_mapWallet) + { + for(int update_idx = updated_sorted.size()-1; update_idx >= 0; --update_idx) + { + const uint256 &hash = updated_sorted.at(update_idx); + /* Find transaction in wallet */ + std::map::iterator mi = mapWallet.find(hash); + bool inWallet = mi != mapWallet.end(); + /* Find bounds of this transaction in model */ + QList::iterator lower = qLowerBound( + cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); + QList::iterator upper = qUpperBound( + cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); + int lowerIndex = (lower - cachedWallet.begin()); + int upperIndex = (upper - cachedWallet.begin()); + + bool inModel = false; + if(lower != upper) + { + inModel = true; + } + + qDebug() << " " << QString::fromStdString(hash.ToString()) << inWallet << " " << inModel + << lowerIndex << "-" << upperIndex; + + if(inWallet && !inModel) + { + /* Added */ + QList toInsert = + TransactionRecord::decomposeTransaction(mi->second); + if(!toInsert.isEmpty()) /* only if something to insert */ + { + parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); + int insert_idx = lowerIndex; + foreach(const TransactionRecord &rec, toInsert) + { + cachedWallet.insert(insert_idx, rec); + insert_idx += 1; + } + parent->endInsertRows(); + } + } + else if(!inWallet && inModel) + { + /* Removed */ + parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); + cachedWallet.erase(lower, upper); + parent->endRemoveRows(); + } + else if(inWallet && inModel) + { + /* Updated -- nothing to do, status update will take care of this */ + } + } + } + } + + int size() + { + return cachedWallet.size(); + } + + TransactionRecord *index(int idx) + { + if(idx >= 0 && idx < cachedWallet.size()) + { + TransactionRecord *rec = &cachedWallet[idx]; + + /* If a status update is needed (blocks came in since last check), + update the status of this transaction from the wallet. Otherwise, + simply re-use the cached status. + */ + if(rec->statusUpdateNeeded()) + { + CRITICAL_BLOCK(cs_mapWallet) + { + std::map::iterator mi = mapWallet.find(rec->hash); + + if(mi != mapWallet.end()) + { + rec->updateStatus(mi->second); + } + } + } + return rec; + } + else + { + return 0; + } + } + + QString describe(TransactionRecord *rec) + { + CRITICAL_BLOCK(cs_mapWallet) + { + std::map::iterator mi = mapWallet.find(rec->hash); + if(mi != mapWallet.end()) + { + return QString::fromStdString(TransactionDesc::toHTML(mi->second)); + } + } + return QString(""); + } + +}; + +/* Credit and Debit columns are right-aligned as they contain numbers */ +static int column_alignments[] = { + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignRight|Qt::AlignVCenter, + Qt::AlignRight|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter + }; + +TransactionTableModel::TransactionTableModel(QObject *parent): + QAbstractTableModel(parent), + priv(new TransactionTablePriv(this)) +{ + columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); + + priv->refreshWallet(); + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(update())); + timer->start(MODEL_UPDATE_DELAY); +} + +TransactionTableModel::~TransactionTableModel() +{ + delete priv; +} + +void TransactionTableModel::update() +{ + QList updated; + + /* Check if there are changes to wallet map */ + TRY_CRITICAL_BLOCK(cs_mapWallet) + { + if(!vWalletUpdated.empty()) + { + BOOST_FOREACH(uint256 hash, vWalletUpdated) + { + updated.append(hash); + } + vWalletUpdated.clear(); + } + } + + if(!updated.empty()) + { + priv->updateWallet(updated); + + /* Status (number of confirmations) and (possibly) description + columns changed for all rows. + */ + emit dataChanged(index(0, Status), index(priv->size()-1, Status)); + emit dataChanged(index(0, Description), index(priv->size()-1, Description)); + } +} + +int TransactionTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return priv->size(); +} + +int TransactionTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} + +QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const +{ + QString status; + + switch(wtx->status.status) + { + case TransactionStatus::OpenUntilBlock: + status = tr("Open for %n block(s)","",wtx->status.open_for); + break; + case TransactionStatus::OpenUntilDate: + status = tr("Open until ") + GUIUtil::DateTimeStr(wtx->status.open_for); + break; + case TransactionStatus::Offline: + status = tr("%1/offline").arg(wtx->status.depth); + break; + case TransactionStatus::Unconfirmed: + status = tr("%1/unconfirmed").arg(wtx->status.depth); + break; + case TransactionStatus::HaveConfirmations: + status = tr("%1 confirmations").arg(wtx->status.depth); + break; + } + + return QVariant(status); +} + +QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const +{ + if(wtx->time) + { + return QVariant(GUIUtil::DateTimeStr(wtx->time)); + } + else + { + return QVariant(); + } +} + +/* Look up address in address book, if found return + address[0:12]... (label) + otherwise just return address + */ +std::string lookupAddress(const std::string &address) +{ + std::string description; + CRITICAL_BLOCK(cs_mapAddressBook) + { + std::map::iterator mi = mapAddressBook.find(address); + if (mi != mapAddressBook.end() && !(*mi).second.empty()) + { + std::string label = (*mi).second; + description += address.substr(0,12) + "... "; + description += "(" + label + ")"; + } + else + { + description += address; + } + } + return description; +} + +QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx) const +{ + QString description; + + switch(wtx->type) + { + case TransactionRecord::RecvWithAddress: + description = tr("Received with: ") + QString::fromStdString(lookupAddress(wtx->address)); + break; + case TransactionRecord::RecvFromIP: + description = tr("Received from IP: ") + QString::fromStdString(wtx->address); + break; + case TransactionRecord::SendToAddress: + description = tr("Sent to: ") + QString::fromStdString(lookupAddress(wtx->address)); + break; + case TransactionRecord::SendToIP: + description = tr("Sent to IP: ") + QString::fromStdString(wtx->address); + break; + case TransactionRecord::SendToSelf: + description = tr("Payment to yourself"); + break; + case TransactionRecord::Generated: + switch(wtx->status.maturity) + { + case TransactionStatus::Immature: + description = tr("Generated (matures in %n more blocks)", "", + wtx->status.matures_in); + break; + case TransactionStatus::Mature: + description = tr("Generated"); + break; + case TransactionStatus::MaturesWarning: + description = tr("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!"); + break; + case TransactionStatus::NotAccepted: + description = tr("Generated (not accepted)"); + break; + } + break; + } + return QVariant(description); +} + +QVariant TransactionTableModel::formatTxDebit(const TransactionRecord *wtx) const +{ + if(wtx->debit) + { + QString str = QString::fromStdString(FormatMoney(wtx->debit)); + if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) + { + str = QString("[") + str + QString("]"); + } + return QVariant(str); + } + else + { + return QVariant(); + } +} + +QVariant TransactionTableModel::formatTxCredit(const TransactionRecord *wtx) const +{ + if(wtx->credit) + { + QString str = QString::fromStdString(FormatMoney(wtx->credit)); + if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) + { + str = QString("[") + str + QString("]"); + } + return QVariant(str); + } + else + { + return QVariant(); + } +} + +QVariant TransactionTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + TransactionRecord *rec = static_cast(index.internalPointer()); + + if(role == Qt::DisplayRole) + { + /* Delegate to specific column handlers */ + switch(index.column()) + { + case Status: + return formatTxStatus(rec); + case Date: + return formatTxDate(rec); + case Description: + return formatTxDescription(rec); + case Debit: + return formatTxDebit(rec); + case Credit: + return formatTxCredit(rec); + } + } + else if(role == Qt::EditRole) + { + /* Edit role is used for sorting so return the real values */ + switch(index.column()) + { + case Status: + return QString::fromStdString(rec->status.sortKey); + case Date: + return rec->time; + case Description: + return formatTxDescription(rec); + case Debit: + return rec->debit; + case Credit: + return rec->credit; + } + } + else if (role == Qt::TextAlignmentRole) + { + return column_alignments[index.column()]; + } + else if (role == Qt::ForegroundRole) + { + /* Non-confirmed transactions are grey */ + if(rec->status.confirmed) + { + return QColor(0, 0, 0); + } + else + { + return QColor(128, 128, 128); + } + } + else if (role == TypeRole) + { + /* Role for filtering tabs by type */ + switch(rec->type) + { + case TransactionRecord::RecvWithAddress: + case TransactionRecord::RecvFromIP: + return TransactionTableModel::Received; + case TransactionRecord::SendToAddress: + case TransactionRecord::SendToIP: + case TransactionRecord::SendToSelf: + return TransactionTableModel::Sent; + default: + return TransactionTableModel::Other; + } + } + else if (role == LongDescriptionRole) + { + return priv->describe(rec); + } + return QVariant(); +} + +QVariant TransactionTableModel::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]; + } + } + return QVariant(); +} + +Qt::ItemFlags TransactionTableModel::flags(const QModelIndex &index) const +{ + return QAbstractTableModel::flags(index); +} + +QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + TransactionRecord *data = priv->index(row); + if(data) + { + return createIndex(row, column, priv->index(row)); + } + else + { + return QModelIndex(); + } +} + diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h new file mode 100644 index 0000000000..5974c0e7fe --- /dev/null +++ b/src/qt/transactiontablemodel.h @@ -0,0 +1,58 @@ +#ifndef TRANSACTIONTABLEMODEL_H +#define TRANSACTIONTABLEMODEL_H + +#include +#include + +class TransactionTablePriv; +class TransactionRecord; + +class TransactionTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit TransactionTableModel(QObject *parent = 0); + ~TransactionTableModel(); + + enum { + Status = 0, + Date = 1, + Description = 2, + Debit = 3, + Credit = 4 + } ColumnIndex; + + enum { + TypeRole = Qt::UserRole, + LongDescriptionRole = Qt::UserRole+1 + } RoleIndex; + + /* TypeRole values */ + static const QString Sent; + static const QString Received; + static const QString Other; + + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; +private: + QStringList columns; + TransactionTablePriv *priv; + + QVariant formatTxStatus(const TransactionRecord *wtx) const; + QVariant formatTxDate(const TransactionRecord *wtx) const; + QVariant formatTxDescription(const TransactionRecord *wtx) const; + QVariant formatTxDebit(const TransactionRecord *wtx) const; + QVariant formatTxCredit(const TransactionRecord *wtx) const; + +private slots: + void update(); + + friend class TransactionTablePriv; +}; + +#endif + diff --git a/src/rpc.cpp b/src/rpc.cpp new file mode 100644 index 0000000000..530bef4a43 --- /dev/null +++ b/src/rpc.cpp @@ -0,0 +1,2209 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" +#include "cryptopp/sha.h" +#undef printf +#include +#include +#include +#ifdef USE_SSL +#include +typedef boost::asio::ssl::stream SSLStream; +#endif +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_writer_template.h" +#include "json/json_spirit_utils.h" +#define printf OutputDebugStringF +// MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are +// precompiled in headers.h. The problem might be when the pch file goes over +// a certain size around 145MB. If we need access to json_spirit outside this +// file, we could use the compiled json_spirit option. + +using namespace std; +using namespace boost; +using namespace boost::asio; +using namespace json_spirit; + +void ThreadRPCServer2(void* parg); +typedef Value(*rpcfn_type)(const Array& params, bool fHelp); +extern map mapCallTable; + + +Object JSONRPCError(int code, const string& message) +{ + Object error; + error.push_back(Pair("code", code)); + error.push_back(Pair("message", message)); + return error; +} + + +void PrintConsole(const char* format, ...) +{ + char buffer[50000]; + int limit = sizeof(buffer); + va_list arg_ptr; + va_start(arg_ptr, format); + int ret = _vsnprintf(buffer, limit, format, arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + ret = limit - 1; + buffer[limit-1] = 0; + } + printf("%s", buffer); +#if defined(__WXMSW__) && defined(GUI) + MyMessageBox(buffer, "Bitcoin", wxOK | wxICON_EXCLAMATION); +#else + fprintf(stdout, "%s", buffer); +#endif +} + + +int64 AmountFromValue(const Value& value) +{ + double dAmount = value.get_real(); + if (dAmount <= 0.0 || dAmount > 21000000.0) + throw JSONRPCError(-3, "Invalid amount"); + int64 nAmount = roundint64(dAmount * COIN); + if (!MoneyRange(nAmount)) + throw JSONRPCError(-3, "Invalid amount"); + return nAmount; +} + +Value ValueFromAmount(int64 amount) +{ + return (double)amount / (double)COIN; +} + +void WalletTxToJSON(const CWalletTx& wtx, Object& entry) +{ + entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain())); + entry.push_back(Pair("txid", wtx.GetHash().GetHex())); + entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); + BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) + entry.push_back(Pair(item.first, item.second)); +} + +string AccountFromValue(const Value& value) +{ + string strAccount = value.get_str(); + if (strAccount == "*") + throw JSONRPCError(-11, "Invalid account name"); + return strAccount; +} + + + +/// +/// Note: This interface may still be subject to change. +/// + + +Value help(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "help [command]\n" + "List commands, or get help for a command."); + + string strCommand; + if (params.size() > 0) + strCommand = params[0].get_str(); + + string strRet; + set setDone; + for (map::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi) + { + string strMethod = (*mi).first; + // We already filter duplicates, but these deprecated screw up the sort order + if (strMethod == "getamountreceived" || + strMethod == "getallreceived" || + (strMethod.find("label") != string::npos)) + continue; + if (strCommand != "" && strMethod != strCommand) + continue; + try + { + Array params; + rpcfn_type pfn = (*mi).second; + if (setDone.insert(pfn).second) + (*pfn)(params, true); + } + catch (std::exception& e) + { + // Help text is returned in an exception + string strHelp = string(e.what()); + if (strCommand == "") + if (strHelp.find('\n') != -1) + strHelp = strHelp.substr(0, strHelp.find('\n')); + strRet += strHelp + "\n"; + } + } + if (strRet == "") + strRet = strprintf("help: unknown command: %s\n", strCommand.c_str()); + strRet = strRet.substr(0,strRet.size()-1); + return strRet; +} + + +Value stop(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "stop\n" + "Stop bitcoin server."); + + // Shutdown will take long enough that the response should get back + CreateThread(Shutdown, NULL); + return "bitcoin server stopping"; +} + + +Value getblockcount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getblockcount\n" + "Returns the number of blocks in the longest block chain."); + + return nBestHeight; +} + + +Value getblocknumber(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getblocknumber\n" + "Returns the block number of the latest block in the longest block chain."); + + return nBestHeight; +} + + +Value getconnectioncount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getconnectioncount\n" + "Returns the number of connections to other nodes."); + + return (int)vNodes.size(); +} + + +double GetDifficulty() +{ + // Floating point number that is a multiple of the minimum difficulty, + // minimum difficulty = 1.0. + + if (pindexBest == NULL) + return 1.0; + int nShift = (pindexBest->nBits >> 24) & 0xff; + + double dDiff = + (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff); + + while (nShift < 29) + { + dDiff *= 256.0; + nShift++; + } + while (nShift > 29) + { + dDiff /= 256.0; + nShift--; + } + + return dDiff; +} + +Value getdifficulty(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getdifficulty\n" + "Returns the proof-of-work difficulty as a multiple of the minimum difficulty."); + + return GetDifficulty(); +} + + +Value getgenerate(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getgenerate\n" + "Returns true or false."); + + return (bool)fGenerateBitcoins; +} + + +Value setgenerate(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "setgenerate [genproclimit]\n" + " is true or false to turn generation on or off.\n" + "Generation is limited to [genproclimit] processors, -1 is unlimited."); + + bool fGenerate = true; + if (params.size() > 0) + fGenerate = params[0].get_bool(); + + if (params.size() > 1) + { + int nGenProcLimit = params[1].get_int(); + fLimitProcessors = (nGenProcLimit != -1); + CWalletDB().WriteSetting("fLimitProcessors", fLimitProcessors); + if (nGenProcLimit != -1) + CWalletDB().WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit); + if (nGenProcLimit == 0) + fGenerate = false; + } + + GenerateBitcoins(fGenerate); + return Value::null; +} + + +Value gethashespersec(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "gethashespersec\n" + "Returns a recent hashes per second performance measurement while generating."); + + if (GetTimeMillis() - nHPSTimerStart > 8000) + return (boost::int64_t)0; + return (boost::int64_t)dHashesPerSec; +} + + +Value getinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getinfo\n" + "Returns an object containing various state info."); + + Object obj; + obj.push_back(Pair("version", (int)VERSION)); + obj.push_back(Pair("balance", ValueFromAmount(GetBalance()))); + obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("connections", (int)vNodes.size())); + obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string()))); + obj.push_back(Pair("generate", (bool)fGenerateBitcoins)); + obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1))); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("hashespersec", gethashespersec(params, false))); + obj.push_back(Pair("testnet", fTestNet)); + obj.push_back(Pair("keypoololdest", (boost::int64_t)GetOldestKeyPoolTime())); + obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + return obj; +} + + +Value getnewaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getnewaddress [account]\n" + "Returns a new bitcoin address for receiving payments. " + "If [account] is specified (recommended), it is added to the address book " + "so payments received with the address will be credited to [account]."); + + // Parse the account first so we don't generate a key if there's an error + string strAccount; + if (params.size() > 0) + strAccount = AccountFromValue(params[0]); + + // Generate a new key that is added to wallet + string strAddress = PubKeyToAddress(GetKeyFromKeyPool()); + + SetAddressBookName(strAddress, strAccount); + return strAddress; +} + + +// requires cs_main, cs_mapWallet locks +string GetAccountAddress(string strAccount, bool bForceNew=false) +{ + string strAddress; + + CWalletDB walletdb; + walletdb.TxnBegin(); + + CAccount account; + walletdb.ReadAccount(strAccount, account); + + // Check if the current key has been used + if (!account.vchPubKey.empty()) + { + CScript scriptPubKey; + scriptPubKey.SetBitcoinAddress(account.vchPubKey); + for (map::iterator it = mapWallet.begin(); + it != mapWallet.end() && !account.vchPubKey.empty(); + ++it) + { + const CWalletTx& wtx = (*it).second; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + account.vchPubKey.clear(); + } + } + + // Generate a new key + if (account.vchPubKey.empty() || bForceNew) + { + account.vchPubKey = GetKeyFromKeyPool(); + string strAddress = PubKeyToAddress(account.vchPubKey); + SetAddressBookName(strAddress, strAccount); + walletdb.WriteAccount(strAccount, account); + } + + walletdb.TxnCommit(); + strAddress = PubKeyToAddress(account.vchPubKey); + + return strAddress; +} + +Value getaccountaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaccountaddress \n" + "Returns the current bitcoin address for receiving payments to this account."); + + // Parse the account first so we don't generate a key if there's an error + string strAccount = AccountFromValue(params[0]); + + Value ret; + + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + { + ret = GetAccountAddress(strAccount); + } + + return ret; +} + + + +Value setaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "setaccount \n" + "Sets the account associated with the given address."); + + string strAddress = params[0].get_str(); + uint160 hash160; + bool isValid = AddressToHash160(strAddress, hash160); + if (!isValid) + throw JSONRPCError(-5, "Invalid bitcoin address"); + + + string strAccount; + if (params.size() > 1) + strAccount = AccountFromValue(params[1]); + + // Detect when changing the account of an address that is the 'unused current key' of another account: + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_mapAddressBook) + { + if (mapAddressBook.count(strAddress)) + { + string strOldAccount = mapAddressBook[strAddress]; + if (strAddress == GetAccountAddress(strOldAccount)) + GetAccountAddress(strOldAccount, true); + } + } + + SetAddressBookName(strAddress, strAccount); + return Value::null; +} + + +Value getaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaccount \n" + "Returns the account associated with the given address."); + + string strAddress = params[0].get_str(); + + string strAccount; + CRITICAL_BLOCK(cs_mapAddressBook) + { + map::iterator mi = mapAddressBook.find(strAddress); + if (mi != mapAddressBook.end() && !(*mi).second.empty()) + strAccount = (*mi).second; + } + return strAccount; +} + + +Value getaddressesbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaddressesbyaccount \n" + "Returns the list of addresses for the given account."); + + string strAccount = AccountFromValue(params[0]); + + // Find all addresses that have the given account + Array ret; + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook) + { + const string& strAddress = item.first; + const string& strName = item.second; + if (strName == strAccount) + { + // We're only adding valid bitcoin addresses and not ip addresses + CScript scriptPubKey; + if (scriptPubKey.SetBitcoinAddress(strAddress)) + ret.push_back(strAddress); + } + } + } + return ret; +} + +Value settxfee(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "settxfee \n" + " is a real and is rounded to the nearest 0.00000001"); + + // Amount + int64 nAmount = 0; + if (params[0].get_real() != 0.0) + nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts + + nTransactionFee = nAmount; + return true; +} + +Value sendtoaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "sendtoaddress [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.00000001"); + + string strAddress = params[0].get_str(); + + // Amount + int64 nAmount = AmountFromValue(params[1]); + + // Wallet comments + CWalletTx wtx; + if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty()) + wtx.mapValue["comment"] = params[2].get_str(); + if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) + wtx.mapValue["to"] = params[3].get_str(); + + CRITICAL_BLOCK(cs_main) + { + string strError = SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); + if (strError != "") + throw JSONRPCError(-4, strError); + } + + return wtx.GetHash().GetHex(); +} + + +Value getreceivedbyaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getreceivedbyaddress [minconf=1]\n" + "Returns the total amount received by in transactions with at least [minconf] confirmations."); + + // Bitcoin address + string strAddress = params[0].get_str(); + CScript scriptPubKey; + if (!scriptPubKey.SetBitcoinAddress(strAddress)) + throw JSONRPCError(-5, "Invalid bitcoin address"); + if (!IsMine(scriptPubKey)) + return (double)0.0; + + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + // Tally + int64 nAmount = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + } + + return ValueFromAmount(nAmount); +} + + +void GetAccountPubKeys(string strAccount, set& setPubKey) +{ + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook) + { + const string& strAddress = item.first; + const string& strName = item.second; + if (strName == strAccount) + { + // We're only counting our own valid bitcoin addresses and not ip addresses + CScript scriptPubKey; + if (scriptPubKey.SetBitcoinAddress(strAddress)) + if (IsMine(scriptPubKey)) + setPubKey.insert(scriptPubKey); + } + } + } +} + + +Value getreceivedbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getreceivedbyaccount [minconf=1]\n" + "Returns the total amount received by addresses with in transactions with at least [minconf] confirmations."); + + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + // Get the set of pub keys that have the label + string strAccount = AccountFromValue(params[0]); + set setPubKey; + GetAccountPubKeys(strAccount, setPubKey); + + // Tally + int64 nAmount = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (setPubKey.count(txout.scriptPubKey)) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + } + + return (double)nAmount / (double)COIN; +} + + +int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) +{ + int64 nBalance = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + // Tally wallet transactions + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!wtx.IsFinal()) + continue; + + int64 nGenerated, nReceived, nSent, nFee; + wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee); + + if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) + nBalance += nReceived; + nBalance += nGenerated - nSent - nFee; + } + + // Tally internal accounting entries + nBalance += walletdb.GetAccountCreditDebit(strAccount); + } + + return nBalance; +} + +int64 GetAccountBalance(const string& strAccount, int nMinDepth) +{ + CWalletDB walletdb; + return GetAccountBalance(walletdb, strAccount, nMinDepth); +} + + +Value getbalance(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 0 || params.size() > 2) + throw runtime_error( + "getbalance [account] [minconf=1]\n" + "If [account] is not specified, returns the server's total available balance.\n" + "If [account] is specified, returns the balance in the account."); + + if (params.size() == 0) + return ValueFromAmount(GetBalance()); + + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + if (params[0].get_str() == "*") { + // Calculate total balance a different way from GetBalance() + // (GetBalance() sums up all unspent TxOuts) + // getbalance and getbalance '*' should always return the same number. + int64 nBalance = 0; + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!wtx.IsFinal()) + continue; + + int64 allGeneratedImmature, allGeneratedMature, allFee; + allGeneratedImmature = allGeneratedMature = allFee = 0; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); + if (wtx.GetDepthInMainChain() >= nMinDepth) + BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) + nBalance += r.second; + BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listSent) + nBalance -= r.second; + nBalance -= allFee; + nBalance += allGeneratedMature; + } + return ValueFromAmount(nBalance); + } + + string strAccount = AccountFromValue(params[0]); + + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + + return ValueFromAmount(nBalance); +} + + +Value movecmd(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 5) + throw runtime_error( + "move [minconf=1] [comment]\n" + "Move from one account in your wallet to another."); + + string strFrom = AccountFromValue(params[0]); + string strTo = AccountFromValue(params[1]); + int64 nAmount = AmountFromValue(params[2]); + int nMinDepth = 1; + if (params.size() > 3) + nMinDepth = params[3].get_int(); + string strComment; + if (params.size() > 4) + strComment = params[4].get_str(); + + CRITICAL_BLOCK(cs_mapWallet) + { + CWalletDB walletdb; + walletdb.TxnBegin(); + + int64 nNow = GetAdjustedTime(); + + // Debit + CAccountingEntry debit; + debit.strAccount = strFrom; + debit.nCreditDebit = -nAmount; + debit.nTime = nNow; + debit.strOtherAccount = strTo; + debit.strComment = strComment; + walletdb.WriteAccountingEntry(debit); + + // Credit + CAccountingEntry credit; + credit.strAccount = strTo; + credit.nCreditDebit = nAmount; + credit.nTime = nNow; + credit.strOtherAccount = strFrom; + credit.strComment = strComment; + walletdb.WriteAccountingEntry(credit); + + walletdb.TxnCommit(); + } + return true; +} + + +Value sendfrom(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 6) + throw runtime_error( + "sendfrom [minconf=1] [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.00000001"); + + string strAccount = AccountFromValue(params[0]); + string strAddress = params[1].get_str(); + int64 nAmount = AmountFromValue(params[2]); + int nMinDepth = 1; + if (params.size() > 3) + nMinDepth = params[3].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) + wtx.mapValue["comment"] = params[4].get_str(); + if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) + wtx.mapValue["to"] = params[5].get_str(); + + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + { + // Check funds + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + if (nAmount > nBalance) + throw JSONRPCError(-6, "Account has insufficient funds"); + + // Send + string strError = SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); + if (strError != "") + throw JSONRPCError(-4, strError); + } + + return wtx.GetHash().GetHex(); +} + +Value sendmany(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "sendmany {address:amount,...} [minconf=1] [comment]\n" + "amounts are double-precision floating point numbers"); + + string strAccount = AccountFromValue(params[0]); + Object sendTo = params[1].get_obj(); + int nMinDepth = 1; + if (params.size() > 2) + nMinDepth = params[2].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) + wtx.mapValue["comment"] = params[3].get_str(); + + set setAddress; + vector > vecSend; + + int64 totalAmount = 0; + BOOST_FOREACH(const Pair& s, sendTo) + { + uint160 hash160; + string strAddress = s.name_; + + if (setAddress.count(strAddress)) + throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+strAddress); + setAddress.insert(strAddress); + + CScript scriptPubKey; + if (!scriptPubKey.SetBitcoinAddress(strAddress)) + throw JSONRPCError(-5, string("Invalid bitcoin address:")+strAddress); + int64 nAmount = AmountFromValue(s.value_); + totalAmount += nAmount; + + vecSend.push_back(make_pair(scriptPubKey, nAmount)); + } + + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + { + // Check funds + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + if (totalAmount > nBalance) + throw JSONRPCError(-6, "Account has insufficient funds"); + + // Send + CReserveKey keyChange; + int64 nFeeRequired = 0; + bool fCreated = CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); + if (!fCreated) + { + if (totalAmount + nFeeRequired > GetBalance()) + throw JSONRPCError(-6, "Insufficient funds"); + throw JSONRPCError(-4, "Transaction creation failed"); + } + if (!CommitTransaction(wtx, keyChange)) + throw JSONRPCError(-4, "Transaction commit failed"); + } + + return wtx.GetHash().GetHex(); +} + + +struct tallyitem +{ + int64 nAmount; + int nConf; + tallyitem() + { + nAmount = 0; + nConf = INT_MAX; + } +}; + +Value ListReceived(const Array& params, bool fByAccounts) +{ + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + // Whether to include empty accounts + bool fIncludeEmpty = false; + if (params.size() > 1) + fIncludeEmpty = params[1].get_bool(); + + // Tally + map mapTally; + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + int nDepth = wtx.GetDepthInMainChain(); + if (nDepth < nMinDepth) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + // Only counting our own bitcoin addresses and not ip addresses + uint160 hash160 = txout.scriptPubKey.GetBitcoinAddressHash160(); + if (hash160 == 0 || !mapPubKeys.count(hash160)) // IsMine + continue; + + tallyitem& item = mapTally[hash160]; + item.nAmount += txout.nValue; + item.nConf = min(item.nConf, nDepth); + } + } + } + + // Reply + Array ret; + map mapAccountTally; + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook) + { + const string& strAddress = item.first; + const string& strAccount = item.second; + uint160 hash160; + if (!AddressToHash160(strAddress, hash160)) + continue; + map::iterator it = mapTally.find(hash160); + if (it == mapTally.end() && !fIncludeEmpty) + continue; + + int64 nAmount = 0; + int nConf = INT_MAX; + if (it != mapTally.end()) + { + nAmount = (*it).second.nAmount; + nConf = (*it).second.nConf; + } + + if (fByAccounts) + { + tallyitem& item = mapAccountTally[strAccount]; + item.nAmount += nAmount; + item.nConf = min(item.nConf, nConf); + } + else + { + Object obj; + obj.push_back(Pair("address", strAddress)); + obj.push_back(Pair("account", strAccount)); + obj.push_back(Pair("label", strAccount)); // deprecated + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); + ret.push_back(obj); + } + } + } + + if (fByAccounts) + { + for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) + { + int64 nAmount = (*it).second.nAmount; + int nConf = (*it).second.nConf; + Object obj; + obj.push_back(Pair("account", (*it).first)); + obj.push_back(Pair("label", (*it).first)); // deprecated + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); + ret.push_back(obj); + } + } + + return ret; +} + +Value listreceivedbyaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "listreceivedbyaddress [minconf=1] [includeempty=false]\n" + "[minconf] is the minimum number of confirmations before payments are included.\n" + "[includeempty] whether to include addresses that haven't received any payments.\n" + "Returns an array of objects containing:\n" + " \"address\" : receiving address\n" + " \"account\" : the account of the receiving address\n" + " \"amount\" : total amount received by the address\n" + " \"confirmations\" : number of confirmations of the most recent transaction included"); + + return ListReceived(params, false); +} + +Value listreceivedbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "listreceivedbyaccount [minconf=1] [includeempty=false]\n" + "[minconf] is the minimum number of confirmations before payments are included.\n" + "[includeempty] whether to include accounts that haven't received any payments.\n" + "Returns an array of objects containing:\n" + " \"account\" : the account of the receiving addresses\n" + " \"amount\" : total amount received by addresses with this account\n" + " \"confirmations\" : number of confirmations of the most recent transaction included"); + + return ListReceived(params, true); +} + +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret) +{ + int64 nGeneratedImmature, nGeneratedMature, nFee; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); + + bool fAllAccounts = (strAccount == string("*")); + + // Generated blocks assigned to account "" + if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == "")) + { + Object entry; + entry.push_back(Pair("account", string(""))); + if (nGeneratedImmature) + { + entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan")); + entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature))); + } + else + { + entry.push_back(Pair("category", "generate")); + entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature))); + } + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + + // Sent + if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) + { + BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) + { + Object entry; + entry.push_back(Pair("account", strSentAccount)); + entry.push_back(Pair("address", s.first)); + entry.push_back(Pair("category", "send")); + entry.push_back(Pair("amount", ValueFromAmount(-s.second))); + entry.push_back(Pair("fee", ValueFromAmount(-nFee))); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + + // Received + if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) + { + string account; + if (mapAddressBook.count(r.first)) + account = mapAddressBook[r.first]; + if (fAllAccounts || (account == strAccount)) + { + Object entry; + entry.push_back(Pair("account", account)); + entry.push_back(Pair("address", r.first)); + entry.push_back(Pair("category", "receive")); + entry.push_back(Pair("amount", ValueFromAmount(r.second))); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + } + +} + +void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) +{ + bool fAllAccounts = (strAccount == string("*")); + + if (fAllAccounts || acentry.strAccount == strAccount) + { + Object entry; + entry.push_back(Pair("account", acentry.strAccount)); + entry.push_back(Pair("category", "move")); + entry.push_back(Pair("time", (boost::int64_t)acentry.nTime)); + entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); + entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); + entry.push_back(Pair("comment", acentry.strComment)); + ret.push_back(entry); + } +} + +Value listtransactions(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 3) + throw runtime_error( + "listtransactions [account] [count=10] [from=0]\n" + "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account]."); + + string strAccount = "*"; + if (params.size() > 0) + strAccount = params[0].get_str(); + int nCount = 10; + if (params.size() > 1) + nCount = params[1].get_int(); + int nFrom = 0; + if (params.size() > 2) + nFrom = params[2].get_int(); + + Array ret; + CWalletDB walletdb; + + CRITICAL_BLOCK(cs_mapWallet) + { + // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap: + typedef pair TxPair; + typedef multimap TxItems; + TxItems txByTime; + + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0))); + } + list acentries; + walletdb.ListAccountCreditDebit(strAccount, acentries); + BOOST_FOREACH(CAccountingEntry& entry, acentries) + { + txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + } + + // Now: iterate backwards until we have nCount items to return: + TxItems::reverse_iterator it = txByTime.rbegin(); + for (std::advance(it, nFrom); it != txByTime.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx != 0) + ListTransactions(*pwtx, strAccount, 0, true, ret); + CAccountingEntry *const pacentry = (*it).second.second; + if (pacentry != 0) + AcentryToJSON(*pacentry, strAccount, ret); + + if (ret.size() >= nCount) break; + } + // ret is now newest to oldest + } + + // Make sure we return only last nCount items (sends-to-self might give us an extra): + if (ret.size() > nCount) + { + Array::iterator last = ret.begin(); + std::advance(last, nCount); + ret.erase(last, ret.end()); + } + std::reverse(ret.begin(), ret.end()); // oldest to newest + + return ret; +} + +Value listaccounts(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "listaccounts [minconf=1]\n" + "Returns Object that has account names as keys, account balances as values."); + + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + map mapAccountBalances; + CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& entry, mapAddressBook) { + uint160 hash160; + if(AddressToHash160(entry.first, hash160) && mapPubKeys.count(hash160)) // This address belongs to me + mapAccountBalances[entry.second] = 0; + } + + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + int64 nGeneratedImmature, nGeneratedMature, nFee; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); + mapAccountBalances[strSentAccount] -= nFee; + BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) + mapAccountBalances[strSentAccount] -= s.second; + if (wtx.GetDepthInMainChain() >= nMinDepth) + { + mapAccountBalances[""] += nGeneratedMature; + BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) + if (mapAddressBook.count(r.first)) + mapAccountBalances[mapAddressBook[r.first]] += r.second; + else + mapAccountBalances[""] += r.second; + } + } + } + + list acentries; + CWalletDB().ListAccountCreditDebit("*", acentries); + BOOST_FOREACH(const CAccountingEntry& entry, acentries) + mapAccountBalances[entry.strAccount] += entry.nCreditDebit; + + Object ret; + BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) { + ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); + } + return ret; +} + +Value gettransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "gettransaction \n" + "Get detailed information about "); + + uint256 hash; + hash.SetHex(params[0].get_str()); + + Object entry; + CRITICAL_BLOCK(cs_mapWallet) + { + if (!mapWallet.count(hash)) + throw JSONRPCError(-5, "Invalid or non-wallet transaction id"); + const CWalletTx& wtx = mapWallet[hash]; + + int64 nCredit = wtx.GetCredit(); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); + + entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); + if (wtx.IsFromMe()) + entry.push_back(Pair("fee", ValueFromAmount(nFee))); + + WalletTxToJSON(mapWallet[hash], entry); + + Array details; + ListTransactions(mapWallet[hash], "*", 0, false, details); + entry.push_back(Pair("details", details)); + } + + return entry; +} + + +Value backupwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "backupwallet \n" + "Safely copies wallet.dat to destination, which can be a directory or a path with filename."); + + string strDest = params[0].get_str(); + BackupWallet(strDest); + + return Value::null; +} + + +Value validateaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "validateaddress \n" + "Return information about ."); + + string strAddress = params[0].get_str(); + uint160 hash160; + bool isValid = AddressToHash160(strAddress, hash160); + + Object ret; + ret.push_back(Pair("isvalid", isValid)); + if (isValid) + { + // Call Hash160ToAddress() so we always return current ADDRESSVERSION + // version of the address: + string currentAddress = Hash160ToAddress(hash160); + ret.push_back(Pair("address", currentAddress)); + ret.push_back(Pair("ismine", (mapPubKeys.count(hash160) > 0))); + CRITICAL_BLOCK(cs_mapAddressBook) + { + if (mapAddressBook.count(currentAddress)) + ret.push_back(Pair("account", mapAddressBook[currentAddress])); + } + } + return ret; +} + + +Value getwork(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getwork [data]\n" + "If [data] is not specified, returns formatted hash data to work on:\n" + " \"midstate\" : precomputed hash state after hashing the first half of the data\n" + " \"data\" : block data\n" + " \"hash1\" : formatted hash buffer for second hash\n" + " \"target\" : little endian hash target\n" + "If [data] is specified, tries to solve the block and returns true if it was successful."); + + if (vNodes.empty()) + throw JSONRPCError(-9, "Bitcoin is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(-10, "Bitcoin is downloading blocks..."); + + static map > mapNewBlock; + static vector vNewBlock; + static CReserveKey reservekey; + + if (params.size() == 0) + { + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64 nStart; + static CBlock* pblock; + if (pindexPrev != pindexBest || + (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) + { + if (pindexPrev != pindexBest) + { + // Deallocate old blocks since they're obsolete now + mapNewBlock.clear(); + BOOST_FOREACH(CBlock* pblock, vNewBlock) + delete pblock; + vNewBlock.clear(); + } + nTransactionsUpdatedLast = nTransactionsUpdated; + pindexPrev = pindexBest; + nStart = GetTime(); + + // Create new block + pblock = CreateNewBlock(reservekey); + if (!pblock) + throw JSONRPCError(-7, "Out of memory"); + vNewBlock.push_back(pblock); + } + + // Update nTime + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->nNonce = 0; + + // Update nExtraNonce + static unsigned int nExtraNonce = 0; + static int64 nPrevTime = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, nPrevTime); + + // Save + mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, nExtraNonce); + + // Prebuild hash buffers + char pmidstate[32]; + char pdata[128]; + char phash1[64]; + FormatHashBuffers(pblock, pmidstate, pdata, phash1); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + Object result; + result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); + result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); + result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); + result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); + return result; + } + else + { + // Parse parameters + vector vchData = ParseHex(params[0].get_str()); + if (vchData.size() != 128) + throw JSONRPCError(-8, "Invalid parameter"); + CBlock* pdata = (CBlock*)&vchData[0]; + + // Byte reverse + for (int i = 0; i < 128/4; i++) + ((unsigned int*)pdata)[i] = CryptoPP::ByteReverse(((unsigned int*)pdata)[i]); + + // Get saved block + if (!mapNewBlock.count(pdata->hashMerkleRoot)) + return false; + CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; + unsigned int nExtraNonce = mapNewBlock[pdata->hashMerkleRoot].second; + + pblock->nTime = pdata->nTime; + pblock->nNonce = pdata->nNonce; + pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + + return CheckWork(pblock, reservekey); + } +} + + + + + + + + + + + +// +// Call Table +// + +pair pCallTable[] = +{ + make_pair("help", &help), + make_pair("stop", &stop), + make_pair("getblockcount", &getblockcount), + make_pair("getblocknumber", &getblocknumber), + make_pair("getconnectioncount", &getconnectioncount), + make_pair("getdifficulty", &getdifficulty), + make_pair("getgenerate", &getgenerate), + make_pair("setgenerate", &setgenerate), + make_pair("gethashespersec", &gethashespersec), + make_pair("getinfo", &getinfo), + make_pair("getnewaddress", &getnewaddress), + make_pair("getaccountaddress", &getaccountaddress), + make_pair("setaccount", &setaccount), + make_pair("setlabel", &setaccount), // deprecated + make_pair("getaccount", &getaccount), + make_pair("getlabel", &getaccount), // deprecated + make_pair("getaddressesbyaccount", &getaddressesbyaccount), + make_pair("getaddressesbylabel", &getaddressesbyaccount), // deprecated + make_pair("sendtoaddress", &sendtoaddress), + make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress + make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress + make_pair("getreceivedbyaddress", &getreceivedbyaddress), + make_pair("getreceivedbyaccount", &getreceivedbyaccount), + make_pair("getreceivedbylabel", &getreceivedbyaccount), // deprecated + make_pair("listreceivedbyaddress", &listreceivedbyaddress), + make_pair("listreceivedbyaccount", &listreceivedbyaccount), + make_pair("listreceivedbylabel", &listreceivedbyaccount), // deprecated + make_pair("backupwallet", &backupwallet), + make_pair("validateaddress", &validateaddress), + make_pair("getbalance", &getbalance), + make_pair("move", &movecmd), + make_pair("sendfrom", &sendfrom), + make_pair("sendmany", &sendmany), + make_pair("gettransaction", &gettransaction), + make_pair("listtransactions", &listtransactions), + make_pair("getwork", &getwork), + make_pair("listaccounts", &listaccounts), + make_pair("settxfee", &settxfee), +}; +map mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0])); + +string pAllowInSafeMode[] = +{ + "help", + "stop", + "getblockcount", + "getblocknumber", + "getconnectioncount", + "getdifficulty", + "getgenerate", + "setgenerate", + "gethashespersec", + "getinfo", + "getnewaddress", + "getaccountaddress", + "setlabel", + "getaccount", + "getlabel", // deprecated + "getaddressesbyaccount", + "getaddressesbylabel", // deprecated + "backupwallet", + "validateaddress", + "getwork", +}; +set setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0])); + + + + +// +// HTTP protocol +// +// This ain't Apache. We're just using HTTP header for the length field +// and to be compatible with other JSON-RPC implementations. +// + +string HTTPPost(const string& strMsg, const map& mapRequestHeaders) +{ + ostringstream s; + s << "POST / HTTP/1.1\r\n" + << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n" + << "Host: 127.0.0.1\r\n" + << "Content-Type: application/json\r\n" + << "Content-Length: " << strMsg.size() << "\r\n" + << "Accept: application/json\r\n"; + BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders) + s << item.first << ": " << item.second << "\r\n"; + s << "\r\n" << strMsg; + + return s.str(); +} + +string rfc1123Time() +{ + char buffer[64]; + time_t now; + time(&now); + struct tm* now_gmt = gmtime(&now); + string locale(setlocale(LC_TIME, NULL)); + setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings + strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); + setlocale(LC_TIME, locale.c_str()); + return string(buffer); +} + +string HTTPReply(int nStatus, const string& strMsg) +{ + if (nStatus == 401) + return strprintf("HTTP/1.0 401 Authorization Required\r\n" + "Date: %s\r\n" + "Server: bitcoin-json-rpc/%s\r\n" + "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 296\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Error\r\n" + "\r\n" + "\r\n" + "

401 Unauthorized.

\r\n" + "\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); + string strStatus; + if (nStatus == 200) strStatus = "OK"; + else if (nStatus == 400) strStatus = "Bad Request"; + else if (nStatus == 404) strStatus = "Not Found"; + else if (nStatus == 500) strStatus = "Internal Server Error"; + return strprintf( + "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Connection: close\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/json\r\n" + "Server: bitcoin-json-rpc/%s\r\n" + "\r\n" + "%s", + nStatus, + strStatus.c_str(), + rfc1123Time().c_str(), + strMsg.size(), + FormatFullVersion().c_str(), + strMsg.c_str()); +} + +int ReadHTTPStatus(std::basic_istream& stream) +{ + string str; + getline(stream, str); + vector vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return 500; + return atoi(vWords[1].c_str()); +} + +int ReadHTTPHeader(std::basic_istream& stream, map& mapHeadersRet) +{ + int nLen = 0; + loop + { + string str; + std::getline(stream, str); + if (str.empty() || str == "\r") + break; + string::size_type nColon = str.find(":"); + if (nColon != string::npos) + { + string strHeader = str.substr(0, nColon); + boost::trim(strHeader); + boost::to_lower(strHeader); + string strValue = str.substr(nColon+1); + boost::trim(strValue); + mapHeadersRet[strHeader] = strValue; + if (strHeader == "content-length") + nLen = atoi(strValue.c_str()); + } + } + return nLen; +} + +int ReadHTTP(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet) +{ + mapHeadersRet.clear(); + strMessageRet = ""; + + // Read status + int nStatus = ReadHTTPStatus(stream); + + // Read header + int nLen = ReadHTTPHeader(stream, mapHeadersRet); + if (nLen < 0 || nLen > MAX_SIZE) + return 500; + + // Read message + if (nLen > 0) + { + vector vch(nLen); + stream.read(&vch[0], nLen); + strMessageRet = string(vch.begin(), vch.end()); + } + + return nStatus; +} + +string EncodeBase64(string s) +{ + BIO *b64, *bmem; + BUF_MEM *bptr; + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_new(BIO_s_mem()); + b64 = BIO_push(b64, bmem); + BIO_write(b64, s.c_str(), s.size()); + BIO_flush(b64); + BIO_get_mem_ptr(b64, &bptr); + + string result(bptr->data, bptr->length); + BIO_free_all(b64); + + return result; +} + +string DecodeBase64(string s) +{ + BIO *b64, *bmem; + + char* buffer = static_cast(calloc(s.size(), sizeof(char))); + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_new_mem_buf(const_cast(s.c_str()), s.size()); + bmem = BIO_push(b64, bmem); + BIO_read(bmem, buffer, s.size()); + BIO_free_all(bmem); + + string result(buffer); + free(buffer); + return result; +} + +bool HTTPAuthorized(map& mapHeaders) +{ + string strAuth = mapHeaders["authorization"]; + if (strAuth.substr(0,6) != "Basic ") + return false; + string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); + string strUserPass = DecodeBase64(strUserPass64); + string::size_type nColon = strUserPass.find(":"); + if (nColon == string::npos) + return false; + string strUser = strUserPass.substr(0, nColon); + string strPassword = strUserPass.substr(nColon+1); + return (strUser == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]); +} + +// +// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, +// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were +// unspecified (HTTP errors and contents of 'error'). +// +// 1.0 spec: http://json-rpc.org/wiki/specification +// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http +// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx +// + +string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) +{ + Object request; + request.push_back(Pair("method", strMethod)); + request.push_back(Pair("params", params)); + request.push_back(Pair("id", id)); + return write_string(Value(request), false) + "\n"; +} + +string JSONRPCReply(const Value& result, const Value& error, const Value& id) +{ + Object reply; + if (error.type() != null_type) + reply.push_back(Pair("result", Value::null)); + else + reply.push_back(Pair("result", result)); + reply.push_back(Pair("error", error)); + reply.push_back(Pair("id", id)); + return write_string(Value(reply), false) + "\n"; +} + +void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) +{ + // Send error reply from json-rpc error object + int nStatus = 500; + int code = find_value(objError, "code").get_int(); + if (code == -32600) nStatus = 400; + else if (code == -32601) nStatus = 404; + string strReply = JSONRPCReply(Value::null, objError, id); + stream << HTTPReply(nStatus, strReply) << std::flush; +} + +bool ClientAllowed(const string& strAddress) +{ + if (strAddress == asio::ip::address_v4::loopback().to_string()) + return true; + const vector& vAllow = mapMultiArgs["-rpcallowip"]; + BOOST_FOREACH(string strAllow, vAllow) + if (WildcardMatch(strAddress, strAllow)) + return true; + return false; +} + +#ifdef USE_SSL +// +// IOStream device that speaks SSL but can also speak non-SSL +// +class SSLIOStreamDevice : public iostreams::device { +public: + SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn) + { + fUseSSL = fUseSSLIn; + fNeedHandshake = fUseSSLIn; + } + + void handshake(ssl::stream_base::handshake_type role) + { + if (!fNeedHandshake) return; + fNeedHandshake = false; + stream.handshake(role); + } + std::streamsize read(char* s, std::streamsize n) + { + handshake(ssl::stream_base::server); // HTTPS servers read first + if (fUseSSL) return stream.read_some(asio::buffer(s, n)); + return stream.next_layer().read_some(asio::buffer(s, n)); + } + std::streamsize write(const char* s, std::streamsize n) + { + handshake(ssl::stream_base::client); // HTTPS clients write first + if (fUseSSL) return asio::write(stream, asio::buffer(s, n)); + return asio::write(stream.next_layer(), asio::buffer(s, n)); + } + bool connect(const std::string& server, const std::string& port) + { + ip::tcp::resolver resolver(stream.get_io_service()); + ip::tcp::resolver::query query(server.c_str(), port.c_str()); + ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); + ip::tcp::resolver::iterator end; + boost::system::error_code error = asio::error::host_not_found; + while (error && endpoint_iterator != end) + { + stream.lowest_layer().close(); + stream.lowest_layer().connect(*endpoint_iterator++, error); + } + if (error) + return false; + return true; + } + +private: + bool fNeedHandshake; + bool fUseSSL; + SSLStream& stream; +}; +#endif + +void ThreadRPCServer(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg)); + try + { + vnThreadsRunning[4]++; + ThreadRPCServer2(parg); + vnThreadsRunning[4]--; + } + catch (std::exception& e) { + vnThreadsRunning[4]--; + PrintException(&e, "ThreadRPCServer()"); + } catch (...) { + vnThreadsRunning[4]--; + PrintException(NULL, "ThreadRPCServer()"); + } + printf("ThreadRPCServer exiting\n"); +} + +void ThreadRPCServer2(void* parg) +{ + printf("ThreadRPCServer started\n"); + + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + { + string strWhatAmI = "To use bitcoind"; + if (mapArgs.count("-server")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); + else if (mapArgs.count("-daemon")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); + PrintConsole( + _("Warning: %s, you must set rpcpassword=\nin the configuration file: %s\n" + "If the file does not exist, create it with owner-readable-only file permissions.\n"), + strWhatAmI.c_str(), + GetConfigFile().c_str()); + CreateThread(Shutdown, NULL); + return; + } + + bool fUseSSL = GetBoolArg("-rpcssl"); + asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); + + asio::io_service io_service; + ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332)); + ip::tcp::acceptor acceptor(io_service, endpoint); + + acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + +#ifdef USE_SSL + ssl::context context(io_service, ssl::context::sslv23); + if (fUseSSL) + { + context.set_options(ssl::context::no_sslv2); + filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert"); + if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile; + if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str()); + else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str()); + filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem"); + if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile; + if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem); + else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str()); + + string ciphers = GetArg("-rpcsslciphers", + "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); + SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str()); + } +#else + if (fUseSSL) + throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); +#endif + + loop + { + // Accept connection +#ifdef USE_SSL + SSLStream sslStream(io_service, context); + SSLIOStreamDevice d(sslStream, fUseSSL); + iostreams::stream stream(d); +#else + ip::tcp::iostream stream; +#endif + + ip::tcp::endpoint peer; + vnThreadsRunning[4]--; +#ifdef USE_SSL + acceptor.accept(sslStream.lowest_layer(), peer); +#else + acceptor.accept(*stream.rdbuf(), peer); +#endif + vnThreadsRunning[4]++; + if (fShutdown) + return; + + // Restrict callers by IP + if (!ClientAllowed(peer.address().to_string())) + continue; + + map mapHeaders; + string strRequest; + + boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest)); + if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30)))) + { // Timed out: + acceptor.cancel(); + printf("ThreadRPCServer ReadHTTP timeout\n"); + continue; + } + + // Check authorization + if (mapHeaders.count("authorization") == 0) + { + stream << HTTPReply(401, "") << std::flush; + continue; + } + if (!HTTPAuthorized(mapHeaders)) + { + // Deter brute-forcing short passwords + if (mapArgs["-rpcpassword"].size() < 15) + Sleep(50); + + stream << HTTPReply(401, "") << std::flush; + printf("ThreadRPCServer incorrect password attempt\n"); + continue; + } + + Value id = Value::null; + try + { + // Parse request + Value valRequest; + if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type) + throw JSONRPCError(-32700, "Parse error"); + const Object& request = valRequest.get_obj(); + + // Parse id now so errors from here on will have the id + id = find_value(request, "id"); + + // Parse method + Value valMethod = find_value(request, "method"); + if (valMethod.type() == null_type) + throw JSONRPCError(-32600, "Missing method"); + if (valMethod.type() != str_type) + throw JSONRPCError(-32600, "Method must be a string"); + string strMethod = valMethod.get_str(); + if (strMethod != "getwork") + printf("ThreadRPCServer method=%s\n", strMethod.c_str()); + + // Parse params + Value valParams = find_value(request, "params"); + Array params; + if (valParams.type() == array_type) + params = valParams.get_array(); + else if (valParams.type() == null_type) + params = Array(); + else + throw JSONRPCError(-32600, "Params must be an array"); + + // Find method + map::iterator mi = mapCallTable.find(strMethod); + if (mi == mapCallTable.end()) + throw JSONRPCError(-32601, "Method not found"); + + // Observe safe mode + string strWarning = GetWarnings("rpc"); + if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod)) + throw JSONRPCError(-2, string("Safe mode: ") + strWarning); + + try + { + // Execute + Value result = (*(*mi).second)(params, false); + + // Send reply + string strReply = JSONRPCReply(result, Value::null, id); + stream << HTTPReply(200, strReply) << std::flush; + } + catch (std::exception& e) + { + ErrorReply(stream, JSONRPCError(-1, e.what()), id); + } + } + catch (Object& objError) + { + ErrorReply(stream, objError, id); + } + catch (std::exception& e) + { + ErrorReply(stream, JSONRPCError(-32700, e.what()), id); + } + } +} + + + + +Object CallRPC(const string& strMethod, const Array& params) +{ + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + throw runtime_error(strprintf( + _("You must set rpcpassword= in the configuration file:\n%s\n" + "If the file does not exist, create it with owner-readable-only file permissions."), + GetConfigFile().c_str())); + + // Connect to localhost + bool fUseSSL = GetBoolArg("-rpcssl"); +#ifdef USE_SSL + asio::io_service io_service; + ssl::context context(io_service, ssl::context::sslv23); + context.set_options(ssl::context::no_sslv2); + SSLStream sslStream(io_service, context); + SSLIOStreamDevice d(sslStream, fUseSSL); + iostreams::stream stream(d); + if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"))) + throw runtime_error("couldn't connect to server"); +#else + if (fUseSSL) + throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); + + ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")); + if (stream.fail()) + throw runtime_error("couldn't connect to server"); +#endif + + + // HTTP basic authentication + string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); + map mapRequestHeaders; + mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; + + // Send request + string strRequest = JSONRPCRequest(strMethod, params, 1); + string strPost = HTTPPost(strRequest, mapRequestHeaders); + stream << strPost << std::flush; + + // Receive reply + map mapHeaders; + string strReply; + int nStatus = ReadHTTP(stream, mapHeaders, strReply); + if (nStatus == 401) + throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500) + throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); + else if (strReply.empty()) + throw runtime_error("no response from server"); + + // Parse reply + Value valReply; + if (!read_string(strReply, valReply)) + throw runtime_error("couldn't parse reply from server"); + const Object& reply = valReply.get_obj(); + if (reply.empty()) + throw runtime_error("expected reply to have result, error and id properties"); + + return reply; +} + + + + +template +void ConvertTo(Value& value) +{ + if (value.type() == str_type) + { + // reinterpret string as unquoted json value + Value value2; + if (!read_string(value.get_str(), value2)) + throw runtime_error("type mismatch"); + value = value2.get_value(); + } + else + { + value = value.get_value(); + } +} + +int CommandLineRPC(int argc, char *argv[]) +{ + string strPrint; + int nRet = 0; + try + { + // Skip switches + while (argc > 1 && IsSwitchChar(argv[1][0])) + { + argc--; + argv++; + } + + // Method + if (argc < 2) + throw runtime_error("too few parameters"); + string strMethod = argv[1]; + + // Parameters default to strings + Array params; + for (int i = 2; i < argc; i++) + params.push_back(argv[i]); + int n = params.size(); + + // + // Special case non-string parameter types + // + if (strMethod == "setgenerate" && n > 0) ConvertTo(params[0]); + if (strMethod == "setgenerate" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); + if (strMethod == "getamountreceived" && n > 1) ConvertTo(params[1]); // deprecated + if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo(params[1]); + if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated + if (strMethod == "getallreceived" && n > 0) ConvertTo(params[0]); // deprecated + if (strMethod == "getallreceived" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo(params[0]); + if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo(params[0]); // deprecated + if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated + if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); + if (strMethod == "move" && n > 2) ConvertTo(params[2]); + if (strMethod == "move" && n > 3) ConvertTo(params[3]); + if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]); + if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); + if (strMethod == "listtransactions" && n > 1) ConvertTo(params[1]); + if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); + if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); + if (strMethod == "sendmany" && n > 1) + { + string s = params[1].get_str(); + Value v; + if (!read_string(s, v) || v.type() != obj_type) + throw runtime_error("type mismatch"); + params[1] = v.get_obj(); + } + if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + + // Execute + Object reply = CallRPC(strMethod, params); + + // Parse reply + const Value& result = find_value(reply, "result"); + const Value& error = find_value(reply, "error"); + const Value& id = find_value(reply, "id"); + + if (error.type() != null_type) + { + // Error + strPrint = "error: " + write_string(error, false); + int code = find_value(error.get_obj(), "code").get_int(); + nRet = abs(code); + } + else + { + // Result + if (result.type() == null_type) + strPrint = ""; + else if (result.type() == str_type) + strPrint = result.get_str(); + else + strPrint = write_string(result, true); + } + } + catch (std::exception& e) + { + strPrint = string("error: ") + e.what(); + nRet = 87; + } + catch (...) + { + PrintException(NULL, "CommandLineRPC()"); + } + + if (strPrint != "") + { +#if defined(__WXMSW__) && defined(GUI) + // Windows GUI apps can't print to command line, + // so settle for a message box yuck + MyMessageBox(strPrint, "Bitcoin", wxOK); +#else + fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); +#endif + } + return nRet; +} + + + + +#ifdef TEST +int main(int argc, char *argv[]) +{ +#ifdef _MSC_VER + // Turn off microsoft heap dump noise + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); +#endif + setbuf(stdin, NULL); + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + try + { + if (argc >= 2 && string(argv[1]) == "-server") + { + printf("server ready\n"); + ThreadRPCServer(NULL); + } + else + { + return CommandLineRPC(argc, argv); + } + } + catch (std::exception& e) { + PrintException(&e, "main()"); + } catch (...) { + PrintException(NULL, "main()"); + } + return 0; +} +#endif diff --git a/src/rpc.h b/src/rpc.h new file mode 100644 index 0000000000..48a7b8a8a6 --- /dev/null +++ b/src/rpc.h @@ -0,0 +1,6 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +void ThreadRPCServer(void* parg); +int CommandLineRPC(int argc, char *argv[]); diff --git a/src/script.cpp b/src/script.cpp new file mode 100644 index 0000000000..97334ca0a0 --- /dev/null +++ b/src/script.cpp @@ -0,0 +1,1208 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#include "headers.h" + +using namespace std; +using namespace boost; + +bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); + + + +typedef vector valtype; +static const valtype vchFalse(0); +static const valtype vchZero(0); +static const valtype vchTrue(1, 1); +static const CBigNum bnZero(0); +static const CBigNum bnOne(1); +static const CBigNum bnFalse(0); +static const CBigNum bnTrue(1); +static const size_t nMaxNumSize = 4; + + +CBigNum CastToBigNum(const valtype& vch) +{ + if (vch.size() > nMaxNumSize) + throw runtime_error("CastToBigNum() : overflow"); + // Get rid of extra leading zeros + return CBigNum(CBigNum(vch).getvch()); +} + +bool CastToBool(const valtype& vch) +{ + for (int i = 0; i < vch.size(); i++) + { + if (vch[i] != 0) + { + // Can be negative zero + if (i == vch.size()-1 && vch[i] == 0x80) + return false; + return true; + } + } + return false; +} + +void MakeSameSize(valtype& vch1, valtype& vch2) +{ + // Lengthen the shorter one + if (vch1.size() < vch2.size()) + vch1.resize(vch2.size(), 0); + if (vch2.size() < vch1.size()) + vch2.resize(vch1.size(), 0); +} + + + +// +// Script is a stack machine (like Forth) that evaluates a predicate +// returning a bool indicating valid or not. There are no loops. +// +#define stacktop(i) (stack.at(stack.size()+(i))) +#define altstacktop(i) (altstack.at(altstack.size()+(i))) +static inline void popstack(vector& stack) +{ + if (stack.empty()) + throw runtime_error("popstack() : stack empty"); + stack.pop_back(); +} + + +bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + CAutoBN_CTX pctx; + CScript::const_iterator pc = script.begin(); + CScript::const_iterator pend = script.end(); + CScript::const_iterator pbegincodehash = script.begin(); + opcodetype opcode; + valtype vchPushValue; + vector vfExec; + vector altstack; + if (script.size() > 10000) + return false; + int nOpCount = 0; + + + try + { + while (pc < pend) + { + bool fExec = !count(vfExec.begin(), vfExec.end(), false); + + // + // Read instruction + // + if (!script.GetOp(pc, opcode, vchPushValue)) + return false; + if (vchPushValue.size() > 520) + return false; + if (opcode > OP_16 && ++nOpCount > 201) + return false; + + if (opcode == OP_CAT || + opcode == OP_SUBSTR || + opcode == OP_LEFT || + opcode == OP_RIGHT || + opcode == OP_INVERT || + opcode == OP_AND || + opcode == OP_OR || + opcode == OP_XOR || + opcode == OP_2MUL || + opcode == OP_2DIV || + opcode == OP_MUL || + opcode == OP_DIV || + opcode == OP_MOD || + opcode == OP_LSHIFT || + opcode == OP_RSHIFT) + return false; + + if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) + stack.push_back(vchPushValue); + else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) + switch (opcode) + { + // + // Push value + // + case OP_1NEGATE: + case OP_1: + case OP_2: + case OP_3: + case OP_4: + case OP_5: + case OP_6: + case OP_7: + case OP_8: + case OP_9: + case OP_10: + case OP_11: + case OP_12: + case OP_13: + case OP_14: + case OP_15: + case OP_16: + { + // ( -- value) + CBigNum bn((int)opcode - (int)(OP_1 - 1)); + stack.push_back(bn.getvch()); + } + break; + + + // + // Control + // + case OP_NOP: + case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: + case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: + break; + + case OP_IF: + case OP_NOTIF: + { + // if [statements] [else [statements]] endif + bool fValue = false; + if (fExec) + { + if (stack.size() < 1) + return false; + valtype& vch = stacktop(-1); + fValue = CastToBool(vch); + if (opcode == OP_NOTIF) + fValue = !fValue; + popstack(stack); + } + vfExec.push_back(fValue); + } + break; + + case OP_ELSE: + { + if (vfExec.empty()) + return false; + vfExec.back() = !vfExec.back(); + } + break; + + case OP_ENDIF: + { + if (vfExec.empty()) + return false; + vfExec.pop_back(); + } + break; + + case OP_VERIFY: + { + // (true -- ) or + // (false -- false) and return + if (stack.size() < 1) + return false; + bool fValue = CastToBool(stacktop(-1)); + if (fValue) + popstack(stack); + else + return false; + } + break; + + case OP_RETURN: + { + return false; + } + break; + + + // + // Stack ops + // + case OP_TOALTSTACK: + { + if (stack.size() < 1) + return false; + altstack.push_back(stacktop(-1)); + popstack(stack); + } + break; + + case OP_FROMALTSTACK: + { + if (altstack.size() < 1) + return false; + stack.push_back(altstacktop(-1)); + popstack(altstack); + } + break; + + case OP_2DROP: + { + // (x1 x2 -- ) + if (stack.size() < 2) + return false; + popstack(stack); + popstack(stack); + } + break; + + case OP_2DUP: + { + // (x1 x2 -- x1 x2 x1 x2) + if (stack.size() < 2) + return false; + valtype vch1 = stacktop(-2); + valtype vch2 = stacktop(-1); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_3DUP: + { + // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) + if (stack.size() < 3) + return false; + valtype vch1 = stacktop(-3); + valtype vch2 = stacktop(-2); + valtype vch3 = stacktop(-1); + stack.push_back(vch1); + stack.push_back(vch2); + stack.push_back(vch3); + } + break; + + case OP_2OVER: + { + // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) + if (stack.size() < 4) + return false; + valtype vch1 = stacktop(-4); + valtype vch2 = stacktop(-3); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_2ROT: + { + // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) + if (stack.size() < 6) + return false; + valtype vch1 = stacktop(-6); + valtype vch2 = stacktop(-5); + stack.erase(stack.end()-6, stack.end()-4); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_2SWAP: + { + // (x1 x2 x3 x4 -- x3 x4 x1 x2) + if (stack.size() < 4) + return false; + swap(stacktop(-4), stacktop(-2)); + swap(stacktop(-3), stacktop(-1)); + } + break; + + case OP_IFDUP: + { + // (x - 0 | x x) + if (stack.size() < 1) + return false; + valtype vch = stacktop(-1); + if (CastToBool(vch)) + stack.push_back(vch); + } + break; + + case OP_DEPTH: + { + // -- stacksize + CBigNum bn(stack.size()); + stack.push_back(bn.getvch()); + } + break; + + case OP_DROP: + { + // (x -- ) + if (stack.size() < 1) + return false; + popstack(stack); + } + break; + + case OP_DUP: + { + // (x -- x x) + if (stack.size() < 1) + return false; + valtype vch = stacktop(-1); + stack.push_back(vch); + } + break; + + case OP_NIP: + { + // (x1 x2 -- x2) + if (stack.size() < 2) + return false; + stack.erase(stack.end() - 2); + } + break; + + case OP_OVER: + { + // (x1 x2 -- x1 x2 x1) + if (stack.size() < 2) + return false; + valtype vch = stacktop(-2); + stack.push_back(vch); + } + break; + + case OP_PICK: + case OP_ROLL: + { + // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) + // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) + if (stack.size() < 2) + return false; + int n = CastToBigNum(stacktop(-1)).getint(); + popstack(stack); + if (n < 0 || n >= stack.size()) + return false; + valtype vch = stacktop(-n-1); + if (opcode == OP_ROLL) + stack.erase(stack.end()-n-1); + stack.push_back(vch); + } + break; + + case OP_ROT: + { + // (x1 x2 x3 -- x2 x3 x1) + // x2 x1 x3 after first swap + // x2 x3 x1 after second swap + if (stack.size() < 3) + return false; + swap(stacktop(-3), stacktop(-2)); + swap(stacktop(-2), stacktop(-1)); + } + break; + + case OP_SWAP: + { + // (x1 x2 -- x2 x1) + if (stack.size() < 2) + return false; + swap(stacktop(-2), stacktop(-1)); + } + break; + + case OP_TUCK: + { + // (x1 x2 -- x2 x1 x2) + if (stack.size() < 2) + return false; + valtype vch = stacktop(-1); + stack.insert(stack.end()-2, vch); + } + break; + + + // + // Splice ops + // + case OP_CAT: + { + // (x1 x2 -- out) + if (stack.size() < 2) + return false; + valtype& vch1 = stacktop(-2); + valtype& vch2 = stacktop(-1); + vch1.insert(vch1.end(), vch2.begin(), vch2.end()); + popstack(stack); + if (stacktop(-1).size() > 520) + return false; + } + break; + + case OP_SUBSTR: + { + // (in begin size -- out) + if (stack.size() < 3) + return false; + valtype& vch = stacktop(-3); + int nBegin = CastToBigNum(stacktop(-2)).getint(); + int nEnd = nBegin + CastToBigNum(stacktop(-1)).getint(); + if (nBegin < 0 || nEnd < nBegin) + return false; + if (nBegin > vch.size()) + nBegin = vch.size(); + if (nEnd > vch.size()) + nEnd = vch.size(); + vch.erase(vch.begin() + nEnd, vch.end()); + vch.erase(vch.begin(), vch.begin() + nBegin); + popstack(stack); + popstack(stack); + } + break; + + case OP_LEFT: + case OP_RIGHT: + { + // (in size -- out) + if (stack.size() < 2) + return false; + valtype& vch = stacktop(-2); + int nSize = CastToBigNum(stacktop(-1)).getint(); + if (nSize < 0) + return false; + if (nSize > vch.size()) + nSize = vch.size(); + if (opcode == OP_LEFT) + vch.erase(vch.begin() + nSize, vch.end()); + else + vch.erase(vch.begin(), vch.end() - nSize); + popstack(stack); + } + break; + + case OP_SIZE: + { + // (in -- in size) + if (stack.size() < 1) + return false; + CBigNum bn(stacktop(-1).size()); + stack.push_back(bn.getvch()); + } + break; + + + // + // Bitwise logic + // + case OP_INVERT: + { + // (in - out) + if (stack.size() < 1) + return false; + valtype& vch = stacktop(-1); + for (int i = 0; i < vch.size(); i++) + vch[i] = ~vch[i]; + } + break; + + case OP_AND: + case OP_OR: + case OP_XOR: + { + // (x1 x2 - out) + if (stack.size() < 2) + return false; + valtype& vch1 = stacktop(-2); + valtype& vch2 = stacktop(-1); + MakeSameSize(vch1, vch2); + if (opcode == OP_AND) + { + for (int i = 0; i < vch1.size(); i++) + vch1[i] &= vch2[i]; + } + else if (opcode == OP_OR) + { + for (int i = 0; i < vch1.size(); i++) + vch1[i] |= vch2[i]; + } + else if (opcode == OP_XOR) + { + for (int i = 0; i < vch1.size(); i++) + vch1[i] ^= vch2[i]; + } + popstack(stack); + } + break; + + case OP_EQUAL: + case OP_EQUALVERIFY: + //case OP_NOTEQUAL: // use OP_NUMNOTEQUAL + { + // (x1 x2 - bool) + if (stack.size() < 2) + return false; + valtype& vch1 = stacktop(-2); + valtype& vch2 = stacktop(-1); + bool fEqual = (vch1 == vch2); + // OP_NOTEQUAL is disabled because it would be too easy to say + // something like n != 1 and have some wiseguy pass in 1 with extra + // zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001) + //if (opcode == OP_NOTEQUAL) + // fEqual = !fEqual; + popstack(stack); + popstack(stack); + stack.push_back(fEqual ? vchTrue : vchFalse); + if (opcode == OP_EQUALVERIFY) + { + if (fEqual) + popstack(stack); + else + return false; + } + } + break; + + + // + // Numeric + // + case OP_1ADD: + case OP_1SUB: + case OP_2MUL: + case OP_2DIV: + case OP_NEGATE: + case OP_ABS: + case OP_NOT: + case OP_0NOTEQUAL: + { + // (in -- out) + if (stack.size() < 1) + return false; + CBigNum bn = CastToBigNum(stacktop(-1)); + switch (opcode) + { + case OP_1ADD: bn += bnOne; break; + case OP_1SUB: bn -= bnOne; break; + case OP_2MUL: bn <<= 1; break; + case OP_2DIV: bn >>= 1; break; + case OP_NEGATE: bn = -bn; break; + case OP_ABS: if (bn < bnZero) bn = -bn; break; + case OP_NOT: bn = (bn == bnZero); break; + case OP_0NOTEQUAL: bn = (bn != bnZero); break; + } + popstack(stack); + stack.push_back(bn.getvch()); + } + break; + + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_MOD: + case OP_LSHIFT: + case OP_RSHIFT: + case OP_BOOLAND: + case OP_BOOLOR: + case OP_NUMEQUAL: + case OP_NUMEQUALVERIFY: + case OP_NUMNOTEQUAL: + case OP_LESSTHAN: + case OP_GREATERTHAN: + case OP_LESSTHANOREQUAL: + case OP_GREATERTHANOREQUAL: + case OP_MIN: + case OP_MAX: + { + // (x1 x2 -- out) + if (stack.size() < 2) + return false; + CBigNum bn1 = CastToBigNum(stacktop(-2)); + CBigNum bn2 = CastToBigNum(stacktop(-1)); + CBigNum bn; + switch (opcode) + { + case OP_ADD: + bn = bn1 + bn2; + break; + + case OP_SUB: + bn = bn1 - bn2; + break; + + case OP_MUL: + if (!BN_mul(&bn, &bn1, &bn2, pctx)) + return false; + break; + + case OP_DIV: + if (!BN_div(&bn, NULL, &bn1, &bn2, pctx)) + return false; + break; + + case OP_MOD: + if (!BN_mod(&bn, &bn1, &bn2, pctx)) + return false; + break; + + case OP_LSHIFT: + if (bn2 < bnZero || bn2 > CBigNum(2048)) + return false; + bn = bn1 << bn2.getulong(); + break; + + case OP_RSHIFT: + if (bn2 < bnZero || bn2 > CBigNum(2048)) + return false; + bn = bn1 >> bn2.getulong(); + break; + + case OP_BOOLAND: bn = (bn1 != bnZero && bn2 != bnZero); break; + case OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break; + case OP_NUMEQUAL: bn = (bn1 == bn2); break; + case OP_NUMEQUALVERIFY: bn = (bn1 == bn2); break; + case OP_NUMNOTEQUAL: bn = (bn1 != bn2); break; + case OP_LESSTHAN: bn = (bn1 < bn2); break; + case OP_GREATERTHAN: bn = (bn1 > bn2); break; + case OP_LESSTHANOREQUAL: bn = (bn1 <= bn2); break; + case OP_GREATERTHANOREQUAL: bn = (bn1 >= bn2); break; + case OP_MIN: bn = (bn1 < bn2 ? bn1 : bn2); break; + case OP_MAX: bn = (bn1 > bn2 ? bn1 : bn2); break; + } + popstack(stack); + popstack(stack); + stack.push_back(bn.getvch()); + + if (opcode == OP_NUMEQUALVERIFY) + { + if (CastToBool(stacktop(-1))) + popstack(stack); + else + return false; + } + } + break; + + case OP_WITHIN: + { + // (x min max -- out) + if (stack.size() < 3) + return false; + CBigNum bn1 = CastToBigNum(stacktop(-3)); + CBigNum bn2 = CastToBigNum(stacktop(-2)); + CBigNum bn3 = CastToBigNum(stacktop(-1)); + bool fValue = (bn2 <= bn1 && bn1 < bn3); + popstack(stack); + popstack(stack); + popstack(stack); + stack.push_back(fValue ? vchTrue : vchFalse); + } + break; + + + // + // Crypto + // + case OP_RIPEMD160: + case OP_SHA1: + case OP_SHA256: + case OP_HASH160: + case OP_HASH256: + { + // (in -- hash) + if (stack.size() < 1) + return false; + valtype& vch = stacktop(-1); + valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); + if (opcode == OP_RIPEMD160) + RIPEMD160(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_SHA1) + SHA1(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_SHA256) + SHA256(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_HASH160) + { + uint160 hash160 = Hash160(vch); + memcpy(&vchHash[0], &hash160, sizeof(hash160)); + } + else if (opcode == OP_HASH256) + { + uint256 hash = Hash(vch.begin(), vch.end()); + memcpy(&vchHash[0], &hash, sizeof(hash)); + } + popstack(stack); + stack.push_back(vchHash); + } + break; + + case OP_CODESEPARATOR: + { + // Hash starts after the code separator + pbegincodehash = pc; + } + break; + + case OP_CHECKSIG: + case OP_CHECKSIGVERIFY: + { + // (sig pubkey -- bool) + if (stack.size() < 2) + return false; + + valtype& vchSig = stacktop(-2); + valtype& vchPubKey = stacktop(-1); + + ////// debug print + //PrintHex(vchSig.begin(), vchSig.end(), "sig: %s\n"); + //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n"); + + // Subset of script starting at the most recent codeseparator + CScript scriptCode(pbegincodehash, pend); + + // Drop the signature, since there's no way for a signature to sign itself + scriptCode.FindAndDelete(CScript(vchSig)); + + bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); + + popstack(stack); + popstack(stack); + stack.push_back(fSuccess ? vchTrue : vchFalse); + if (opcode == OP_CHECKSIGVERIFY) + { + if (fSuccess) + popstack(stack); + else + return false; + } + } + break; + + case OP_CHECKMULTISIG: + case OP_CHECKMULTISIGVERIFY: + { + // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) + + int i = 1; + if (stack.size() < i) + return false; + + int nKeysCount = CastToBigNum(stacktop(-i)).getint(); + if (nKeysCount < 0 || nKeysCount > 20) + return false; + nOpCount += nKeysCount; + if (nOpCount > 201) + return false; + int ikey = ++i; + i += nKeysCount; + if (stack.size() < i) + return false; + + int nSigsCount = CastToBigNum(stacktop(-i)).getint(); + if (nSigsCount < 0 || nSigsCount > nKeysCount) + return false; + int isig = ++i; + i += nSigsCount; + if (stack.size() < i) + return false; + + // Subset of script starting at the most recent codeseparator + CScript scriptCode(pbegincodehash, pend); + + // Drop the signatures, since there's no way for a signature to sign itself + for (int k = 0; k < nSigsCount; k++) + { + valtype& vchSig = stacktop(-isig-k); + scriptCode.FindAndDelete(CScript(vchSig)); + } + + bool fSuccess = true; + while (fSuccess && nSigsCount > 0) + { + valtype& vchSig = stacktop(-isig); + valtype& vchPubKey = stacktop(-ikey); + + // Check signature + if (CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType)) + { + isig++; + nSigsCount--; + } + ikey++; + nKeysCount--; + + // If there are more signatures left than keys left, + // then too many signatures have failed + if (nSigsCount > nKeysCount) + fSuccess = false; + } + + while (i-- > 0) + popstack(stack); + stack.push_back(fSuccess ? vchTrue : vchFalse); + + if (opcode == OP_CHECKMULTISIGVERIFY) + { + if (fSuccess) + popstack(stack); + else + return false; + } + } + break; + + default: + return false; + } + + // Size limits + if (stack.size() + altstack.size() > 1000) + return false; + } + } + catch (...) + { + return false; + } + + + if (!vfExec.empty()) + return false; + + return true; +} + + + + + + + + + +uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + if (nIn >= txTo.vin.size()) + { + printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); + return 1; + } + CTransaction txTmp(txTo); + + // In case concatenating two scripts ends up with two codeseparators, + // or an extra one at the end, this prevents all those possible incompatibilities. + scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); + + // Blank out other inputs' signatures + for (int i = 0; i < txTmp.vin.size(); i++) + txTmp.vin[i].scriptSig = CScript(); + txTmp.vin[nIn].scriptSig = scriptCode; + + // Blank out some of the outputs + if ((nHashType & 0x1f) == SIGHASH_NONE) + { + // Wildcard payee + txTmp.vout.clear(); + + // Let the others update at will + for (int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + else if ((nHashType & 0x1f) == SIGHASH_SINGLE) + { + // Only lockin the txout payee at same index as txin + unsigned int nOut = nIn; + if (nOut >= txTmp.vout.size()) + { + printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); + return 1; + } + txTmp.vout.resize(nOut+1); + for (int i = 0; i < nOut; i++) + txTmp.vout[i].SetNull(); + + // Let the others update at will + for (int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + + // Blank out other inputs completely, not recommended for open transactions + if (nHashType & SIGHASH_ANYONECANPAY) + { + txTmp.vin[0] = txTmp.vin[nIn]; + txTmp.vin.resize(1); + } + + // Serialize and hash + CDataStream ss(SER_GETHASH); + ss.reserve(10000); + ss << txTmp << nHashType; + return Hash(ss.begin(), ss.end()); +} + + +bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, + const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + CKey key; + if (!key.SetPubKey(vchPubKey)) + return false; + + // Hash type is one byte tacked on to the end of the signature + if (vchSig.empty()) + return false; + if (nHashType == 0) + nHashType = vchSig.back(); + else if (nHashType != vchSig.back()) + return false; + vchSig.pop_back(); + + return key.Verify(SignatureHash(scriptCode, txTo, nIn, nHashType), vchSig); +} + + + + + + + + + + +bool Solver(const CScript& scriptPubKey, vector >& vSolutionRet) +{ + // Templates + static vector vTemplates; + if (vTemplates.empty()) + { + // Standard tx, sender provides pubkey, receiver adds signature + vTemplates.push_back(CScript() << OP_PUBKEY << OP_CHECKSIG); + + // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey + vTemplates.push_back(CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG); + } + + // Scan templates + const CScript& script1 = scriptPubKey; + BOOST_FOREACH(const CScript& script2, vTemplates) + { + vSolutionRet.clear(); + opcodetype opcode1, opcode2; + vector vch1, vch2; + + // Compare + CScript::const_iterator pc1 = script1.begin(); + CScript::const_iterator pc2 = script2.begin(); + loop + { + if (pc1 == script1.end() && pc2 == script2.end()) + { + // Found a match + reverse(vSolutionRet.begin(), vSolutionRet.end()); + return true; + } + if (!script1.GetOp(pc1, opcode1, vch1)) + break; + if (!script2.GetOp(pc2, opcode2, vch2)) + break; + if (opcode2 == OP_PUBKEY) + { + if (vch1.size() < 33 || vch1.size() > 120) + break; + vSolutionRet.push_back(make_pair(opcode2, vch1)); + } + else if (opcode2 == OP_PUBKEYHASH) + { + if (vch1.size() != sizeof(uint160)) + break; + vSolutionRet.push_back(make_pair(opcode2, vch1)); + } + else if (opcode1 != opcode2 || vch1 != vch2) + { + break; + } + } + } + + vSolutionRet.clear(); + return false; +} + + +bool Solver(const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet) +{ + scriptSigRet.clear(); + + vector > vSolution; + if (!Solver(scriptPubKey, vSolution)) + return false; + + // Compile solution + CRITICAL_BLOCK(cs_mapKeys) + { + BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) + { + if (item.first == OP_PUBKEY) + { + // Sign + const valtype& vchPubKey = item.second; + if (!mapKeys.count(vchPubKey)) + return false; + if (hash != 0) + { + vector vchSig; + if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig)) + return false; + vchSig.push_back((unsigned char)nHashType); + scriptSigRet << vchSig; + } + } + else if (item.first == OP_PUBKEYHASH) + { + // Sign and give pubkey + map::iterator mi = mapPubKeys.find(uint160(item.second)); + if (mi == mapPubKeys.end()) + return false; + const vector& vchPubKey = (*mi).second; + if (!mapKeys.count(vchPubKey)) + return false; + if (hash != 0) + { + vector vchSig; + if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig)) + return false; + vchSig.push_back((unsigned char)nHashType); + scriptSigRet << vchSig << vchPubKey; + } + } + else + { + return false; + } + } + } + + return true; +} + + +bool IsStandard(const CScript& scriptPubKey) +{ + vector > vSolution; + return Solver(scriptPubKey, vSolution); +} + + +bool IsMine(const CScript& scriptPubKey) +{ + CScript scriptSig; + return Solver(scriptPubKey, 0, 0, scriptSig); +} + + +bool ExtractPubKey(const CScript& scriptPubKey, bool fMineOnly, vector& vchPubKeyRet) +{ + vchPubKeyRet.clear(); + + vector > vSolution; + if (!Solver(scriptPubKey, vSolution)) + return false; + + CRITICAL_BLOCK(cs_mapKeys) + { + BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) + { + valtype vchPubKey; + if (item.first == OP_PUBKEY) + { + vchPubKey = item.second; + } + else if (item.first == OP_PUBKEYHASH) + { + map::iterator mi = mapPubKeys.find(uint160(item.second)); + if (mi == mapPubKeys.end()) + continue; + vchPubKey = (*mi).second; + } + if (!fMineOnly || mapKeys.count(vchPubKey)) + { + vchPubKeyRet = vchPubKey; + return true; + } + } + } + return false; +} + + +bool ExtractHash160(const CScript& scriptPubKey, uint160& hash160Ret) +{ + hash160Ret = 0; + + vector > vSolution; + if (!Solver(scriptPubKey, vSolution)) + return false; + + BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) + { + if (item.first == OP_PUBKEYHASH) + { + hash160Ret = uint160(item.second); + return true; + } + } + return false; +} + + +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + vector > stack; + if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType)) + return false; + if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType)) + return false; + if (stack.empty()) + return false; + return CastToBool(stack.back()); +} + + +bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType, CScript scriptPrereq) +{ + assert(nIn < txTo.vin.size()); + CTxIn& txin = txTo.vin[nIn]; + assert(txin.prevout.n < txFrom.vout.size()); + const CTxOut& txout = txFrom.vout[txin.prevout.n]; + + // Leave out the signature from the hash, since a signature can't sign itself. + // The checksig op will also drop the signatures from its hash. + uint256 hash = SignatureHash(scriptPrereq + txout.scriptPubKey, txTo, nIn, nHashType); + + if (!Solver(txout.scriptPubKey, hash, nHashType, txin.scriptSig)) + return false; + + txin.scriptSig = scriptPrereq + txin.scriptSig; + + // Test solution + if (scriptPrereq.empty()) + if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, 0)) + return false; + + return true; +} + + +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + assert(nIn < txTo.vin.size()); + const CTxIn& txin = txTo.vin[nIn]; + if (txin.prevout.n >= txFrom.vout.size()) + return false; + const CTxOut& txout = txFrom.vout[txin.prevout.n]; + + if (txin.prevout.hash != txFrom.GetHash()) + return false; + + if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nHashType)) + return false; + + // Anytime a signature is successfully verified, it's proof the outpoint is spent, + // so lets update the wallet spent flag if it doesn't know due to wallet.dat being + // restored from backup or the user making copies of wallet.dat. + WalletUpdateSpent(txin.prevout); + + return true; +} diff --git a/src/script.h b/src/script.h new file mode 100644 index 0000000000..22a6020dce --- /dev/null +++ b/src/script.h @@ -0,0 +1,718 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef H_BITCOIN_SCRIPT +#define H_BITCOIN_SCRIPT + +#include "base58.h" + +#include +#include + +class CTransaction; + +enum +{ + SIGHASH_ALL = 1, + SIGHASH_NONE = 2, + SIGHASH_SINGLE = 3, + SIGHASH_ANYONECANPAY = 0x80, +}; + + + +enum opcodetype +{ + // push value + OP_0=0, + OP_FALSE=OP_0, + OP_PUSHDATA1=76, + OP_PUSHDATA2, + OP_PUSHDATA4, + OP_1NEGATE, + OP_RESERVED, + OP_1, + OP_TRUE=OP_1, + OP_2, + OP_3, + OP_4, + OP_5, + OP_6, + OP_7, + OP_8, + OP_9, + OP_10, + OP_11, + OP_12, + OP_13, + OP_14, + OP_15, + OP_16, + + // control + OP_NOP, + OP_VER, + OP_IF, + OP_NOTIF, + OP_VERIF, + OP_VERNOTIF, + OP_ELSE, + OP_ENDIF, + OP_VERIFY, + OP_RETURN, + + // stack ops + OP_TOALTSTACK, + OP_FROMALTSTACK, + OP_2DROP, + OP_2DUP, + OP_3DUP, + OP_2OVER, + OP_2ROT, + OP_2SWAP, + OP_IFDUP, + OP_DEPTH, + OP_DROP, + OP_DUP, + OP_NIP, + OP_OVER, + OP_PICK, + OP_ROLL, + OP_ROT, + OP_SWAP, + OP_TUCK, + + // splice ops + OP_CAT, + OP_SUBSTR, + OP_LEFT, + OP_RIGHT, + OP_SIZE, + + // bit logic + OP_INVERT, + OP_AND, + OP_OR, + OP_XOR, + OP_EQUAL, + OP_EQUALVERIFY, + OP_RESERVED1, + OP_RESERVED2, + + // numeric + OP_1ADD, + OP_1SUB, + OP_2MUL, + OP_2DIV, + OP_NEGATE, + OP_ABS, + OP_NOT, + OP_0NOTEQUAL, + + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_MOD, + OP_LSHIFT, + OP_RSHIFT, + + OP_BOOLAND, + OP_BOOLOR, + OP_NUMEQUAL, + OP_NUMEQUALVERIFY, + OP_NUMNOTEQUAL, + OP_LESSTHAN, + OP_GREATERTHAN, + OP_LESSTHANOREQUAL, + OP_GREATERTHANOREQUAL, + OP_MIN, + OP_MAX, + + OP_WITHIN, + + // crypto + OP_RIPEMD160, + OP_SHA1, + OP_SHA256, + OP_HASH160, + OP_HASH256, + OP_CODESEPARATOR, + OP_CHECKSIG, + OP_CHECKSIGVERIFY, + OP_CHECKMULTISIG, + OP_CHECKMULTISIGVERIFY, + + // expansion + OP_NOP1, + OP_NOP2, + OP_NOP3, + OP_NOP4, + OP_NOP5, + OP_NOP6, + OP_NOP7, + OP_NOP8, + OP_NOP9, + OP_NOP10, + + + + // template matching params + OP_PUBKEYHASH = 0xfd, + OP_PUBKEY = 0xfe, + + OP_INVALIDOPCODE = 0xff, +}; + + + + + + + + +inline const char* GetOpName(opcodetype opcode) +{ + switch (opcode) + { + // push value + case OP_0 : return "0"; + case OP_PUSHDATA1 : return "OP_PUSHDATA1"; + case OP_PUSHDATA2 : return "OP_PUSHDATA2"; + case OP_PUSHDATA4 : return "OP_PUSHDATA4"; + case OP_1NEGATE : return "-1"; + case OP_RESERVED : return "OP_RESERVED"; + case OP_1 : return "1"; + case OP_2 : return "2"; + case OP_3 : return "3"; + case OP_4 : return "4"; + case OP_5 : return "5"; + case OP_6 : return "6"; + case OP_7 : return "7"; + case OP_8 : return "8"; + case OP_9 : return "9"; + case OP_10 : return "10"; + case OP_11 : return "11"; + case OP_12 : return "12"; + case OP_13 : return "13"; + case OP_14 : return "14"; + case OP_15 : return "15"; + case OP_16 : return "16"; + + // control + case OP_NOP : return "OP_NOP"; + case OP_VER : return "OP_VER"; + case OP_IF : return "OP_IF"; + case OP_NOTIF : return "OP_NOTIF"; + case OP_VERIF : return "OP_VERIF"; + case OP_VERNOTIF : return "OP_VERNOTIF"; + case OP_ELSE : return "OP_ELSE"; + case OP_ENDIF : return "OP_ENDIF"; + case OP_VERIFY : return "OP_VERIFY"; + case OP_RETURN : return "OP_RETURN"; + + // stack ops + case OP_TOALTSTACK : return "OP_TOALTSTACK"; + case OP_FROMALTSTACK : return "OP_FROMALTSTACK"; + case OP_2DROP : return "OP_2DROP"; + case OP_2DUP : return "OP_2DUP"; + case OP_3DUP : return "OP_3DUP"; + case OP_2OVER : return "OP_2OVER"; + case OP_2ROT : return "OP_2ROT"; + case OP_2SWAP : return "OP_2SWAP"; + case OP_IFDUP : return "OP_IFDUP"; + case OP_DEPTH : return "OP_DEPTH"; + case OP_DROP : return "OP_DROP"; + case OP_DUP : return "OP_DUP"; + case OP_NIP : return "OP_NIP"; + case OP_OVER : return "OP_OVER"; + case OP_PICK : return "OP_PICK"; + case OP_ROLL : return "OP_ROLL"; + case OP_ROT : return "OP_ROT"; + case OP_SWAP : return "OP_SWAP"; + case OP_TUCK : return "OP_TUCK"; + + // splice ops + case OP_CAT : return "OP_CAT"; + case OP_SUBSTR : return "OP_SUBSTR"; + case OP_LEFT : return "OP_LEFT"; + case OP_RIGHT : return "OP_RIGHT"; + case OP_SIZE : return "OP_SIZE"; + + // bit logic + case OP_INVERT : return "OP_INVERT"; + case OP_AND : return "OP_AND"; + case OP_OR : return "OP_OR"; + case OP_XOR : return "OP_XOR"; + case OP_EQUAL : return "OP_EQUAL"; + case OP_EQUALVERIFY : return "OP_EQUALVERIFY"; + case OP_RESERVED1 : return "OP_RESERVED1"; + case OP_RESERVED2 : return "OP_RESERVED2"; + + // numeric + case OP_1ADD : return "OP_1ADD"; + case OP_1SUB : return "OP_1SUB"; + case OP_2MUL : return "OP_2MUL"; + case OP_2DIV : return "OP_2DIV"; + case OP_NEGATE : return "OP_NEGATE"; + case OP_ABS : return "OP_ABS"; + case OP_NOT : return "OP_NOT"; + case OP_0NOTEQUAL : return "OP_0NOTEQUAL"; + case OP_ADD : return "OP_ADD"; + case OP_SUB : return "OP_SUB"; + case OP_MUL : return "OP_MUL"; + case OP_DIV : return "OP_DIV"; + case OP_MOD : return "OP_MOD"; + case OP_LSHIFT : return "OP_LSHIFT"; + case OP_RSHIFT : return "OP_RSHIFT"; + case OP_BOOLAND : return "OP_BOOLAND"; + case OP_BOOLOR : return "OP_BOOLOR"; + case OP_NUMEQUAL : return "OP_NUMEQUAL"; + case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY"; + case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL"; + case OP_LESSTHAN : return "OP_LESSTHAN"; + case OP_GREATERTHAN : return "OP_GREATERTHAN"; + case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL"; + case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL"; + case OP_MIN : return "OP_MIN"; + case OP_MAX : return "OP_MAX"; + case OP_WITHIN : return "OP_WITHIN"; + + // crypto + case OP_RIPEMD160 : return "OP_RIPEMD160"; + case OP_SHA1 : return "OP_SHA1"; + case OP_SHA256 : return "OP_SHA256"; + case OP_HASH160 : return "OP_HASH160"; + case OP_HASH256 : return "OP_HASH256"; + case OP_CODESEPARATOR : return "OP_CODESEPARATOR"; + case OP_CHECKSIG : return "OP_CHECKSIG"; + case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; + case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; + case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; + + // expanson + case OP_NOP1 : return "OP_NOP1"; + case OP_NOP2 : return "OP_NOP2"; + case OP_NOP3 : return "OP_NOP3"; + case OP_NOP4 : return "OP_NOP4"; + case OP_NOP5 : return "OP_NOP5"; + case OP_NOP6 : return "OP_NOP6"; + case OP_NOP7 : return "OP_NOP7"; + case OP_NOP8 : return "OP_NOP8"; + case OP_NOP9 : return "OP_NOP9"; + case OP_NOP10 : return "OP_NOP10"; + + + + // template matching params + case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; + case OP_PUBKEY : return "OP_PUBKEY"; + + case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; + default: + return "OP_UNKNOWN"; + } +}; + + + + +inline std::string ValueString(const std::vector& vch) +{ + if (vch.size() <= 4) + return strprintf("%d", CBigNum(vch).getint()); + else + return HexStr(vch); +} + +inline std::string StackString(const std::vector >& vStack) +{ + std::string str; + BOOST_FOREACH(const std::vector& vch, vStack) + { + if (!str.empty()) + str += " "; + str += ValueString(vch); + } + return str; +} + + + + + + + + + +class CScript : public std::vector +{ +protected: + CScript& push_int64(int64 n) + { + if (n == -1 || (n >= 1 && n <= 16)) + { + push_back(n + (OP_1 - 1)); + } + else + { + CBigNum bn(n); + *this << bn.getvch(); + } + return *this; + } + + CScript& push_uint64(uint64 n) + { + if (n >= 1 && n <= 16) + { + push_back(n + (OP_1 - 1)); + } + else + { + CBigNum bn(n); + *this << bn.getvch(); + } + return *this; + } + +public: + CScript() { } + CScript(const CScript& b) : std::vector(b.begin(), b.end()) { } + CScript(const_iterator pbegin, const_iterator pend) : std::vector(pbegin, pend) { } +#ifndef _MSC_VER + CScript(const unsigned char* pbegin, const unsigned char* pend) : std::vector(pbegin, pend) { } +#endif + + CScript& operator+=(const CScript& b) + { + insert(end(), b.begin(), b.end()); + return *this; + } + + friend CScript operator+(const CScript& a, const CScript& b) + { + CScript ret = a; + ret += b; + return ret; + } + + + explicit CScript(char b) { operator<<(b); } + explicit CScript(short b) { operator<<(b); } + explicit CScript(int b) { operator<<(b); } + explicit CScript(long b) { operator<<(b); } + explicit CScript(int64 b) { operator<<(b); } + explicit CScript(unsigned char b) { operator<<(b); } + explicit CScript(unsigned int b) { operator<<(b); } + explicit CScript(unsigned short b) { operator<<(b); } + explicit CScript(unsigned long b) { operator<<(b); } + explicit CScript(uint64 b) { operator<<(b); } + + explicit CScript(opcodetype b) { operator<<(b); } + explicit CScript(const uint256& b) { operator<<(b); } + explicit CScript(const CBigNum& b) { operator<<(b); } + explicit CScript(const std::vector& b) { operator<<(b); } + + + CScript& operator<<(char b) { return push_int64(b); } + CScript& operator<<(short b) { return push_int64(b); } + CScript& operator<<(int b) { return push_int64(b); } + CScript& operator<<(long b) { return push_int64(b); } + CScript& operator<<(int64 b) { return push_int64(b); } + CScript& operator<<(unsigned char b) { return push_uint64(b); } + CScript& operator<<(unsigned int b) { return push_uint64(b); } + CScript& operator<<(unsigned short b) { return push_uint64(b); } + CScript& operator<<(unsigned long b) { return push_uint64(b); } + CScript& operator<<(uint64 b) { return push_uint64(b); } + + CScript& operator<<(opcodetype opcode) + { + if (opcode < 0 || opcode > 0xff) + throw std::runtime_error("CScript::operator<<() : invalid opcode"); + insert(end(), (unsigned char)opcode); + return *this; + } + + CScript& operator<<(const uint160& b) + { + insert(end(), sizeof(b)); + insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b)); + return *this; + } + + CScript& operator<<(const uint256& b) + { + insert(end(), sizeof(b)); + insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b)); + return *this; + } + + CScript& operator<<(const CBigNum& b) + { + *this << b.getvch(); + return *this; + } + + CScript& operator<<(const std::vector& b) + { + if (b.size() < OP_PUSHDATA1) + { + insert(end(), (unsigned char)b.size()); + } + else if (b.size() <= 0xff) + { + insert(end(), OP_PUSHDATA1); + insert(end(), (unsigned char)b.size()); + } + else if (b.size() <= 0xffff) + { + insert(end(), OP_PUSHDATA2); + unsigned short nSize = b.size(); + insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize)); + } + else + { + insert(end(), OP_PUSHDATA4); + unsigned int nSize = b.size(); + insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize)); + } + insert(end(), b.begin(), b.end()); + return *this; + } + + CScript& operator<<(const CScript& b) + { + // I'm not sure if this should push the script or concatenate scripts. + // If there's ever a use for pushing a script onto a script, delete this member fn + assert(("warning: pushing a CScript onto a CScript with << is probably not intended, use + to concatenate", false)); + return *this; + } + + + bool GetOp(iterator& pc, opcodetype& opcodeRet, std::vector& vchRet) + { + // Wrapper so it can be called with either iterator or const_iterator + const_iterator pc2 = pc; + bool fRet = GetOp2(pc2, opcodeRet, &vchRet); + pc = begin() + (pc2 - begin()); + return fRet; + } + + bool GetOp(iterator& pc, opcodetype& opcodeRet) + { + const_iterator pc2 = pc; + bool fRet = GetOp2(pc2, opcodeRet, NULL); + pc = begin() + (pc2 - begin()); + return fRet; + } + + bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector& vchRet) const + { + return GetOp2(pc, opcodeRet, &vchRet); + } + + bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const + { + return GetOp2(pc, opcodeRet, NULL); + } + + bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector* pvchRet) const + { + opcodeRet = OP_INVALIDOPCODE; + if (pvchRet) + pvchRet->clear(); + if (pc >= end()) + return false; + + // Read instruction + if (end() - pc < 1) + return false; + unsigned int opcode = *pc++; + + // Immediate operand + if (opcode <= OP_PUSHDATA4) + { + unsigned int nSize; + if (opcode < OP_PUSHDATA1) + { + nSize = opcode; + } + else if (opcode == OP_PUSHDATA1) + { + if (end() - pc < 1) + return false; + nSize = *pc++; + } + else if (opcode == OP_PUSHDATA2) + { + if (end() - pc < 2) + return false; + nSize = 0; + memcpy(&nSize, &pc[0], 2); + pc += 2; + } + else if (opcode == OP_PUSHDATA4) + { + if (end() - pc < 4) + return false; + memcpy(&nSize, &pc[0], 4); + pc += 4; + } + if (end() - pc < nSize) + return false; + if (pvchRet) + pvchRet->assign(pc, pc + nSize); + pc += nSize; + } + + opcodeRet = (opcodetype)opcode; + return true; + } + + + void FindAndDelete(const CScript& b) + { + if (b.empty()) + return; + iterator pc = begin(); + opcodetype opcode; + do + { + while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0) + erase(pc, pc + b.size()); + } + while (GetOp(pc, opcode)); + } + + + int GetSigOpCount() const + { + int n = 0; + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + break; + if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY) + n++; + else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY) + n += 20; + } + return n; + } + + + bool IsPushOnly() const + { + if (size() > 200) + return false; + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + return false; + if (opcode > OP_16) + return false; + } + return true; + } + + + uint160 GetBitcoinAddressHash160() const + { + opcodetype opcode; + std::vector vch; + CScript::const_iterator pc = begin(); + if (!GetOp(pc, opcode, vch) || opcode != OP_DUP) return 0; + if (!GetOp(pc, opcode, vch) || opcode != OP_HASH160) return 0; + if (!GetOp(pc, opcode, vch) || vch.size() != sizeof(uint160)) return 0; + uint160 hash160 = uint160(vch); + if (!GetOp(pc, opcode, vch) || opcode != OP_EQUALVERIFY) return 0; + if (!GetOp(pc, opcode, vch) || opcode != OP_CHECKSIG) return 0; + if (pc != end()) return 0; + return hash160; + } + + std::string GetBitcoinAddress() const + { + uint160 hash160 = GetBitcoinAddressHash160(); + if (hash160 == 0) + return ""; + return Hash160ToAddress(hash160); + } + + void SetBitcoinAddress(const uint160& hash160) + { + this->clear(); + *this << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; + } + + void SetBitcoinAddress(const std::vector& vchPubKey) + { + SetBitcoinAddress(Hash160(vchPubKey)); + } + + bool SetBitcoinAddress(const std::string& strAddress) + { + this->clear(); + uint160 hash160; + if (!AddressToHash160(strAddress, hash160)) + return false; + SetBitcoinAddress(hash160); + return true; + } + + + void PrintHex() const + { + printf("CScript(%s)\n", HexStr(begin(), end(), true).c_str()); + } + + std::string ToString() const + { + std::string str; + opcodetype opcode; + std::vector vch; + const_iterator pc = begin(); + while (pc < end()) + { + if (!str.empty()) + str += " "; + if (!GetOp(pc, opcode, vch)) + { + str += "[error]"; + return str; + } + if (0 <= opcode && opcode <= OP_PUSHDATA4) + str += ValueString(vch); + else + str += GetOpName(opcode); + } + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + + + + + +uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +bool IsStandard(const CScript& scriptPubKey); +bool IsMine(const CScript& scriptPubKey); +bool ExtractPubKey(const CScript& scriptPubKey, bool fMineOnly, std::vector& vchPubKeyRet); +bool ExtractHash160(const CScript& scriptPubKey, uint160& hash160Ret); +bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript()); +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType=0); + +#endif diff --git a/src/serialize.h b/src/serialize.h new file mode 100644 index 0000000000..0d66d6a956 --- /dev/null +++ b/src/serialize.h @@ -0,0 +1,1271 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_SERIALIZE_H +#define BITCOIN_SERIALIZE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false) ; else for +#endif +class CScript; +class CDataStream; +class CAutoFile; +static const unsigned int MAX_SIZE = 0x02000000; + +static const int VERSION = 32300; +static const char* pszSubVer = ""; +static const bool VERSION_IS_BETA = true; + + + + + + +///////////////////////////////////////////////////////////////// +// +// Templates for serializing to anything that looks like a stream, +// i.e. anything that supports .read(char*, int) and .write(char*, int) +// + +enum +{ + // primary actions + SER_NETWORK = (1 << 0), + SER_DISK = (1 << 1), + SER_GETHASH = (1 << 2), + + // modifiers + SER_SKIPSIG = (1 << 16), + SER_BLOCKHEADERONLY = (1 << 17), +}; + +#define IMPLEMENT_SERIALIZE(statements) \ + unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const \ + { \ + CSerActionGetSerializeSize ser_action; \ + const bool fGetSize = true; \ + const bool fWrite = false; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + ser_streamplaceholder s; \ + s.nType = nType; \ + s.nVersion = nVersion; \ + {statements} \ + return nSerSize; \ + } \ + template \ + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const \ + { \ + CSerActionSerialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = true; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + {statements} \ + } \ + template \ + void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) \ + { \ + CSerActionUnserialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = false; \ + const bool fRead = true; \ + unsigned int nSerSize = 0; \ + {statements} \ + } + +#define READWRITE(obj) (nSerSize += ::SerReadWrite(s, (obj), nType, nVersion, ser_action)) + + + + + + +// +// Basic types +// +#define WRITEDATA(s, obj) s.write((char*)&(obj), sizeof(obj)) +#define READDATA(s, obj) s.read((char*)&(obj), sizeof(obj)) + +inline unsigned int GetSerializeSize(char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed short a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned short a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed int a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned int a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(int64 a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(uint64 a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(float a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(double a, int, int=0) { return sizeof(a); } + +template inline void Serialize(Stream& s, char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed short a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned short a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed int a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned int a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed long a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned long a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, int64 a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, uint64 a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, float a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, double a, int, int=0) { WRITEDATA(s, a); } + +template inline void Unserialize(Stream& s, char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed short& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned short& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed int& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned int& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed long& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned long& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, int64& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, uint64& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, float& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, double& a, int, int=0) { READDATA(s, a); } + +inline unsigned int GetSerializeSize(bool a, int, int=0) { return sizeof(char); } +template inline void Serialize(Stream& s, bool a, int, int=0) { char f=a; WRITEDATA(s, f); } +template inline void Unserialize(Stream& s, bool& a, int, int=0) { char f; READDATA(s, f); a=f; } + + + + + + +// +// Compact size +// size < 253 -- 1 byte +// size <= USHRT_MAX -- 3 bytes (253 + 2 bytes) +// size <= UINT_MAX -- 5 bytes (254 + 4 bytes) +// size > UINT_MAX -- 9 bytes (255 + 8 bytes) +// +inline unsigned int GetSizeOfCompactSize(uint64 nSize) +{ + if (nSize < 253) return sizeof(unsigned char); + else if (nSize <= USHRT_MAX) return sizeof(unsigned char) + sizeof(unsigned short); + else if (nSize <= UINT_MAX) return sizeof(unsigned char) + sizeof(unsigned int); + else return sizeof(unsigned char) + sizeof(uint64); +} + +template +void WriteCompactSize(Stream& os, uint64 nSize) +{ + if (nSize < 253) + { + unsigned char chSize = nSize; + WRITEDATA(os, chSize); + } + else if (nSize <= USHRT_MAX) + { + unsigned char chSize = 253; + unsigned short xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + else if (nSize <= UINT_MAX) + { + unsigned char chSize = 254; + unsigned int xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + else + { + unsigned char chSize = 255; + uint64 xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + return; +} + +template +uint64 ReadCompactSize(Stream& is) +{ + unsigned char chSize; + READDATA(is, chSize); + uint64 nSizeRet = 0; + if (chSize < 253) + { + nSizeRet = chSize; + } + else if (chSize == 253) + { + unsigned short xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + else if (chSize == 254) + { + unsigned int xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + else + { + uint64 xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + if (nSizeRet > (uint64)MAX_SIZE) + throw std::ios_base::failure("ReadCompactSize() : size too large"); + return nSizeRet; +} + + + +// +// Wrapper for serializing arrays and POD +// There's a clever template way to make arrays serialize normally, but MSVC6 doesn't support it +// +#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) +class CFlatData +{ +protected: + char* pbegin; + char* pend; +public: + CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } + char* begin() { return pbegin; } + const char* begin() const { return pbegin; } + char* end() { return pend; } + const char* end() const { return pend; } + + unsigned int GetSerializeSize(int, int=0) const + { + return pend - pbegin; + } + + template + void Serialize(Stream& s, int, int=0) const + { + s.write(pbegin, pend - pbegin); + } + + template + void Unserialize(Stream& s, int, int=0) + { + s.read(pbegin, pend - pbegin); + } +}; + + + +// +// string stored as a fixed length field +// +template +class CFixedFieldString +{ +protected: + const std::string* pcstr; + std::string* pstr; +public: + explicit CFixedFieldString(const std::string& str) : pcstr(&str), pstr(NULL) { } + explicit CFixedFieldString(std::string& str) : pcstr(&str), pstr(&str) { } + + unsigned int GetSerializeSize(int, int=0) const + { + return LEN; + } + + template + void Serialize(Stream& s, int, int=0) const + { + char pszBuf[LEN]; + strncpy(pszBuf, pcstr->c_str(), LEN); + s.write(pszBuf, LEN); + } + + template + void Unserialize(Stream& s, int, int=0) + { + if (pstr == NULL) + throw std::ios_base::failure("CFixedFieldString::Unserialize : trying to unserialize to const string"); + char pszBuf[LEN+1]; + s.read(pszBuf, LEN); + pszBuf[LEN] = '\0'; + *pstr = pszBuf; + } +}; + + + + + +// +// Forward declarations +// + +// string +template unsigned int GetSerializeSize(const std::basic_string& str, int, int=0); +template void Serialize(Stream& os, const std::basic_string& str, int, int=0); +template void Unserialize(Stream& is, std::basic_string& str, int, int=0); + +// vector +template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&); +template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion=VERSION); +template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&); +template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion=VERSION); +template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&); +template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion=VERSION); + +// others derived from vector +extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const CScript& v, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, CScript& v, int nType, int nVersion=VERSION); + +// pair +template unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const std::pair& item, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, std::pair& item, int nType, int nVersion=VERSION); + +// 3 tuple +template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion=VERSION); + +// 4 tuple +template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion=VERSION); + +// map +template unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const std::map& m, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, std::map& m, int nType, int nVersion=VERSION); + +// set +template unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const std::set& m, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, std::set& m, int nType, int nVersion=VERSION); + + + + + +// +// If none of the specialized versions above matched, default to calling member function. +// "int nType" is changed to "long nType" to keep from getting an ambiguous overload error. +// The compiler will only cast int to long if none of the other templates matched. +// Thanks to Boost serialization for this idea. +// +template +inline unsigned int GetSerializeSize(const T& a, long nType, int nVersion=VERSION) +{ + return a.GetSerializeSize((int)nType, nVersion); +} + +template +inline void Serialize(Stream& os, const T& a, long nType, int nVersion=VERSION) +{ + a.Serialize(os, (int)nType, nVersion); +} + +template +inline void Unserialize(Stream& is, T& a, long nType, int nVersion=VERSION) +{ + a.Unserialize(is, (int)nType, nVersion); +} + + + + + +// +// string +// +template +unsigned int GetSerializeSize(const std::basic_string& str, int, int) +{ + return GetSizeOfCompactSize(str.size()) + str.size() * sizeof(str[0]); +} + +template +void Serialize(Stream& os, const std::basic_string& str, int, int) +{ + WriteCompactSize(os, str.size()); + if (!str.empty()) + os.write((char*)&str[0], str.size() * sizeof(str[0])); +} + +template +void Unserialize(Stream& is, std::basic_string& str, int, int) +{ + unsigned int nSize = ReadCompactSize(is); + str.resize(nSize); + if (nSize != 0) + is.read((char*)&str[0], nSize * sizeof(str[0])); +} + + + +// +// vector +// +template +unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); +} + +template +unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + unsigned int nSize = GetSizeOfCompactSize(v.size()); + for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + nSize += GetSerializeSize((*vi), nType, nVersion); + return nSize; +} + +template +inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion) +{ + return GetSerializeSize_impl(v, nType, nVersion, boost::is_fundamental()); +} + + +template +void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + WriteCompactSize(os, v.size()); + if (!v.empty()) + os.write((char*)&v[0], v.size() * sizeof(T)); +} + +template +void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + WriteCompactSize(os, v.size()); + for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + ::Serialize(os, (*vi), nType, nVersion); +} + +template +inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion) +{ + Serialize_impl(os, v, nType, nVersion, boost::is_fundamental()); +} + + +template +void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + //unsigned int nSize = ReadCompactSize(is); + //v.resize(nSize); + //is.read((char*)&v[0], nSize * sizeof(T)); + + // Limit size per read so bogus size value won't cause out of memory + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + while (i < nSize) + { + unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); + v.resize(i + blk); + is.read((char*)&v[i], blk * sizeof(T)); + i += blk; + } +} + +template +void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + //unsigned int nSize = ReadCompactSize(is); + //v.resize(nSize); + //for (std::vector::iterator vi = v.begin(); vi != v.end(); ++vi) + // Unserialize(is, (*vi), nType, nVersion); + + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + unsigned int nMid = 0; + while (nMid < nSize) + { + nMid += 5000000 / sizeof(T); + if (nMid > nSize) + nMid = nSize; + v.resize(nMid); + for (; i < nMid; i++) + Unserialize(is, v[i], nType, nVersion); + } +} + +template +inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion) +{ + Unserialize_impl(is, v, nType, nVersion, boost::is_fundamental()); +} + + + +// +// others derived from vector +// +inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion) +{ + return GetSerializeSize((const std::vector&)v, nType, nVersion); +} + +template +void Serialize(Stream& os, const CScript& v, int nType, int nVersion) +{ + Serialize(os, (const std::vector&)v, nType, nVersion); +} + +template +void Unserialize(Stream& is, CScript& v, int nType, int nVersion) +{ + Unserialize(is, (std::vector&)v, nType, nVersion); +} + + + +// +// pair +// +template +unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion) +{ + return GetSerializeSize(item.first, nType, nVersion) + GetSerializeSize(item.second, nType, nVersion); +} + +template +void Serialize(Stream& os, const std::pair& item, int nType, int nVersion) +{ + Serialize(os, item.first, nType, nVersion); + Serialize(os, item.second, nType, nVersion); +} + +template +void Unserialize(Stream& is, std::pair& item, int nType, int nVersion) +{ + Unserialize(is, item.first, nType, nVersion); + Unserialize(is, item.second, nType, nVersion); +} + + + +// +// 3 tuple +// +template +unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) +{ + unsigned int nSize = 0; + nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) +{ + Serialize(os, boost::get<0>(item), nType, nVersion); + Serialize(os, boost::get<1>(item), nType, nVersion); + Serialize(os, boost::get<2>(item), nType, nVersion); +} + +template +void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) +{ + Unserialize(is, boost::get<0>(item), nType, nVersion); + Unserialize(is, boost::get<1>(item), nType, nVersion); + Unserialize(is, boost::get<2>(item), nType, nVersion); +} + + + +// +// 4 tuple +// +template +unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) +{ + unsigned int nSize = 0; + nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<3>(item), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) +{ + Serialize(os, boost::get<0>(item), nType, nVersion); + Serialize(os, boost::get<1>(item), nType, nVersion); + Serialize(os, boost::get<2>(item), nType, nVersion); + Serialize(os, boost::get<3>(item), nType, nVersion); +} + +template +void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) +{ + Unserialize(is, boost::get<0>(item), nType, nVersion); + Unserialize(is, boost::get<1>(item), nType, nVersion); + Unserialize(is, boost::get<2>(item), nType, nVersion); + Unserialize(is, boost::get<3>(item), nType, nVersion); +} + + + +// +// map +// +template +unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion) +{ + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) + nSize += GetSerializeSize((*mi), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const std::map& m, int nType, int nVersion) +{ + WriteCompactSize(os, m.size()); + for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) + Serialize(os, (*mi), nType, nVersion); +} + +template +void Unserialize(Stream& is, std::map& m, int nType, int nVersion) +{ + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::map::iterator mi = m.begin(); + for (unsigned int i = 0; i < nSize; i++) + { + std::pair item; + Unserialize(is, item, nType, nVersion); + mi = m.insert(mi, item); + } +} + + + +// +// set +// +template +unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion) +{ + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) + nSize += GetSerializeSize((*it), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const std::set& m, int nType, int nVersion) +{ + WriteCompactSize(os, m.size()); + for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) + Serialize(os, (*it), nType, nVersion); +} + +template +void Unserialize(Stream& is, std::set& m, int nType, int nVersion) +{ + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::set::iterator it = m.begin(); + for (unsigned int i = 0; i < nSize; i++) + { + K key; + Unserialize(is, key, nType, nVersion); + it = m.insert(it, key); + } +} + + + +// +// Support for IMPLEMENT_SERIALIZE and READWRITE macro +// +class CSerActionGetSerializeSize { }; +class CSerActionSerialize { }; +class CSerActionUnserialize { }; + +template +inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionGetSerializeSize ser_action) +{ + return ::GetSerializeSize(obj, nType, nVersion); +} + +template +inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionSerialize ser_action) +{ + ::Serialize(s, obj, nType, nVersion); + return 0; +} + +template +inline unsigned int SerReadWrite(Stream& s, T& obj, int nType, int nVersion, CSerActionUnserialize ser_action) +{ + ::Unserialize(s, obj, nType, nVersion); + return 0; +} + +struct ser_streamplaceholder +{ + int nType; + int nVersion; +}; + + + + + + + + + +// +// Allocator that clears its contents before deletion +// +template +struct secure_allocator : public std::allocator +{ + // MSVC8 default copy constructor is broken + typedef std::allocator base; + typedef typename base::size_type size_type; + typedef typename base::difference_type difference_type; + typedef typename base::pointer pointer; + typedef typename base::const_pointer const_pointer; + typedef typename base::reference reference; + typedef typename base::const_reference const_reference; + typedef typename base::value_type value_type; + secure_allocator() throw() {} + secure_allocator(const secure_allocator& a) throw() : base(a) {} + template + secure_allocator(const secure_allocator& a) throw() : base(a) {} + ~secure_allocator() throw() {} + template struct rebind + { typedef secure_allocator<_Other> other; }; + + void deallocate(T* p, std::size_t n) + { + if (p != NULL) + memset(p, 0, sizeof(T) * n); + std::allocator::deallocate(p, n); + } +}; + + + +// +// Double ended buffer combining vector and stream-like interfaces. +// >> and << read and write unformatted data using the above serialization templates. +// Fills with data in linear time; some stringstream implementations take N^2 time. +// +class CDataStream +{ +protected: + typedef std::vector > vector_type; + vector_type vch; + unsigned int nReadPos; + short state; + short exceptmask; +public: + int nType; + int nVersion; + + typedef vector_type::allocator_type allocator_type; + typedef vector_type::size_type size_type; + typedef vector_type::difference_type difference_type; + typedef vector_type::reference reference; + typedef vector_type::const_reference const_reference; + typedef vector_type::value_type value_type; + typedef vector_type::iterator iterator; + typedef vector_type::const_iterator const_iterator; + typedef vector_type::reverse_iterator reverse_iterator; + + explicit CDataStream(int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(pbegin, pend) + { + Init(nTypeIn, nVersionIn); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + CDataStream(const char* pbegin, const char* pend, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(pbegin, pend) + { + Init(nTypeIn, nVersionIn); + } +#endif + + CDataStream(const vector_type& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end()) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end()) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch((char*)&vchIn.begin()[0], (char*)&vchIn.end()[0]) + { + Init(nTypeIn, nVersionIn); + } + + void Init(int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) + { + nReadPos = 0; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = std::ios::badbit | std::ios::failbit; + } + + CDataStream& operator+=(const CDataStream& b) + { + vch.insert(vch.end(), b.begin(), b.end()); + return *this; + } + + friend CDataStream operator+(const CDataStream& a, const CDataStream& b) + { + CDataStream ret = a; + ret += b; + return (ret); + } + + std::string str() const + { + return (std::string(begin(), end())); + } + + + // + // Vector subset + // + const_iterator begin() const { return vch.begin() + nReadPos; } + iterator begin() { return vch.begin() + nReadPos; } + const_iterator end() const { return vch.end(); } + iterator end() { return vch.end(); } + size_type size() const { return vch.size() - nReadPos; } + bool empty() const { return vch.size() == nReadPos; } + void resize(size_type n, value_type c=0) { vch.resize(n + nReadPos, c); } + void reserve(size_type n) { vch.reserve(n + nReadPos); } + const_reference operator[](size_type pos) const { return vch[pos + nReadPos]; } + reference operator[](size_type pos) { return vch[pos + nReadPos]; } + void clear() { vch.clear(); nReadPos = 0; } + iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); } + void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); } + + void insert(iterator it, const_iterator first, const_iterator last) + { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } + else + vch.insert(it, first, last); + } + + void insert(iterator it, std::vector::const_iterator first, std::vector::const_iterator last) + { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } + else + vch.insert(it, first, last); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + void insert(iterator it, const char* first, const char* last) + { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } + else + vch.insert(it, first, last); + } +#endif + + iterator erase(iterator it) + { + if (it == vch.begin() + nReadPos) + { + // special case for erasing from the front + if (++nReadPos >= vch.size()) + { + // whenever we reach the end, we take the opportunity to clear the buffer + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } + return vch.begin() + nReadPos; + } + else + return vch.erase(it); + } + + iterator erase(iterator first, iterator last) + { + if (first == vch.begin() + nReadPos) + { + // special case for erasing from the front + if (last == vch.end()) + { + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } + else + { + nReadPos = (last - vch.begin()); + return last; + } + } + else + return vch.erase(first, last); + } + + inline void Compact() + { + vch.erase(vch.begin(), vch.begin() + nReadPos); + nReadPos = 0; + } + + bool Rewind(size_type n) + { + // Rewind by n characters if the buffer hasn't been compacted yet + if (n > nReadPos) + return false; + nReadPos -= n; + return true; + } + + + // + // Stream subset + // + void setstate(short bits, const char* psz) + { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + bool eof() const { return size() == 0; } + bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } + bool good() const { return !eof() && (state == 0); } + void clear(short n) { state = n; } // name conflict with vector clear() + short exceptions() { return exceptmask; } + short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CDataStream"); return prev; } + CDataStream* rdbuf() { return this; } + int in_avail() { return size(); } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CDataStream& read(char* pch, int nSize) + { + // Read from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) + { + if (nReadPosNext > vch.size()) + { + setstate(std::ios::failbit, "CDataStream::read() : end of data"); + memset(pch, 0, nSize); + nSize = vch.size() - nReadPos; + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = 0; + vch.clear(); + return (*this); + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream& ignore(int nSize) + { + // Ignore from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) + { + if (nReadPosNext > vch.size()) + { + setstate(std::ios::failbit, "CDataStream::ignore() : end of data"); + nSize = vch.size() - nReadPos; + } + nReadPos = 0; + vch.clear(); + return (*this); + } + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream& write(const char* pch, int nSize) + { + // Write to the end of the buffer + assert(nSize >= 0); + vch.insert(vch.end(), pch, pch + nSize); + return (*this); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const + { + // Special case: stream << stream concatenates like stream += stream + if (!vch.empty()) + s.write((char*)&vch[0], vch.size() * sizeof(vch[0])); + } + + template + unsigned int GetSerializeSize(const T& obj) + { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template + CDataStream& operator<<(const T& obj) + { + // Serialize to this stream + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template + CDataStream& operator>>(T& obj) + { + // Unserialize from this stream + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } +}; + +#ifdef TESTCDATASTREAM +// VC6sp6 +// CDataStream: +// n=1000 0 seconds +// n=2000 0 seconds +// n=4000 0 seconds +// n=8000 0 seconds +// n=16000 0 seconds +// n=32000 0 seconds +// n=64000 1 seconds +// n=128000 1 seconds +// n=256000 2 seconds +// n=512000 4 seconds +// n=1024000 8 seconds +// n=2048000 16 seconds +// n=4096000 32 seconds +// stringstream: +// n=1000 1 seconds +// n=2000 1 seconds +// n=4000 13 seconds +// n=8000 87 seconds +// n=16000 400 seconds +// n=32000 1660 seconds +// n=64000 6749 seconds +// n=128000 27241 seconds +// n=256000 109804 seconds +#include +int main(int argc, char *argv[]) +{ + vector vch(0xcc, 250); + printf("CDataStream:\n"); + for (int n = 1000; n <= 4500000; n *= 2) + { + CDataStream ss; + time_t nStart = time(NULL); + for (int i = 0; i < n; i++) + ss.write((char*)&vch[0], vch.size()); + printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); + } + printf("stringstream:\n"); + for (int n = 1000; n <= 4500000; n *= 2) + { + stringstream ss; + time_t nStart = time(NULL); + for (int i = 0; i < n; i++) + ss.write((char*)&vch[0], vch.size()); + printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); + } +} +#endif + + + + + + + + + + +// +// Automatic closing wrapper for FILE* +// - Will automatically close the file when it goes out of scope if not null. +// - If you're returning the file pointer, return file.release(). +// - If you need to close the file early, use file.fclose() instead of fclose(file). +// +class CAutoFile +{ +protected: + FILE* file; + short state; + short exceptmask; +public: + int nType; + int nVersion; + + typedef FILE element_type; + + CAutoFile(FILE* filenew=NULL, int nTypeIn=SER_DISK, int nVersionIn=VERSION) + { + file = filenew; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = std::ios::badbit | std::ios::failbit; + } + + ~CAutoFile() + { + fclose(); + } + + void fclose() + { + if (file != NULL && file != stdin && file != stdout && file != stderr) + ::fclose(file); + file = NULL; + } + + FILE* release() { FILE* ret = file; file = NULL; return ret; } + operator FILE*() { return file; } + FILE* operator->() { return file; } + FILE& operator*() { return *file; } + FILE** operator&() { return &file; } + FILE* operator=(FILE* pnew) { return file = pnew; } + bool operator!() { return (file == NULL); } + + + // + // Stream subset + // + void setstate(short bits, const char* psz) + { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } + bool good() const { return state == 0; } + void clear(short n = 0) { state = n; } + short exceptions() { return exceptmask; } + short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CAutoFile"); return prev; } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CAutoFile& read(char* pch, int nSize) + { + if (!file) + throw std::ios_base::failure("CAutoFile::read : file handle is NULL"); + if (fread(pch, 1, nSize, file) != nSize) + setstate(std::ios::failbit, feof(file) ? "CAutoFile::read : end of file" : "CAutoFile::read : fread failed"); + return (*this); + } + + CAutoFile& write(const char* pch, int nSize) + { + if (!file) + throw std::ios_base::failure("CAutoFile::write : file handle is NULL"); + if (fwrite(pch, 1, nSize, file) != nSize) + setstate(std::ios::failbit, "CAutoFile::write : write failed"); + return (*this); + } + + template + unsigned int GetSerializeSize(const T& obj) + { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template + CAutoFile& operator<<(const T& obj) + { + // Serialize to this stream + if (!file) + throw std::ios_base::failure("CAutoFile::operator<< : file handle is NULL"); + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template + CAutoFile& operator>>(T& obj) + { + // Unserialize from this stream + if (!file) + throw std::ios_base::failure("CAutoFile::operator>> : file handle is NULL"); + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } +}; + +#endif diff --git a/src/strlcpy.h b/src/strlcpy.h new file mode 100644 index 0000000000..d4d1908e7a --- /dev/null +++ b/src/strlcpy.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BITCOIN_STRLCPY_H +#define BITCOIN_STRLCPY_H +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +inline size_t strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) + { + while (--n != 0) + { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) + { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +inline size_t strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') + { + if (n != 1) + { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} +#endif diff --git a/src/uint256.h b/src/uint256.h new file mode 100644 index 0000000000..14feb1683d --- /dev/null +++ b/src/uint256.h @@ -0,0 +1,765 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UINT256_H +#define BITCOIN_UINT256_H + +#include "serialize.h" + +#include +#include +#include + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false) ; else for +#endif + + +inline int Testuint256AdHoc(std::vector vArg); + + + +// We have to keep a separate base class without constructors +// so the compiler will let us use it in a union +template +class base_uint +{ +protected: + enum { WIDTH=BITS/32 }; + unsigned int pn[WIDTH]; +public: + + bool operator!() const + { + for (int i = 0; i < WIDTH; i++) + if (pn[i] != 0) + return false; + return true; + } + + const base_uint operator~() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + return ret; + } + + const base_uint operator-() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + ret++; + return ret; + } + + + base_uint& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + base_uint& operator^=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] ^= b.pn[i]; + return *this; + } + + base_uint& operator&=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] &= b.pn[i]; + return *this; + } + + base_uint& operator|=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] |= b.pn[i]; + return *this; + } + + base_uint& operator^=(uint64 b) + { + pn[0] ^= (unsigned int)b; + pn[1] ^= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator&=(uint64 b) + { + pn[0] &= (unsigned int)b; + pn[1] &= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator|=(uint64 b) + { + pn[0] |= (unsigned int)b; + pn[1] |= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator<<=(unsigned int shift) + { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i+k+1 < WIDTH && shift != 0) + pn[i+k+1] |= (a.pn[i] >> (32-shift)); + if (i+k < WIDTH) + pn[i+k] |= (a.pn[i] << shift); + } + return *this; + } + + base_uint& operator>>=(unsigned int shift) + { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i-k-1 >= 0 && shift != 0) + pn[i-k-1] |= (a.pn[i] << (32-shift)); + if (i-k >= 0) + pn[i-k] |= (a.pn[i] >> shift); + } + return *this; + } + + base_uint& operator+=(const base_uint& b) + { + uint64 carry = 0; + for (int i = 0; i < WIDTH; i++) + { + uint64 n = carry + pn[i] + b.pn[i]; + pn[i] = n & 0xffffffff; + carry = n >> 32; + } + return *this; + } + + base_uint& operator-=(const base_uint& b) + { + *this += -b; + return *this; + } + + base_uint& operator+=(uint64 b64) + { + base_uint b; + b = b64; + *this += b; + return *this; + } + + base_uint& operator-=(uint64 b64) + { + base_uint b; + b = b64; + *this += -b; + return *this; + } + + + base_uint& operator++() + { + // prefix operator + int i = 0; + while (++pn[i] == 0 && i < WIDTH-1) + i++; + return *this; + } + + const base_uint operator++(int) + { + // postfix operator + const base_uint ret = *this; + ++(*this); + return ret; + } + + base_uint& operator--() + { + // prefix operator + int i = 0; + while (--pn[i] == -1 && i < WIDTH-1) + i++; + return *this; + } + + const base_uint operator--(int) + { + // postfix operator + const base_uint ret = *this; + --(*this); + return ret; + } + + + friend inline bool operator<(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator<=(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator>(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator>=(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator==(const base_uint& a, const base_uint& b) + { + for (int i = 0; i < base_uint::WIDTH; i++) + if (a.pn[i] != b.pn[i]) + return false; + return true; + } + + friend inline bool operator==(const base_uint& a, uint64 b) + { + if (a.pn[0] != (unsigned int)b) + return false; + if (a.pn[1] != (unsigned int)(b >> 32)) + return false; + for (int i = 2; i < base_uint::WIDTH; i++) + if (a.pn[i] != 0) + return false; + return true; + } + + friend inline bool operator!=(const base_uint& a, const base_uint& b) + { + return (!(a == b)); + } + + friend inline bool operator!=(const base_uint& a, uint64 b) + { + return (!(a == b)); + } + + + + std::string GetHex() const + { + char psz[sizeof(pn)*2 + 1]; + for (int i = 0; i < sizeof(pn); i++) + sprintf(psz + i*2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]); + return std::string(psz, psz + sizeof(pn)*2); + } + + void SetHex(const char* psz) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + + // skip leading spaces + while (isspace(*psz)) + psz++; + + // skip 0x + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + + // hex string to uint + static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; + const char* pbegin = psz; + while (phexdigit[*psz] || *psz == '0') + psz++; + psz--; + unsigned char* p1 = (unsigned char*)pn; + unsigned char* pend = p1 + WIDTH * 4; + while (psz >= pbegin && p1 < pend) + { + *p1 = phexdigit[(unsigned char)*psz--]; + if (psz >= pbegin) + { + *p1 |= (phexdigit[(unsigned char)*psz--] << 4); + p1++; + } + } + } + + void SetHex(const std::string& str) + { + SetHex(str.c_str()); + } + + std::string ToString() const + { + return (GetHex()); + } + + unsigned char* begin() + { + return (unsigned char*)&pn[0]; + } + + unsigned char* end() + { + return (unsigned char*)&pn[WIDTH]; + } + + unsigned int size() + { + return sizeof(pn); + } + + + unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const + { + return sizeof(pn); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const + { + s.write((char*)pn, sizeof(pn)); + } + + template + void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) + { + s.read((char*)pn, sizeof(pn)); + } + + + friend class uint160; + friend class uint256; + friend inline int Testuint256AdHoc(std::vector vArg); +}; + +typedef base_uint<160> base_uint160; +typedef base_uint<256> base_uint256; + + + +// +// uint160 and uint256 could be implemented as templates, but to keep +// compile errors and debugging cleaner, they're copy and pasted. +// + + + +////////////////////////////////////////////////////////////////////////////// +// +// uint160 +// + +class uint160 : public base_uint160 +{ +public: + typedef base_uint160 basetype; + + uint160() + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint160(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint160& operator=(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint160(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint160& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint160(const std::string& str) + { + SetHex(str); + } + + explicit uint160(const std::vector& vch) + { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint160& a, uint64 b) { return (base_uint160)a == b; } +inline bool operator!=(const uint160& a, uint64 b) { return (base_uint160)a != b; } +inline const uint160 operator<<(const base_uint160& a, unsigned int shift) { return uint160(a) <<= shift; } +inline const uint160 operator>>(const base_uint160& a, unsigned int shift) { return uint160(a) >>= shift; } +inline const uint160 operator<<(const uint160& a, unsigned int shift) { return uint160(a) <<= shift; } +inline const uint160 operator>>(const uint160& a, unsigned int shift) { return uint160(a) >>= shift; } + +inline const uint160 operator^(const base_uint160& a, const base_uint160& b) { return uint160(a) ^= b; } +inline const uint160 operator&(const base_uint160& a, const base_uint160& b) { return uint160(a) &= b; } +inline const uint160 operator|(const base_uint160& a, const base_uint160& b) { return uint160(a) |= b; } +inline const uint160 operator+(const base_uint160& a, const base_uint160& b) { return uint160(a) += b; } +inline const uint160 operator-(const base_uint160& a, const base_uint160& b) { return uint160(a) -= b; } + +inline bool operator<(const base_uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const base_uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const base_uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const base_uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const base_uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const base_uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const base_uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const base_uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const base_uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const base_uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const base_uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160& a, const base_uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160& a, const base_uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160& a, const base_uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160& a, const base_uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160& a, const base_uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160& a, const base_uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160& a, const base_uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160& a, const base_uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160& a, const base_uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160& a, const base_uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160& a, const base_uint160& b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// uint256 +// + +class uint256 : public base_uint256 +{ +public: + typedef base_uint256 basetype; + + uint256() + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint256(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint256& operator=(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint256(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint256& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint256(const std::string& str) + { + SetHex(str); + } + + explicit uint256(const std::vector& vch) + { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint256& a, uint64 b) { return (base_uint256)a == b; } +inline bool operator!=(const uint256& a, uint64 b) { return (base_uint256)a != b; } +inline const uint256 operator<<(const base_uint256& a, unsigned int shift) { return uint256(a) <<= shift; } +inline const uint256 operator>>(const base_uint256& a, unsigned int shift) { return uint256(a) >>= shift; } +inline const uint256 operator<<(const uint256& a, unsigned int shift) { return uint256(a) <<= shift; } +inline const uint256 operator>>(const uint256& a, unsigned int shift) { return uint256(a) >>= shift; } + +inline const uint256 operator^(const base_uint256& a, const base_uint256& b) { return uint256(a) ^= b; } +inline const uint256 operator&(const base_uint256& a, const base_uint256& b) { return uint256(a) &= b; } +inline const uint256 operator|(const base_uint256& a, const base_uint256& b) { return uint256(a) |= b; } +inline const uint256 operator+(const base_uint256& a, const base_uint256& b) { return uint256(a) += b; } +inline const uint256 operator-(const base_uint256& a, const base_uint256& b) { return uint256(a) -= b; } + +inline bool operator<(const base_uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const base_uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const base_uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const base_uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const base_uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const base_uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const base_uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const base_uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const base_uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const base_uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const base_uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } + +inline bool operator<(const uint256& a, const base_uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const uint256& a, const base_uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const uint256& a, const base_uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const uint256& a, const base_uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const uint256& a, const base_uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const uint256& a, const base_uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const uint256& a, const base_uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const uint256& a, const base_uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const uint256& a, const base_uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const uint256& a, const base_uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const uint256& a, const base_uint256& b) { return (base_uint256)a - (base_uint256)b; } + +inline bool operator<(const uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } + + + + + + + + + + + + +inline int Testuint256AdHoc(std::vector vArg) +{ + uint256 g(0); + + + printf("%s\n", g.ToString().c_str()); + g--; printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g--; printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + + + + uint256 a(7); + printf("a=7\n"); + printf("%s\n", a.ToString().c_str()); + + uint256 b; + printf("b undefined\n"); + printf("%s\n", b.ToString().c_str()); + int c = 3; + + a = c; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + uint256 k(c); + + a = 5; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + b = 1; + b <<= 52; + + a |= b; + + a ^= 0x500; + + printf("a %s\n", a.ToString().c_str()); + + a = a | b | (uint256)0x1000; + + + printf("a %s\n", a.ToString().c_str()); + printf("b %s\n", b.ToString().c_str()); + + a = 0xfffffffe; + a.pn[4] = 9; + + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + uint256 d = a--; + printf("%s\n", d.ToString().c_str()); + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + + d = a; + + printf("%s\n", d.ToString().c_str()); + for (int i = uint256::WIDTH-1; i >= 0; i--) printf("%08x", d.pn[i]); printf("\n"); + + uint256 neg = d; + neg = ~neg; + printf("%s\n", neg.ToString().c_str()); + + + uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + printf("\n"); + printf("%s\n", e.ToString().c_str()); + + + printf("\n"); + uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + uint256 x2; + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) + { + x2 = x1 << i; + printf("%s\n", x2.ToString().c_str()); + } + + printf("\n"); + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) + { + x2 = x1; + x2 >>= i; + printf("%s\n", x2.ToString().c_str()); + } + + + for (int i = 0; i < 100; i++) + { + uint256 k = (~uint256(0) >> i); + printf("%s\n", k.ToString().c_str()); + } + + for (int i = 0; i < 100; i++) + { + uint256 k = (~uint256(0) << i); + printf("%s\n", k.ToString().c_str()); + } + + return (0); +} + +#endif diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000000..6199109289 --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,906 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#include "headers.h" + +using namespace std; +using namespace boost; + +map mapArgs; +map > mapMultiArgs; +bool fDebug = false; +bool fPrintToConsole = false; +bool fPrintToDebugger = false; +char pszSetDataDir[MAX_PATH] = ""; +bool fRequestShutdown = false; +bool fShutdown = false; +bool fDaemon = false; +bool fServer = false; +bool fCommandLine = false; +string strMiscWarning; +bool fTestNet = false; +bool fNoListen = false; +bool fLogTimestamps = false; + + + + +// Workaround for "multiple definition of `_tls_used'" +// http://svn.boost.org/trac/boost/ticket/4258 +extern "C" void tss_cleanup_implemented() { } + + + + + +// Init openssl library multithreading support +static boost::interprocess::interprocess_mutex** ppmutexOpenSSL; +void locking_callback(int mode, int i, const char* file, int line) +{ + if (mode & CRYPTO_LOCK) + ppmutexOpenSSL[i]->lock(); + else + ppmutexOpenSSL[i]->unlock(); +} + +// Init +class CInit +{ +public: + CInit() + { + // Init openssl library multithreading support + ppmutexOpenSSL = (boost::interprocess::interprocess_mutex**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(boost::interprocess::interprocess_mutex*)); + for (int i = 0; i < CRYPTO_num_locks(); i++) + ppmutexOpenSSL[i] = new boost::interprocess::interprocess_mutex(); + CRYPTO_set_locking_callback(locking_callback); + +#ifdef __WXMSW__ + // Seed random number generator with screen scrape and other hardware sources + RAND_screen(); +#endif + + // Seed random number generator with performance counter + RandAddSeed(); + } + ~CInit() + { + // Shutdown openssl library multithreading support + CRYPTO_set_locking_callback(NULL); + for (int i = 0; i < CRYPTO_num_locks(); i++) + delete ppmutexOpenSSL[i]; + OPENSSL_free(ppmutexOpenSSL); + } +} +instance_of_cinit; + + + + + + + + +void RandAddSeed() +{ + // Seed with CPU performance counter + int64 nCounter = GetPerformanceCounter(); + RAND_add(&nCounter, sizeof(nCounter), 1.5); + memset(&nCounter, 0, sizeof(nCounter)); +} + +void RandAddSeedPerfmon() +{ + RandAddSeed(); + + // This can take up to 2 seconds, so only do it every 10 minutes + static int64 nLastPerfmon; + if (GetTime() < nLastPerfmon + 10 * 60) + return; + nLastPerfmon = GetTime(); + +#ifdef __WXMSW__ + // Don't need this on Linux, OpenSSL automatically uses /dev/urandom + // Seed with the entire set of perfmon data + unsigned char pdata[250000]; + memset(pdata, 0, sizeof(pdata)); + unsigned long nSize = sizeof(pdata); + long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize); + RegCloseKey(HKEY_PERFORMANCE_DATA); + if (ret == ERROR_SUCCESS) + { + RAND_add(pdata, nSize, nSize/100.0); + memset(pdata, 0, nSize); + printf("%s RandAddSeed() %d bytes\n", DateTimeStrFormat("%x %H:%M", GetTime()).c_str(), nSize); + } +#endif +} + +uint64 GetRand(uint64 nMax) +{ + if (nMax == 0) + return 0; + + // The range of the random source must be a multiple of the modulus + // to give every possible output value an equal possibility + uint64 nRange = (UINT64_MAX / nMax) * nMax; + uint64 nRand = 0; + do + RAND_bytes((unsigned char*)&nRand, sizeof(nRand)); + while (nRand >= nRange); + return (nRand % nMax); +} + +int GetRandInt(int nMax) +{ + return GetRand(nMax); +} + + + + + + + + + + + +inline int OutputDebugStringF(const char* pszFormat, ...) +{ + int ret = 0; + if (fPrintToConsole) + { + // print to console + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + ret = vprintf(pszFormat, arg_ptr); + va_end(arg_ptr); + } + else + { + // print to debug.log + static FILE* fileout = NULL; + + if (!fileout) + { + char pszFile[MAX_PATH+100]; + GetDataDir(pszFile); + strlcat(pszFile, "/debug.log", sizeof(pszFile)); + fileout = fopen(pszFile, "a"); + if (fileout) setbuf(fileout, NULL); // unbuffered + } + if (fileout) + { + static bool fStartedNewLine = true; + + // Debug print useful for profiling + if (fLogTimestamps && fStartedNewLine) + fprintf(fileout, "%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); + if (pszFormat[strlen(pszFormat) - 1] == '\n') + fStartedNewLine = true; + else + fStartedNewLine = false; + + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + ret = vfprintf(fileout, pszFormat, arg_ptr); + va_end(arg_ptr); + } + } + +#ifdef __WXMSW__ + if (fPrintToDebugger) + { + static CCriticalSection cs_OutputDebugStringF; + + // accumulate a line at a time + CRITICAL_BLOCK(cs_OutputDebugStringF) + { + static char pszBuffer[50000]; + static char* pend; + if (pend == NULL) + pend = pszBuffer; + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + int limit = END(pszBuffer) - pend - 2; + int ret = _vsnprintf(pend, limit, pszFormat, arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + pend = END(pszBuffer) - 2; + *pend++ = '\n'; + } + else + pend += ret; + *pend = '\0'; + char* p1 = pszBuffer; + char* p2; + while (p2 = strchr(p1, '\n')) + { + p2++; + char c = *p2; + *p2 = '\0'; + OutputDebugStringA(p1); + *p2 = c; + p1 = p2; + } + if (p1 != pszBuffer) + memmove(pszBuffer, p1, pend - p1 + 1); + pend -= (p1 - pszBuffer); + } + } +#endif + return ret; +} + + +// Safer snprintf +// - prints up to limit-1 characters +// - output string is always null terminated even if limit reached +// - return value is the number of characters actually printed +int my_snprintf(char* buffer, size_t limit, const char* format, ...) +{ + if (limit == 0) + return 0; + va_list arg_ptr; + va_start(arg_ptr, format); + int ret = _vsnprintf(buffer, limit, format, arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + ret = limit - 1; + buffer[limit-1] = 0; + } + return ret; +} + + +string strprintf(const char* format, ...) +{ + char buffer[50000]; + char* p = buffer; + int limit = sizeof(buffer); + int ret; + loop + { + va_list arg_ptr; + va_start(arg_ptr, format); + ret = _vsnprintf(p, limit, format, arg_ptr); + va_end(arg_ptr); + if (ret >= 0 && ret < limit) + break; + if (p != buffer) + delete[] p; + limit *= 2; + p = new char[limit]; + if (p == NULL) + throw std::bad_alloc(); + } + string str(p, p+ret); + if (p != buffer) + delete[] p; + return str; +} + + +bool error(const char* format, ...) +{ + char buffer[50000]; + int limit = sizeof(buffer); + va_list arg_ptr; + va_start(arg_ptr, format); + int ret = _vsnprintf(buffer, limit, format, arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + ret = limit - 1; + buffer[limit-1] = 0; + } + printf("ERROR: %s\n", buffer); + return false; +} + + +void ParseString(const string& str, char c, vector& v) +{ + if (str.empty()) + return; + string::size_type i1 = 0; + string::size_type i2; + loop + { + i2 = str.find(c, i1); + if (i2 == str.npos) + { + v.push_back(str.substr(i1)); + return; + } + v.push_back(str.substr(i1, i2-i1)); + i1 = i2+1; + } +} + + +string FormatMoney(int64 n, bool fPlus) +{ + // Note: not using straight sprintf here because we do NOT want + // localized number formatting. + int64 n_abs = (n > 0 ? n : -n); + int64 quotient = n_abs/COIN; + int64 remainder = n_abs%COIN; + string str = strprintf("%"PRI64d".%08"PRI64d, quotient, remainder); + + // Right-trim excess 0's before the decimal point: + int nTrim = 0; + for (int i = str.size()-1; (str[i] == '0' && isdigit(str[i-2])); --i) + ++nTrim; + if (nTrim) + str.erase(str.size()-nTrim, nTrim); + + // Insert thousands-separators: + size_t point = str.find("."); + for (int i = (str.size()-point)+3; i < str.size(); i += 4) + if (isdigit(str[str.size() - i - 1])) + str.insert(str.size() - i, 1, ','); + if (n < 0) + str.insert((unsigned int)0, 1, '-'); + else if (fPlus && n > 0) + str.insert((unsigned int)0, 1, '+'); + return str; +} + + +bool ParseMoney(const string& str, int64& nRet) +{ + return ParseMoney(str.c_str(), nRet); +} + +bool ParseMoney(const char* pszIn, int64& nRet) +{ + string strWhole; + int64 nUnits = 0; + const char* p = pszIn; + while (isspace(*p)) + p++; + for (; *p; p++) + { + if (*p == ',' && p > pszIn && isdigit(p[-1]) && isdigit(p[1]) && isdigit(p[2]) && isdigit(p[3]) && !isdigit(p[4])) + continue; + if (*p == '.') + { + p++; + int64 nMult = CENT*10; + while (isdigit(*p) && (nMult > 0)) + { + nUnits += nMult * (*p++ - '0'); + nMult /= 10; + } + break; + } + if (isspace(*p)) + break; + if (!isdigit(*p)) + return false; + strWhole.insert(strWhole.end(), *p); + } + for (; *p; p++) + if (!isspace(*p)) + return false; + if (strWhole.size() > 14) + return false; + if (nUnits < 0 || nUnits > COIN) + return false; + int64 nWhole = atoi64(strWhole); + int64 nValue = nWhole*COIN + nUnits; + + nRet = nValue; + return true; +} + + +vector ParseHex(const char* psz) +{ + static char phexdigit[256] = + { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1, + -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, }; + + // convert hex dump to vector + vector vch; + loop + { + while (isspace(*psz)) + psz++; + char c = phexdigit[(unsigned char)*psz++]; + if (c == (char)-1) + break; + unsigned char n = (c << 4); + c = phexdigit[(unsigned char)*psz++]; + if (c == (char)-1) + break; + n |= c; + vch.push_back(n); + } + return vch; +} + +vector ParseHex(const string& str) +{ + return ParseHex(str.c_str()); +} + + +void ParseParameters(int argc, char* argv[]) +{ + mapArgs.clear(); + mapMultiArgs.clear(); + for (int i = 1; i < argc; i++) + { + char psz[10000]; + strlcpy(psz, argv[i], sizeof(psz)); + char* pszValue = (char*)""; + if (strchr(psz, '=')) + { + pszValue = strchr(psz, '='); + *pszValue++ = '\0'; + } + #ifdef __WXMSW__ + _strlwr(psz); + if (psz[0] == '/') + psz[0] = '-'; + #endif + if (psz[0] != '-') + break; + mapArgs[psz] = pszValue; + mapMultiArgs[psz].push_back(pszValue); + } +} + + +const char* wxGetTranslation(const char* pszEnglish) +{ +#ifdef GUI + // Wrapper of wxGetTranslation returning the same const char* type as was passed in + static CCriticalSection cs; + CRITICAL_BLOCK(cs) + { + // Look in cache + static map mapCache; + map::iterator mi = mapCache.find(pszEnglish); + if (mi != mapCache.end()) + return (*mi).second; + + // wxWidgets translation + wxString strTranslated = wxGetTranslation(wxString(pszEnglish, wxConvUTF8)); + + // We don't cache unknown strings because caller might be passing in a + // dynamic string and we would keep allocating memory for each variation. + if (strcmp(pszEnglish, strTranslated.utf8_str()) == 0) + return pszEnglish; + + // Add to cache, memory doesn't need to be freed. We only cache because + // we must pass back a pointer to permanently allocated memory. + char* pszCached = new char[strlen(strTranslated.utf8_str())+1]; + strcpy(pszCached, strTranslated.utf8_str()); + mapCache[pszEnglish] = pszCached; + return pszCached; + } + return NULL; +#else + return pszEnglish; +#endif +} + + +bool WildcardMatch(const char* psz, const char* mask) +{ + loop + { + switch (*mask) + { + case '\0': + return (*psz == '\0'); + case '*': + return WildcardMatch(psz, mask+1) || (*psz && WildcardMatch(psz+1, mask)); + case '?': + if (*psz == '\0') + return false; + break; + default: + if (*psz != *mask) + return false; + break; + } + psz++; + mask++; + } +} + +bool WildcardMatch(const string& str, const string& mask) +{ + return WildcardMatch(str.c_str(), mask.c_str()); +} + + + + + + + + +void FormatException(char* pszMessage, std::exception* pex, const char* pszThread) +{ +#ifdef __WXMSW__ + char pszModule[MAX_PATH]; + pszModule[0] = '\0'; + GetModuleFileNameA(NULL, pszModule, sizeof(pszModule)); +#else + const char* pszModule = "bitcoin"; +#endif + if (pex) + snprintf(pszMessage, 1000, + "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread); + else + snprintf(pszMessage, 1000, + "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread); +} + +void LogException(std::exception* pex, const char* pszThread) +{ + char pszMessage[10000]; + FormatException(pszMessage, pex, pszThread); + printf("\n%s", pszMessage); +} + +void PrintException(std::exception* pex, const char* pszThread) +{ + char pszMessage[10000]; + FormatException(pszMessage, pex, pszThread); + printf("\n\n************************\n%s\n", pszMessage); + fprintf(stderr, "\n\n************************\n%s\n", pszMessage); + strMiscWarning = pszMessage; +#ifdef GUI + if (wxTheApp && !fDaemon) + MyMessageBox(pszMessage, "Bitcoin", wxOK | wxICON_ERROR); +#endif + throw; +} + +void ThreadOneMessageBox(string strMessage) +{ + // Skip message boxes if one is already open + static bool fMessageBoxOpen; + if (fMessageBoxOpen) + return; + fMessageBoxOpen = true; + ThreadSafeMessageBox(strMessage, "Bitcoin", wxOK | wxICON_EXCLAMATION); + fMessageBoxOpen = false; +} + +void PrintExceptionContinue(std::exception* pex, const char* pszThread) +{ + char pszMessage[10000]; + FormatException(pszMessage, pex, pszThread); + printf("\n\n************************\n%s\n", pszMessage); + fprintf(stderr, "\n\n************************\n%s\n", pszMessage); + strMiscWarning = pszMessage; +#ifdef GUI + if (wxTheApp && !fDaemon) + boost::thread(boost::bind(ThreadOneMessageBox, string(pszMessage))); +#endif +} + + + + + + + + +#ifdef __WXMSW__ +typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate); + +string MyGetSpecialFolderPath(int nFolder, bool fCreate) +{ + char pszPath[MAX_PATH+100] = ""; + + // SHGetSpecialFolderPath isn't always available on old Windows versions + HMODULE hShell32 = LoadLibraryA("shell32.dll"); + if (hShell32) + { + PSHGETSPECIALFOLDERPATHA pSHGetSpecialFolderPath = + (PSHGETSPECIALFOLDERPATHA)GetProcAddress(hShell32, "SHGetSpecialFolderPathA"); + if (pSHGetSpecialFolderPath) + (*pSHGetSpecialFolderPath)(NULL, pszPath, nFolder, fCreate); + FreeModule(hShell32); + } + + // Backup option + if (pszPath[0] == '\0') + { + if (nFolder == CSIDL_STARTUP) + { + strcpy(pszPath, getenv("USERPROFILE")); + strcat(pszPath, "\\Start Menu\\Programs\\Startup"); + } + else if (nFolder == CSIDL_APPDATA) + { + strcpy(pszPath, getenv("APPDATA")); + } + } + + return pszPath; +} +#endif + +string GetDefaultDataDir() +{ + // Windows: C:\Documents and Settings\username\Application Data\Bitcoin + // Mac: ~/Library/Application Support/Bitcoin + // Unix: ~/.bitcoin +#ifdef __WXMSW__ + // Windows + return MyGetSpecialFolderPath(CSIDL_APPDATA, true) + "\\Bitcoin"; +#else + char* pszHome = getenv("HOME"); + if (pszHome == NULL || strlen(pszHome) == 0) + pszHome = (char*)"/"; + string strHome = pszHome; + if (strHome[strHome.size()-1] != '/') + strHome += '/'; +#ifdef __WXMAC_OSX__ + // Mac + strHome += "Library/Application Support/"; + filesystem::create_directory(strHome.c_str()); + return strHome + "Bitcoin"; +#else + // Unix + return strHome + ".bitcoin"; +#endif +#endif +} + +void GetDataDir(char* pszDir) +{ + // pszDir must be at least MAX_PATH length. + int nVariation; + if (pszSetDataDir[0] != 0) + { + strlcpy(pszDir, pszSetDataDir, MAX_PATH); + nVariation = 0; + } + else + { + // This can be called during exceptions by printf, so we cache the + // value so we don't have to do memory allocations after that. + static char pszCachedDir[MAX_PATH]; + if (pszCachedDir[0] == 0) + strlcpy(pszCachedDir, GetDefaultDataDir().c_str(), sizeof(pszCachedDir)); + strlcpy(pszDir, pszCachedDir, MAX_PATH); + nVariation = 1; + } + if (fTestNet) + { + char* p = pszDir + strlen(pszDir); + if (p > pszDir && p[-1] != '/' && p[-1] != '\\') + *p++ = '/'; + strcpy(p, "testnet"); + nVariation += 2; + } + static bool pfMkdir[4]; + if (!pfMkdir[nVariation]) + { + pfMkdir[nVariation] = true; + boost::filesystem::create_directory(pszDir); + } +} + +string GetDataDir() +{ + char pszDir[MAX_PATH]; + GetDataDir(pszDir); + return pszDir; +} + +string GetConfigFile() +{ + namespace fs = boost::filesystem; + fs::path pathConfig(GetArg("-conf", "bitcoin.conf")); + if (!pathConfig.is_complete()) + pathConfig = fs::path(GetDataDir()) / pathConfig; + return pathConfig.string(); +} + +void ReadConfigFile(map& mapSettingsRet, + map >& mapMultiSettingsRet) +{ + namespace fs = boost::filesystem; + namespace pod = boost::program_options::detail; + + fs::ifstream streamConfig(GetConfigFile()); + if (!streamConfig.good()) + return; + + set setOptions; + setOptions.insert("*"); + + for (pod::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) + { + // Don't overwrite existing settings so command line settings override bitcoin.conf + string strKey = string("-") + it->string_key; + if (mapSettingsRet.count(strKey) == 0) + mapSettingsRet[strKey] = it->value[0]; + mapMultiSettingsRet[strKey].push_back(it->value[0]); + } +} + +string GetPidFile() +{ + namespace fs = boost::filesystem; + fs::path pathConfig(GetArg("-pid", "bitcoind.pid")); + if (!pathConfig.is_complete()) + pathConfig = fs::path(GetDataDir()) / pathConfig; + return pathConfig.string(); +} + +void CreatePidFile(string pidFile, pid_t pid) +{ + FILE* file; + if (file = fopen(pidFile.c_str(), "w")) + { + fprintf(file, "%d\n", pid); + fclose(file); + } +} + +int GetFilesize(FILE* file) +{ + int nSavePos = ftell(file); + int nFilesize = -1; + if (fseek(file, 0, SEEK_END) == 0) + nFilesize = ftell(file); + fseek(file, nSavePos, SEEK_SET); + return nFilesize; +} + +void ShrinkDebugFile() +{ + // Scroll debug.log if it's getting too big + string strFile = GetDataDir() + "/debug.log"; + FILE* file = fopen(strFile.c_str(), "r"); + if (file && GetFilesize(file) > 10 * 1000000) + { + // Restart the file with some of the end + char pch[200000]; + fseek(file, -sizeof(pch), SEEK_END); + int nBytes = fread(pch, 1, sizeof(pch), file); + fclose(file); + if (file = fopen(strFile.c_str(), "w")) + { + fwrite(pch, 1, nBytes, file); + fclose(file); + } + } +} + + + + + + + + +// +// "Never go to sea with two chronometers; take one or three." +// Our three time sources are: +// - System clock +// - Median of other nodes's clocks +// - The user (asking the user to fix the system clock if the first two disagree) +// +int64 GetTime() +{ + return time(NULL); +} + +static int64 nTimeOffset = 0; + +int64 GetAdjustedTime() +{ + return GetTime() + nTimeOffset; +} + +void AddTimeData(unsigned int ip, int64 nTime) +{ + int64 nOffsetSample = nTime - GetTime(); + + // Ignore duplicates + static set setKnown; + if (!setKnown.insert(ip).second) + return; + + // Add data + static vector vTimeOffsets; + if (vTimeOffsets.empty()) + vTimeOffsets.push_back(0); + vTimeOffsets.push_back(nOffsetSample); + printf("Added time data, samples %d, offset %+"PRI64d" (%+"PRI64d" minutes)\n", vTimeOffsets.size(), vTimeOffsets.back(), vTimeOffsets.back()/60); + if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) + { + sort(vTimeOffsets.begin(), vTimeOffsets.end()); + int64 nMedian = vTimeOffsets[vTimeOffsets.size()/2]; + // Only let other nodes change our time by so much + if (abs64(nMedian) < 70 * 60) + { + nTimeOffset = nMedian; + } + else + { + nTimeOffset = 0; + + static bool fDone; + if (!fDone) + { + // If nobody has a time different than ours but within 5 minutes of ours, give a warning + bool fMatch = false; + BOOST_FOREACH(int64 nOffset, vTimeOffsets) + if (nOffset != 0 && abs64(nOffset) < 5 * 60) + fMatch = true; + + if (!fMatch) + { + fDone = true; + string strMessage = _("Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly."); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + boost::thread(boost::bind(ThreadSafeMessageBox, strMessage+" ", string("Bitcoin"), wxOK | wxICON_EXCLAMATION, (wxWindow*)NULL, -1, -1)); + } + } + } + BOOST_FOREACH(int64 n, vTimeOffsets) + printf("%+"PRI64d" ", n); + printf("| nTimeOffset = %+"PRI64d" (%+"PRI64d" minutes)\n", nTimeOffset, nTimeOffset/60); + } +} + + + + + + + + + +string FormatVersion(int nVersion) +{ + if (nVersion%100 == 0) + return strprintf("%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100); + else + return strprintf("%d.%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100, nVersion%100); +} + +string FormatFullVersion() +{ + string s = FormatVersion(VERSION) + pszSubVer; + if (VERSION_IS_BETA) + s += _("-beta"); + return s; +} + + + + + diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000000..32a98e6264 --- /dev/null +++ b/src/util.h @@ -0,0 +1,680 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UTIL_H +#define BITCOIN_UTIL_H + +#include "uint256.h" + +#ifndef __WXMSW__ +#include +#include +#include +#endif +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false) ; else for +#endif +#ifndef _MSC_VER +#define __forceinline inline +#endif + +#define loop for (;;) +#define BEGIN(a) ((char*)&(a)) +#define END(a) ((char*)&((&(a))[1])) +#define UBEGIN(a) ((unsigned char*)&(a)) +#define UEND(a) ((unsigned char*)&((&(a))[1])) +#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) +#define printf OutputDebugStringF + +#ifdef snprintf +#undef snprintf +#endif +#define snprintf my_snprintf + +#ifndef PRI64d +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MSVCRT__) +#define PRI64d "I64d" +#define PRI64u "I64u" +#define PRI64x "I64x" +#else +#define PRI64d "lld" +#define PRI64u "llu" +#define PRI64x "llx" +#endif +#endif + +// This is needed because the foreach macro can't get over the comma in pair +#define PAIRTYPE(t1, t2) std::pair + +// Used to bypass the rule against non-const reference to temporary +// where it makes sense with wrappers such as CFlatData or CTxDB +template +inline T& REF(const T& val) +{ + return (T&)val; +} + +// Align by increasing pointer, must have extra space at end of buffer +template +T* alignup(T* p) +{ + union + { + T* ptr; + size_t n; + } u; + u.ptr = p; + u.n = (u.n + (nBytes-1)) & ~(nBytes-1); + return u.ptr; +} + +#ifdef __WXMSW__ +#define MSG_NOSIGNAL 0 +#define MSG_DONTWAIT 0 +#ifndef UINT64_MAX +#define UINT64_MAX _UI64_MAX +#define INT64_MAX _I64_MAX +#define INT64_MIN _I64_MIN +#endif +#ifndef S_IRUSR +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#endif +#define unlink _unlink +typedef int socklen_t; +#else +#define WSAGetLastError() errno +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEMSGSIZE EMSGSIZE +#define WSAEINTR EINTR +#define WSAEINPROGRESS EINPROGRESS +#define WSAEADDRINUSE EADDRINUSE +#define WSAENOTSOCK EBADF +#define INVALID_SOCKET (SOCKET)(~0) +#define SOCKET_ERROR -1 +typedef u_int SOCKET; +#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) +#define strlwr(psz) to_lower(psz) +#define _strlwr(psz) to_lower(psz) +#define MAX_PATH 1024 +#define Beep(n1,n2) (0) +inline void Sleep(int64 n) +{ + boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(n)); +} +#endif + +inline int myclosesocket(SOCKET& hSocket) +{ + if (hSocket == INVALID_SOCKET) + return WSAENOTSOCK; +#ifdef __WXMSW__ + int ret = closesocket(hSocket); +#else + int ret = close(hSocket); +#endif + hSocket = INVALID_SOCKET; + return ret; +} +#define closesocket(s) myclosesocket(s) + +#ifndef GUI +inline const char* _(const char* psz) +{ + return psz; +} +#endif + + + + + + + + + + +extern std::map mapArgs; +extern std::map > mapMultiArgs; +extern bool fDebug; +extern bool fPrintToConsole; +extern bool fPrintToDebugger; +extern char pszSetDataDir[MAX_PATH]; +extern bool fRequestShutdown; +extern bool fShutdown; +extern bool fDaemon; +extern bool fServer; +extern bool fCommandLine; +extern std::string strMiscWarning; +extern bool fTestNet; +extern bool fNoListen; +extern bool fLogTimestamps; + +void RandAddSeed(); +void RandAddSeedPerfmon(); +int OutputDebugStringF(const char* pszFormat, ...); +int my_snprintf(char* buffer, size_t limit, const char* format, ...); +std::string strprintf(const char* format, ...); +bool error(const char* format, ...); +void LogException(std::exception* pex, const char* pszThread); +void PrintException(std::exception* pex, const char* pszThread); +void PrintExceptionContinue(std::exception* pex, const char* pszThread); +void ParseString(const std::string& str, char c, std::vector& v); +std::string FormatMoney(int64 n, bool fPlus=false); +bool ParseMoney(const std::string& str, int64& nRet); +bool ParseMoney(const char* pszIn, int64& nRet); +std::vector ParseHex(const char* psz); +std::vector ParseHex(const std::string& str); +void ParseParameters(int argc, char* argv[]); +const char* wxGetTranslation(const char* psz); +bool WildcardMatch(const char* psz, const char* mask); +bool WildcardMatch(const std::string& str, const std::string& mask); +int GetFilesize(FILE* file); +void GetDataDir(char* pszDirRet); +std::string GetConfigFile(); +std::string GetPidFile(); +void CreatePidFile(std::string pidFile, pid_t pid); +void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); +#ifdef __WXMSW__ +std::string MyGetSpecialFolderPath(int nFolder, bool fCreate); +#endif +std::string GetDefaultDataDir(); +std::string GetDataDir(); +void ShrinkDebugFile(); +int GetRandInt(int nMax); +uint64 GetRand(uint64 nMax); +int64 GetTime(); +int64 GetAdjustedTime(); +void AddTimeData(unsigned int ip, int64 nTime); +std::string FormatFullVersion(); + + + + + + + + + + + + + +// Wrapper to automatically initialize critical sections +class CCriticalSection +{ +#ifdef __WXMSW__ +protected: + CRITICAL_SECTION cs; +public: + explicit CCriticalSection() { InitializeCriticalSection(&cs); } + ~CCriticalSection() { DeleteCriticalSection(&cs); } + void Enter() { EnterCriticalSection(&cs); } + void Leave() { LeaveCriticalSection(&cs); } + bool TryEnter() { return TryEnterCriticalSection(&cs); } +#else +protected: + boost::interprocess::interprocess_recursive_mutex mutex; +public: + explicit CCriticalSection() { } + ~CCriticalSection() { } + void Enter() { mutex.lock(); } + void Leave() { mutex.unlock(); } + bool TryEnter() { return mutex.try_lock(); } +#endif +public: + const char* pszFile; + int nLine; +}; + +// Automatically leave critical section when leaving block, needed for exception safety +class CCriticalBlock +{ +protected: + CCriticalSection* pcs; +public: + CCriticalBlock(CCriticalSection& csIn) { pcs = &csIn; pcs->Enter(); } + ~CCriticalBlock() { pcs->Leave(); } +}; + +// WARNING: This will catch continue and break! +// break is caught with an assertion, but there's no way to detect continue. +// I'd rather be careful than suffer the other more error prone syntax. +// The compiler will optimise away all this loop junk. +#define CRITICAL_BLOCK(cs) \ + for (bool fcriticalblockonce=true; fcriticalblockonce; assert(("break caught by CRITICAL_BLOCK!", !fcriticalblockonce)), fcriticalblockonce=false) \ + for (CCriticalBlock criticalblock(cs); fcriticalblockonce && (cs.pszFile=__FILE__, cs.nLine=__LINE__, true); fcriticalblockonce=false, cs.pszFile=NULL, cs.nLine=0) + +class CTryCriticalBlock +{ +protected: + CCriticalSection* pcs; +public: + CTryCriticalBlock(CCriticalSection& csIn) { pcs = (csIn.TryEnter() ? &csIn : NULL); } + ~CTryCriticalBlock() { if (pcs) pcs->Leave(); } + bool Entered() { return pcs != NULL; } +}; + +#define TRY_CRITICAL_BLOCK(cs) \ + for (bool fcriticalblockonce=true; fcriticalblockonce; assert(("break caught by TRY_CRITICAL_BLOCK!", !fcriticalblockonce)), fcriticalblockonce=false) \ + for (CTryCriticalBlock criticalblock(cs); fcriticalblockonce && (fcriticalblockonce = criticalblock.Entered()) && (cs.pszFile=__FILE__, cs.nLine=__LINE__, true); fcriticalblockonce=false, cs.pszFile=NULL, cs.nLine=0) + + + + + + + + + + +inline std::string i64tostr(int64 n) +{ + return strprintf("%"PRI64d, n); +} + +inline std::string itostr(int n) +{ + return strprintf("%d", n); +} + +inline int64 atoi64(const char* psz) +{ +#ifdef _MSC_VER + return _atoi64(psz); +#else + return strtoll(psz, NULL, 10); +#endif +} + +inline int64 atoi64(const std::string& str) +{ +#ifdef _MSC_VER + return _atoi64(str.c_str()); +#else + return strtoll(str.c_str(), NULL, 10); +#endif +} + +inline int atoi(const std::string& str) +{ + return atoi(str.c_str()); +} + +inline int roundint(double d) +{ + return (int)(d > 0 ? d + 0.5 : d - 0.5); +} + +inline int64 roundint64(double d) +{ + return (int64)(d > 0 ? d + 0.5 : d - 0.5); +} + +inline int64 abs64(int64 n) +{ + return (n >= 0 ? n : -n); +} + +template +std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) +{ + if (itbegin == itend) + return ""; + const unsigned char* pbegin = (const unsigned char*)&itbegin[0]; + const unsigned char* pend = pbegin + (itend - itbegin) * sizeof(itbegin[0]); + std::string str; + str.reserve((pend-pbegin) * (fSpaces ? 3 : 2)); + for (const unsigned char* p = pbegin; p != pend; p++) + str += strprintf((fSpaces && p != pend-1 ? "%02x " : "%02x"), *p); + return str; +} + +inline std::string HexStr(const std::vector& vch, bool fSpaces=false) +{ + return HexStr(vch.begin(), vch.end(), fSpaces); +} + +template +std::string HexNumStr(const T itbegin, const T itend, bool f0x=true) +{ + if (itbegin == itend) + return ""; + const unsigned char* pbegin = (const unsigned char*)&itbegin[0]; + const unsigned char* pend = pbegin + (itend - itbegin) * sizeof(itbegin[0]); + std::string str = (f0x ? "0x" : ""); + str.reserve(str.size() + (pend-pbegin) * 2); + for (const unsigned char* p = pend-1; p >= pbegin; p--) + str += strprintf("%02x", *p); + return str; +} + +inline std::string HexNumStr(const std::vector& vch, bool f0x=true) +{ + return HexNumStr(vch.begin(), vch.end(), f0x); +} + +template +void PrintHex(const T pbegin, const T pend, const char* pszFormat="%s", bool fSpaces=true) +{ + printf(pszFormat, HexStr(pbegin, pend, fSpaces).c_str()); +} + +inline void PrintHex(const std::vector& vch, const char* pszFormat="%s", bool fSpaces=true) +{ + printf(pszFormat, HexStr(vch, fSpaces).c_str()); +} + +inline int64 GetPerformanceCounter() +{ + int64 nCounter = 0; +#ifdef __WXMSW__ + QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); +#else + timeval t; + gettimeofday(&t, NULL); + nCounter = t.tv_sec * 1000000 + t.tv_usec; +#endif + return nCounter; +} + +inline int64 GetTimeMillis() +{ + return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - + boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); +} + +inline std::string DateTimeStrFormat(const char* pszFormat, int64 nTime) +{ + time_t n = nTime; + struct tm* ptmTime = gmtime(&n); + char pszTime[200]; + strftime(pszTime, sizeof(pszTime), pszFormat, ptmTime); + return pszTime; +} + +template +void skipspaces(T& it) +{ + while (isspace(*it)) + ++it; +} + +inline bool IsSwitchChar(char c) +{ +#ifdef __WXMSW__ + return c == '-' || c == '/'; +#else + return c == '-'; +#endif +} + +inline std::string GetArg(const std::string& strArg, const std::string& strDefault) +{ + if (mapArgs.count(strArg)) + return mapArgs[strArg]; + return strDefault; +} + +inline int64 GetArg(const std::string& strArg, int64 nDefault) +{ + if (mapArgs.count(strArg)) + return atoi64(mapArgs[strArg]); + return nDefault; +} + +inline bool GetBoolArg(const std::string& strArg) +{ + if (mapArgs.count(strArg)) + { + if (mapArgs[strArg].empty()) + return true; + return (atoi(mapArgs[strArg]) != 0); + } + return false; +} + + + + + + + + + + +inline void heapchk() +{ +#ifdef __WXMSW__ + /// for debugging + //if (_heapchk() != _HEAPOK) + // DebugBreak(); +#endif +} + +// Randomize the stack to help protect against buffer overrun exploits +#define IMPLEMENT_RANDOMIZE_STACK(ThreadFn) \ + { \ + static char nLoops; \ + if (nLoops <= 0) \ + nLoops = GetRand(20) + 1; \ + if (nLoops-- > 1) \ + { \ + ThreadFn; \ + return; \ + } \ + } + +#define CATCH_PRINT_EXCEPTION(pszFn) \ + catch (std::exception& e) { \ + PrintException(&e, (pszFn)); \ + } catch (...) { \ + PrintException(NULL, (pszFn)); \ + } + + + + + + + + + + +template +inline uint256 Hash(const T1 pbegin, const T1 pend) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +inline uint256 Hash(const T1 p1begin, const T1 p1end, + const T2 p2begin, const T2 p2end) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); + SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); + SHA256_Final((unsigned char*)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +inline uint256 Hash(const T1 p1begin, const T1 p1end, + const T2 p2begin, const T2 p2end, + const T3 p3begin, const T3 p3end) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); + SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); + SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0])); + SHA256_Final((unsigned char*)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=VERSION) +{ + // Most of the time is spent allocating and deallocating CDataStream's + // buffer. If this ever needs to be optimized further, make a CStaticStream + // class with its buffer on the stack. + CDataStream ss(nType, nVersion); + ss.reserve(10000); + ss << obj; + return Hash(ss.begin(), ss.end()); +} + +inline uint160 Hash160(const std::vector& vch) +{ + uint256 hash1; + SHA256(&vch[0], vch.size(), (unsigned char*)&hash1); + uint160 hash2; + RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + + + + + + + + + + + +// Note: It turns out we might have been able to use boost::thread +// by using TerminateThread(boost::thread.native_handle(), 0); +#ifdef __WXMSW__ +typedef HANDLE pthread_t; + +inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) +{ + DWORD nUnused = 0; + HANDLE hthread = + CreateThread( + NULL, // default security + 0, // inherit stack size from parent + (LPTHREAD_START_ROUTINE)pfn, // function pointer + parg, // argument + 0, // creation option, start immediately + &nUnused); // thread identifier + if (hthread == NULL) + { + printf("Error: CreateThread() returned %d\n", GetLastError()); + return (pthread_t)0; + } + if (!fWantHandle) + { + CloseHandle(hthread); + return (pthread_t)-1; + } + return hthread; +} + +inline void SetThreadPriority(int nPriority) +{ + SetThreadPriority(GetCurrentThread(), nPriority); +} +#else +inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) +{ + pthread_t hthread = 0; + int ret = pthread_create(&hthread, NULL, (void*(*)(void*))pfn, parg); + if (ret != 0) + { + printf("Error: pthread_create() returned %d\n", ret); + return (pthread_t)0; + } + if (!fWantHandle) + return (pthread_t)-1; + return hthread; +} + +#define THREAD_PRIORITY_LOWEST PRIO_MAX +#define THREAD_PRIORITY_BELOW_NORMAL 2 +#define THREAD_PRIORITY_NORMAL 0 +#define THREAD_PRIORITY_ABOVE_NORMAL 0 + +inline void SetThreadPriority(int nPriority) +{ + // It's unclear if it's even possible to change thread priorities on Linux, + // but we really and truly need it for the generation threads. +#ifdef PRIO_THREAD + setpriority(PRIO_THREAD, 0, nPriority); +#else + setpriority(PRIO_PROCESS, 0, nPriority); +#endif +} + +inline bool TerminateThread(pthread_t hthread, unsigned int nExitCode) +{ + return (pthread_cancel(hthread) == 0); +} + +inline void ExitThread(unsigned int nExitCode) +{ + pthread_exit((void*)nExitCode); +} +#endif + + + + + +inline bool AffinityBugWorkaround(void(*pfn)(void*)) +{ +#ifdef __WXMSW__ + // Sometimes after a few hours affinity gets stuck on one processor + DWORD dwProcessAffinityMask = -1; + DWORD dwSystemAffinityMask = -1; + GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask); + DWORD dwPrev1 = SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinityMask); + DWORD dwPrev2 = SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinityMask); + if (dwPrev2 != dwProcessAffinityMask) + { + printf("AffinityBugWorkaround() : SetThreadAffinityMask=%d, ProcessAffinityMask=%d, restarting thread\n", dwPrev2, dwProcessAffinityMask); + if (!CreateThread(pfn, NULL)) + printf("Error: CreateThread() failed\n"); + return true; + } +#endif + return false; +} + +#endif -- cgit v1.2.3 From 0304f4a759eba07e6d0f3e210094867d8da2fb90 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 11 Jun 2011 23:12:39 +0200 Subject: add build instructions to doc --- README.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.rst b/README.rst index f24335b5c3..df73f069c9 100644 --- a/README.rst +++ b/README.rst @@ -37,3 +37,25 @@ This has to be done: - Internationalization (convert WX language files) - Build on Windows + +Build instructions +=================== + +First, make sure that the required packages for Qt4 development of your +distribution are installed, for Debian and Ubuntu these are: + +:: + + apt-get install qt4-qmake libqt4-dev + +then execute the following: + +:: + + qmake + make + +Alternatively, install Qt Creator and open the `bitcoin.pro` file. + +An executable named `bitcoin` will be built. + -- cgit v1.2.3 From 37f793c63169d37031fbaa894fb61c76ef9adc0e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 12 Jun 2011 11:16:08 +0200 Subject: use stylized icon by bitboy --- src/qt/res/icons/bitcoin.png | Bin 1086 -> 645 bytes src/qt/res/icons/toolbar.png | Bin 968 -> 625 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/qt/res/icons/bitcoin.png b/src/qt/res/icons/bitcoin.png index 09520aca23..cee616e354 100644 Binary files a/src/qt/res/icons/bitcoin.png and b/src/qt/res/icons/bitcoin.png differ diff --git a/src/qt/res/icons/toolbar.png b/src/qt/res/icons/toolbar.png index f2dcb20636..e33d0f4bd9 100644 Binary files a/src/qt/res/icons/toolbar.png and b/src/qt/res/icons/toolbar.png differ -- cgit v1.2.3 From c428d9e76a03f066a31f605cfae28af8548b367e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 12 Jun 2011 11:22:44 +0200 Subject: remove wallet updating debug output --- src/qt/transactiontablemodel.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 8fe1839930..ddfebff3fe 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -51,8 +51,9 @@ struct TransactionTablePriv void refreshWallet() { +#ifdef WALLET_UPDATE_DEBUG qDebug() << "refreshWallet"; - +#endif /* Query entire wallet from core. */ cachedWallet.clear(); @@ -72,8 +73,9 @@ struct TransactionTablePriv { /* Walk through updated transactions, update model as needed. */ +#ifdef WALLET_UPDATE_DEBUG qDebug() << "updateWallet"; - +#endif /* Sort update list, and iterate through it in reverse, so that model updates can be emitted from end to beginning (so that earlier updates will not influence the indices of latter ones). @@ -103,8 +105,10 @@ struct TransactionTablePriv inModel = true; } +#ifdef WALLET_UPDATE_DEBUG qDebug() << " " << QString::fromStdString(hash.ToString()) << inWallet << " " << inModel << lowerIndex << "-" << upperIndex; +#endif if(inWallet && !inModel) { -- cgit v1.2.3 From 0424613ba24de94b58f6fa7bf1627fd4e2807208 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 12 Jun 2011 11:27:15 +0200 Subject: add the bitcoin (MIT?) license --- COPYING | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 COPYING diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000..ab042014e8 --- /dev/null +++ b/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2009-2011 Bitcoin Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. -- cgit v1.2.3 From 18cf214528d14692941311be154214cab1772ed5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 12 Jun 2011 12:27:01 +0200 Subject: update bitcoin core to git ce148944c776ae8e91cc058f44ddce356c7cebc9 --- src/base58.h | 2 ++ src/bignum.h | 3 ++ src/db.cpp | 11 ++++---- src/init.cpp | 9 +++++- src/main.h | 5 ++-- src/net.cpp | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/net.h | 3 +- src/util.h | 2 ++ 8 files changed, 111 insertions(+), 15 deletions(-) diff --git a/src/base58.h b/src/base58.h index 580bd3fc63..c2729d4770 100644 --- a/src/base58.h +++ b/src/base58.h @@ -38,6 +38,8 @@ inline std::string EncodeBase58(const unsigned char* pbegin, const unsigned char // Convert bignum to std::string std::string str; + // Expected size increase from base58 conversion is approximately 137% + // use 138% to be safe str.reserve((pend - pbegin) * 138 / 100 + 1); CBigNum dv; CBigNum rem; diff --git a/src/bignum.h b/src/bignum.h index 5b4c78e7fa..5eaa4028b7 100644 --- a/src/bignum.h +++ b/src/bignum.h @@ -228,10 +228,13 @@ public: { std::vector vch2(vch.size() + 4); unsigned int nSize = vch.size(); + // BIGNUM's byte stream format expects 4 bytes of + // big endian size data info at the front vch2[0] = (nSize >> 24) & 0xff; vch2[1] = (nSize >> 16) & 0xff; vch2[2] = (nSize >> 8) & 0xff; vch2[3] = (nSize >> 0) & 0xff; + // swap data to big endian reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4); BN_mpi2bn(&vch2[0], vch2.size(), this); } diff --git a/src/db.cpp b/src/db.cpp index 52c0f5b4c3..c2c239db2f 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -845,12 +845,11 @@ bool LoadWallet(bool& fFirstRunRet) { // Create new keyUser and set as default key RandAddSeedPerfmon(); - keyUser.MakeNewKey(); - if (!AddKey(keyUser)) - return false; - if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "")) - return false; - CWalletDB().WriteDefaultKey(keyUser.GetPubKey()); + + CWalletDB walletdb; + vchDefaultKey = GetKeyFromKeyPool(); + walletdb.WriteDefaultKey(vchDefaultKey); + walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); } CreateThread(ThreadFlushWalletDB, NULL); diff --git a/src/init.cpp b/src/init.cpp index f7a1ce9ef1..51fdf11b69 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -139,7 +139,6 @@ bool AppInit2(int argc, char* argv[]) if (mapArgs.count("-?") || mapArgs.count("--help")) { - string beta = VERSION_IS_BETA ? _(" beta") : ""; string strUsage = string() + _("Bitcoin version") + " " + FormatFullVersion() + "\n\n" + _("Usage:") + "\t\t\t\t\t\t\t\t\t\t\n" + @@ -154,6 +153,7 @@ bool AppInit2(int argc, char* argv[]) " -gen=0 \t\t " + _("Don't generate coins\n") + " -min \t\t " + _("Start minimized\n") + " -datadir= \t\t " + _("Specify data directory\n") + + " -timeout= \t " + _("Specify connection timeout (in milliseconds)\n") + " -proxy= \t " + _("Connect through socks4 proxy\n") + " -dns \t " + _("Allow DNS lookups for addnode and connect\n") + " -addnode= \t " + _("Add a node to connect to\n") + @@ -418,6 +418,13 @@ bool AppInit2(int argc, char* argv[]) return false; } + if (mapArgs.count("-timeout")) + { + int nNewTimeout = GetArg("-timeout", 5000); + if (nNewTimeout > 0 && nNewTimeout < 600000) + nConnectTimeout = nNewTimeout; + } + if (mapArgs.count("-printblock")) { string strMatch = mapArgs["-printblock"]; diff --git a/src/main.h b/src/main.h index 8d9b39f718..436ffbecbd 100644 --- a/src/main.h +++ b/src/main.h @@ -29,8 +29,8 @@ static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; static const int64 COIN = 100000000; static const int64 CENT = 1000000; -static const int64 MIN_TX_FEE = CENT; -static const int64 MIN_RELAY_TX_FEE = 50000; +static const int64 MIN_TX_FEE = 50000; +static const int64 MIN_RELAY_TX_FEE = 10000; static const int64 MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } static const int COINBASE_MATURITY = 100; @@ -79,7 +79,6 @@ extern int fUseUPnP; - bool CheckDiskSpace(uint64 nAdditionalBytes=0); FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); FILE* AppendBlockFile(unsigned int& nFileRet); diff --git a/src/net.cpp b/src/net.cpp index 39360a334c..ca6380fc76 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -51,6 +51,7 @@ map mapAlreadyAskedFor; // Settings int fUseProxy = false; +int nConnectTimeout = 5000; CAddress addrProxy("127.0.0.1",9050); @@ -76,7 +77,7 @@ void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) -bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) +bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet, int nTimeout) { hSocketRet = INVALID_SOCKET; @@ -91,7 +92,86 @@ bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) bool fProxy = (fUseProxy && addrConnect.IsRoutable()); struct sockaddr_in sockaddr = (fProxy ? addrProxy.GetSockAddr() : addrConnect.GetSockAddr()); +#ifdef __WXMSW__ + u_long fNonblock = 1; + if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR) +#else + int fFlags = fcntl(hSocket, F_GETFL, 0); + if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == -1) +#endif + { + closesocket(hSocket); + return false; + } + + if (connect(hSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) + { + // WSAEINVAL is here because some legacy version of winsock uses it + if (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINVAL) + { + struct timeval timeout; + timeout.tv_sec = nTimeout / 1000; + timeout.tv_usec = (nTimeout % 1000) * 1000; + + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(hSocket, &fdset); + int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout); + if (nRet == 0) + { + printf("connection timeout\n"); + closesocket(hSocket); + return false; + } + if (nRet == SOCKET_ERROR) + { + printf("select() for connection failed: %i\n",WSAGetLastError()); + closesocket(hSocket); + return false; + } + socklen_t nRetSize = sizeof(nRet); +#ifdef __WXMSW__ + if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (char*)(&nRet), &nRetSize) == SOCKET_ERROR) +#else + if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR) +#endif + { + printf("getsockopt() for connection failed: %i\n",WSAGetLastError()); + closesocket(hSocket); + return false; + } + if (nRet != 0) + { + printf("connect() failed after select(): %i\n",nRet); + closesocket(hSocket); + return false; + } + } +#ifdef __WXMSW__ + else if (WSAGetLastError() != WSAEISCONN) +#else + else +#endif + { + printf("connect() failed: %s\n",WSAGetLastError()); + closesocket(hSocket); + return false; + } + } + + /* + this isn't even strictly necessary + CNode::ConnectNode immediately turns the socket back to non-blocking + but we'll turn it back to blocking just in case + */ +#ifdef __WXMSW__ + fNonblock = 0; + if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR) +#else + fFlags = fcntl(hSocket, F_GETFL, 0); + if (fcntl(hSocket, F_SETFL, fFlags & !O_NONBLOCK) == SOCKET_ERROR) +#endif { closesocket(hSocket); return false; @@ -756,9 +836,12 @@ void ThreadSocketHandler2(void* parg) if (nSelect == SOCKET_ERROR) { int nErr = WSAGetLastError(); - printf("socket select error %d\n", nErr); - for (int i = 0; i <= hSocketMax; i++) - FD_SET(i, &fdsetRecv); + if (hSocketMax > -1) + { + printf("socket select error %d\n", nErr); + for (int i = 0; i <= hSocketMax; i++) + FD_SET(i, &fdsetRecv); + } FD_ZERO(&fdsetSend); FD_ZERO(&fdsetError); Sleep(timeout.tv_usec/1000); diff --git a/src/net.h b/src/net.h index d1ded87232..8a55856eed 100644 --- a/src/net.h +++ b/src/net.h @@ -19,6 +19,7 @@ class CRequestTracker; class CNode; class CBlockIndex; extern int nBestHeight; +extern int nConnectTimeout; @@ -32,7 +33,7 @@ enum -bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet); +bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet, int nTimeout=nConnectTimeout); bool Lookup(const char *pszName, std::vector& vaddr, int nServices, int nMaxSolutions, bool fAllowLookup = false, int portDefault = 0, bool fAllowPort = false); bool Lookup(const char *pszName, CAddress& addr, int nServices, bool fAllowLookup = false, int portDefault = 0, bool fAllowPort = false); bool GetMyExternalIP(unsigned int& ipRet); diff --git a/src/util.h b/src/util.h index 32a98e6264..25e1f77fd7 100644 --- a/src/util.h +++ b/src/util.h @@ -105,6 +105,8 @@ T* alignup(T* p) typedef int socklen_t; #else #define WSAGetLastError() errno +#define WSAEINVAL EINVAL +#define WSAEALREADY EALREADY #define WSAEWOULDBLOCK EWOULDBLOCK #define WSAEMSGSIZE EMSGSIZE #define WSAEINTR EINTR -- cgit v1.2.3 From ab2fe68fd8aa8137d1dc304a900ea811eac99af5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 12 Jun 2011 14:32:36 +0200 Subject: prepare internationalization; rename project to bitcoin-qt --- README.rst | 7 ++-- bitcoin-qt.pro | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bitcoin.pro | 107 ------------------------------------------------------- 3 files changed, 114 insertions(+), 110 deletions(-) create mode 100644 bitcoin-qt.pro delete mode 100644 bitcoin.pro diff --git a/README.rst b/README.rst index df73f069c9..2ffc4a92b8 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,8 @@ Bitcoin-qt: Qt4 based GUI replacement for Bitcoin ================================================= **Warning** **Warning** **Warning** -Pre-alpha stuff! Use on testnet only! + +Pre-alpha stuff! I'm using this client myself on the production network, and I haven't noticed any glitches, but remember: always backup your wallet! Testing on the testnet is recommended. This has been implemented: @@ -55,7 +56,7 @@ then execute the following: qmake make -Alternatively, install Qt Creator and open the `bitcoin.pro` file. +Alternatively, install Qt Creator and open the `bitcoin-qt.pro` file. -An executable named `bitcoin` will be built. +An executable named `bitcoin-qt` will be built. diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro new file mode 100644 index 0000000000..ab5ddadd14 --- /dev/null +++ b/bitcoin-qt.pro @@ -0,0 +1,110 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += src src/json src/cryptopp src/qt +unix:LIBS += -lssl -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx +macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 + +# disable quite some warnings becuase bitcoin core "sins" a lot +QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wno-invalid-offsetof -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare -Wno-char-subscripts -Wno-unused-value -Wno-sequence-point -Wno-parentheses -Wno-unknown-pragmas -Wno-switch + +# WINDOWS defines, -DSSL, look at build system + +# Input +DEPENDPATH += src/qt src src/cryptopp src json/include +HEADERS += src/qt/bitcoingui.h \ + src/qt/transactiontablemodel.h \ + src/qt/addresstablemodel.h \ + src/qt/optionsdialog.h \ + src/qt/sendcoinsdialog.h \ + src/qt/addressbookdialog.h \ + src/qt/aboutdialog.h \ + src/qt/editaddressdialog.h \ + src/qt/bitcoinaddressvalidator.h \ + src/base58.h \ + src/bignum.h \ + src/util.h \ + src/uint256.h \ + src/serialize.h \ + src/cryptopp/stdcpp.h \ + src/cryptopp/smartptr.h \ + src/cryptopp/simple.h \ + src/cryptopp/sha.h \ + src/cryptopp/secblock.h \ + src/cryptopp/pch.h \ + src/cryptopp/misc.h \ + src/cryptopp/iterhash.h \ + src/cryptopp/cryptlib.h \ + src/cryptopp/cpu.h \ + src/cryptopp/config.h \ + src/strlcpy.h \ + src/main.h \ + src/net.h \ + src/key.h \ + src/db.h \ + src/script.h \ + src/noui.h \ + src/init.h \ + src/headers.h \ + src/irc.h \ + src/json/json_spirit_writer_template.h \ + src/json/json_spirit_writer.h \ + src/json/json_spirit_value.h \ + src/json/json_spirit_utils.h \ + src/json/json_spirit_stream_reader.h \ + src/json/json_spirit_reader_template.h \ + src/json/json_spirit_reader.h \ + src/json/json_spirit_error_position.h \ + src/json/json_spirit.h \ + src/rpc.h \ + src/qt/clientmodel.h \ + src/qt/guiutil.h \ + src/qt/transactionrecord.h \ + src/qt/guiconstants.h \ + src/qt/optionsmodel.h \ + src/qt/monitoreddatamapper.h \ + src/externui.h \ + src/qt/transactiondesc.h \ + src/qt/transactiondescdialog.h +SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ + src/qt/transactiontablemodel.cpp \ + src/qt/addresstablemodel.cpp \ + src/qt/optionsdialog.cpp \ + src/qt/sendcoinsdialog.cpp \ + src/qt/addressbookdialog.cpp \ + src/qt/aboutdialog.cpp \ + src/qt/editaddressdialog.cpp \ + src/qt/bitcoinaddressvalidator.cpp \ + src/cryptopp/sha.cpp \ + src/cryptopp/cpu.cpp \ + src/util.cpp \ + src/script.cpp \ + src/main.cpp \ + src/init.cpp \ + src/rpc.cpp \ + src/net.cpp \ + src/irc.cpp \ + src/db.cpp \ + src/json/json_spirit_writer.cpp \ + src/json/json_spirit_value.cpp \ + src/json/json_spirit_reader.cpp \ + src/qt/clientmodel.cpp \ + src/qt/guiutil.cpp \ + src/qt/transactionrecord.cpp \ + src/qt/optionsmodel.cpp \ + src/qt/monitoreddatamapper.cpp \ + src/qt/transactiondesc.cpp \ + src/qt/transactiondescdialog.cpp + +RESOURCES += \ + src/qt/bitcoin.qrc + +FORMS += \ + src/qt/forms/sendcoinsdialog.ui \ + src/qt/forms/addressbookdialog.ui \ + src/qt/forms/aboutdialog.ui \ + src/qt/forms/editaddressdialog.ui \ + src/qt/forms/transactiondescdialog.ui + +CODECFORTR = UTF-8 +TRANSLATIONS = src/qt/locale/bitcoin_nl.ts diff --git a/bitcoin.pro b/bitcoin.pro deleted file mode 100644 index dd9ea7ff33..0000000000 --- a/bitcoin.pro +++ /dev/null @@ -1,107 +0,0 @@ -TEMPLATE = app -TARGET = -DEPENDPATH += . -INCLUDEPATH += src src/json src/cryptopp src/qt -unix:LIBS += -lssl -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx -macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 - -# disable quite some warnings becuase bitcoin core "sins" a lot -QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wno-invalid-offsetof -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare -Wno-char-subscripts -Wno-unused-value -Wno-sequence-point -Wno-parentheses -Wno-unknown-pragmas -Wno-switch - -# WINDOWS defines, -DSSL, look at build system - -# Input -DEPENDPATH += src/qt src src/cryptopp src json/include -HEADERS += src/qt/bitcoingui.h \ - src/qt/transactiontablemodel.h \ - src/qt/addresstablemodel.h \ - src/qt/optionsdialog.h \ - src/qt/sendcoinsdialog.h \ - src/qt/addressbookdialog.h \ - src/qt/aboutdialog.h \ - src/qt/editaddressdialog.h \ - src/qt/bitcoinaddressvalidator.h \ - src/base58.h \ - src/bignum.h \ - src/util.h \ - src/uint256.h \ - src/serialize.h \ - src/cryptopp/stdcpp.h \ - src/cryptopp/smartptr.h \ - src/cryptopp/simple.h \ - src/cryptopp/sha.h \ - src/cryptopp/secblock.h \ - src/cryptopp/pch.h \ - src/cryptopp/misc.h \ - src/cryptopp/iterhash.h \ - src/cryptopp/cryptlib.h \ - src/cryptopp/cpu.h \ - src/cryptopp/config.h \ - src/strlcpy.h \ - src/main.h \ - src/net.h \ - src/key.h \ - src/db.h \ - src/script.h \ - src/noui.h \ - src/init.h \ - src/headers.h \ - src/irc.h \ - src/json/json_spirit_writer_template.h \ - src/json/json_spirit_writer.h \ - src/json/json_spirit_value.h \ - src/json/json_spirit_utils.h \ - src/json/json_spirit_stream_reader.h \ - src/json/json_spirit_reader_template.h \ - src/json/json_spirit_reader.h \ - src/json/json_spirit_error_position.h \ - src/json/json_spirit.h \ - src/rpc.h \ - src/qt/clientmodel.h \ - src/qt/guiutil.h \ - src/qt/transactionrecord.h \ - src/qt/guiconstants.h \ - src/qt/optionsmodel.h \ - src/qt/monitoreddatamapper.h \ - src/externui.h \ - src/qt/transactiondesc.h \ - src/qt/transactiondescdialog.h -SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ - src/qt/transactiontablemodel.cpp \ - src/qt/addresstablemodel.cpp \ - src/qt/optionsdialog.cpp \ - src/qt/sendcoinsdialog.cpp \ - src/qt/addressbookdialog.cpp \ - src/qt/aboutdialog.cpp \ - src/qt/editaddressdialog.cpp \ - src/qt/bitcoinaddressvalidator.cpp \ - src/cryptopp/sha.cpp \ - src/cryptopp/cpu.cpp \ - src/util.cpp \ - src/script.cpp \ - src/main.cpp \ - src/init.cpp \ - src/rpc.cpp \ - src/net.cpp \ - src/irc.cpp \ - src/db.cpp \ - src/json/json_spirit_writer.cpp \ - src/json/json_spirit_value.cpp \ - src/json/json_spirit_reader.cpp \ - src/qt/clientmodel.cpp \ - src/qt/guiutil.cpp \ - src/qt/transactionrecord.cpp \ - src/qt/optionsmodel.cpp \ - src/qt/monitoreddatamapper.cpp \ - src/qt/transactiondesc.cpp \ - src/qt/transactiondescdialog.cpp - -RESOURCES += \ - src/qt/bitcoin.qrc - -FORMS += \ - src/qt/forms/sendcoinsdialog.ui \ - src/qt/forms/addressbookdialog.ui \ - src/qt/forms/aboutdialog.ui \ - src/qt/forms/editaddressdialog.ui \ - src/qt/forms/transactiondescdialog.ui -- cgit v1.2.3 From 3ac5aa4a9570c93af56e9535c1e49a30241767b0 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 12 Jun 2011 14:36:57 +0200 Subject: add svg version of icon, so that it can be scaled to bigger sizes later --- src/qt/res/icons/bitcoin.svg | 61 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/qt/res/icons/bitcoin.svg diff --git a/src/qt/res/icons/bitcoin.svg b/src/qt/res/icons/bitcoin.svg new file mode 100644 index 0000000000..3e7b05fd66 --- /dev/null +++ b/src/qt/res/icons/bitcoin.svg @@ -0,0 +1,61 @@ + + + + + + + + image/svg+xml + + + + + + + + + -- cgit v1.2.3 From d066e257447eb4ec998b66e65e823201df0339bf Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 12 Jun 2011 18:53:02 +0200 Subject: replace icons --- src/qt/res/icons/address-book.png | Bin 1211 -> 1211 bytes src/qt/res/icons/send.png | Bin 1485 -> 1704 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/qt/res/icons/address-book.png b/src/qt/res/icons/address-book.png index abfb3c3a51..1086fbeb63 100644 Binary files a/src/qt/res/icons/address-book.png and b/src/qt/res/icons/address-book.png differ diff --git a/src/qt/res/icons/send.png b/src/qt/res/icons/send.png index 0ba5359d7b..a1c56f52e0 100644 Binary files a/src/qt/res/icons/send.png and b/src/qt/res/icons/send.png differ -- cgit v1.2.3 From 92ab03afc847d46db47dd1bd1fbf03a7e0ef7ced Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 12 Jun 2011 19:22:06 +0200 Subject: new icons -- test --- src/qt/res/icons/bitcoin.png | Bin 645 -> 1023 bytes src/qt/res/icons/bitcoin.svg | 84 +++++++++++++++++++++++++++++++++++-------- src/qt/res/icons/toolbar.png | Bin 625 -> 1035 bytes 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/qt/res/icons/bitcoin.png b/src/qt/res/icons/bitcoin.png index cee616e354..55620f3e03 100644 Binary files a/src/qt/res/icons/bitcoin.png and b/src/qt/res/icons/bitcoin.png differ diff --git a/src/qt/res/icons/bitcoin.svg b/src/qt/res/icons/bitcoin.svg index 3e7b05fd66..96f10178a2 100644 --- a/src/qt/res/icons/bitcoin.svg +++ b/src/qt/res/icons/bitcoin.svg @@ -7,6 +7,7 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg2" @@ -14,10 +15,11 @@ inkscape:version="0.48.0 r9654" width="256" height="256" - sodipodi:docname="BC_Logo_bigb.svg" + sodipodi:docname="bitcoin.svg" inkscape:export-filename="/store/orion/projects/bitcoin/BC_Logo_icon32.png" - inkscape:export-xdpi="11.24" - inkscape:export-ydpi="11.24"> + inkscape:export-xdpi="11.25" + inkscape:export-ydpi="11.25" + style="display:inline"> @@ -31,7 +33,38 @@ + id="defs6"> + + + + + + + + + - + inkscape:current-layer="layer1" /> + + + + + + diff --git a/src/qt/res/icons/toolbar.png b/src/qt/res/icons/toolbar.png index e33d0f4bd9..84c18e29fa 100644 Binary files a/src/qt/res/icons/toolbar.png and b/src/qt/res/icons/toolbar.png differ -- cgit v1.2.3 From 249300aebe682dc1a7398794b14f0d6ebde0aee7 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 13 Jun 2011 09:05:48 +0200 Subject: Status column reorganization --- src/qt/bitcoingui.cpp | 2 +- src/qt/transactiontablemodel.cpp | 40 ++++++++++++++++++++++++++++++++++++---- src/qt/transactiontablemodel.h | 1 + 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index c08476cec3..3a24c76ab4 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -261,7 +261,7 @@ void BitcoinGUI::setTabsModel(QAbstractItemModel *transaction_model) transaction_table->verticalHeader()->hide(); transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Status, 120); + TransactionTableModel::Status, 145); transaction_table->horizontalHeader()->resizeSection( TransactionTableModel::Date, 120); transaction_table->horizontalHeader()->setResizeMode( diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index ddfebff3fe..64d99a5bc1 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -274,13 +274,13 @@ QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) con status = tr("Open until ") + GUIUtil::DateTimeStr(wtx->status.open_for); break; case TransactionStatus::Offline: - status = tr("%1/offline").arg(wtx->status.depth); + status = tr("Offline (%1)").arg(wtx->status.depth); break; case TransactionStatus::Unconfirmed: - status = tr("%1/unconfirmed").arg(wtx->status.depth); + status = tr("Unconfirmed (%1)").arg(wtx->status.depth); break; case TransactionStatus::HaveConfirmations: - status = tr("%1 confirmations").arg(wtx->status.depth); + status = tr("Confirmed (%1)").arg(wtx->status.depth); break; } @@ -400,13 +400,45 @@ QVariant TransactionTableModel::formatTxCredit(const TransactionRecord *wtx) con } } +QVariant TransactionTableModel::formatTxDecoration(const TransactionRecord *wtx) const +{ + switch(wtx->status.status) + { + case TransactionStatus::OpenUntilBlock: + case TransactionStatus::OpenUntilDate: + return QColor(64,64,255); + break; + case TransactionStatus::Offline: + return QColor(192,192,192); + case TransactionStatus::Unconfirmed: + if(wtx->status.depth) + { + return QColor(255,0,0); + } + else + { + return QColor(192,192,192); + } + case TransactionStatus::HaveConfirmations: + return QColor(0,255,0); + } + return QColor(0,0,0); +} + QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { if(!index.isValid()) return QVariant(); TransactionRecord *rec = static_cast(index.internalPointer()); - if(role == Qt::DisplayRole) + if(role == Qt::DecorationRole) + { + if(index.column() == Status) + { + return formatTxDecoration(rec); + } + } + else if(role == Qt::DisplayRole) { /* Delegate to specific column handlers */ switch(index.column()) diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 5974c0e7fe..804f004e38 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -47,6 +47,7 @@ private: QVariant formatTxDescription(const TransactionRecord *wtx) const; QVariant formatTxDebit(const TransactionRecord *wtx) const; QVariant formatTxCredit(const TransactionRecord *wtx) const; + QVariant formatTxDecoration(const TransactionRecord *wtx) const; private slots: void update(); -- cgit v1.2.3 From e83474f2ebbae84394f0b86cfea977d3024bd33f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 13 Jun 2011 12:07:32 +0200 Subject: Address book: select action (edit/select) based on context --- src/qt/addressbookdialog.cpp | 16 +++++++++++++++- src/qt/addressbookdialog.h | 15 ++++++++++----- src/qt/bitcoingui.cpp | 4 ++-- src/qt/forms/addressbookdialog.ui | 37 ++----------------------------------- src/qt/sendcoinsdialog.cpp | 2 +- 5 files changed, 30 insertions(+), 44 deletions(-) diff --git a/src/qt/addressbookdialog.cpp b/src/qt/addressbookdialog.cpp index 90950a6441..c716cd3eea 100644 --- a/src/qt/addressbookdialog.cpp +++ b/src/qt/addressbookdialog.cpp @@ -8,12 +8,24 @@ #include #include -AddressBookDialog::AddressBookDialog(QWidget *parent) : +AddressBookDialog::AddressBookDialog(Mode mode, QWidget *parent) : QDialog(parent), ui(new Ui::AddressBookDialog), model(0) { ui->setupUi(this); + + switch(mode) + { + case ForSending: + connect(ui->receiveTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_buttonBox_accepted())); + connect(ui->sendTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_buttonBox_accepted())); + break; + case ForEditing: + connect(ui->receiveTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_editButton_clicked())); + connect(ui->sendTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_editButton_clicked())); + break; + } } AddressBookDialog::~AddressBookDialog() @@ -126,6 +138,7 @@ void AddressBookDialog::on_newAddressButton_clicked() void AddressBookDialog::on_tabWidget_currentChanged(int index) { + // Enable/disable buttons based on selected tab switch(index) { case SendingTab: @@ -166,3 +179,4 @@ void AddressBookDialog::on_buttonBox_accepted() reject(); } } + diff --git a/src/qt/addressbookdialog.h b/src/qt/addressbookdialog.h index bf7c2a65a6..25b8839cd6 100644 --- a/src/qt/addressbookdialog.h +++ b/src/qt/addressbookdialog.h @@ -17,13 +17,18 @@ class AddressBookDialog : public QDialog Q_OBJECT public: - explicit AddressBookDialog(QWidget *parent = 0); - ~AddressBookDialog(); - - enum { + enum Tabs { SendingTab = 0, ReceivingTab = 1 - } Tabs; + }; + + enum Mode { + ForSending, // Pick address for sending + ForEditing // Open address book for editing + }; + + explicit AddressBookDialog(Mode mode, QWidget *parent = 0); + ~AddressBookDialog(); void setModel(AddressTableModel *model); void setTab(int tab); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 3a24c76ab4..05ac66fe1b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -285,7 +285,7 @@ void BitcoinGUI::sendcoinsClicked() void BitcoinGUI::addressbookClicked() { - AddressBookDialog dlg; + AddressBookDialog dlg(AddressBookDialog::ForEditing); dlg.setModel(model->getAddressTableModel()); dlg.setTab(AddressBookDialog::SendingTab); dlg.exec(); @@ -293,7 +293,7 @@ void BitcoinGUI::addressbookClicked() void BitcoinGUI::receivingAddressesClicked() { - AddressBookDialog dlg; + AddressBookDialog dlg(AddressBookDialog::ForEditing); dlg.setModel(model->getAddressTableModel()); dlg.setTab(AddressBookDialog::ReceivingTab); dlg.exec(); diff --git a/src/qt/forms/addressbookdialog.ui b/src/qt/forms/addressbookdialog.ui index 2b3c69fb97..2fc6ca785c 100644 --- a/src/qt/forms/addressbookdialog.ui +++ b/src/qt/forms/addressbookdialog.ui @@ -17,7 +17,7 @@ - 0 + 1 @@ -159,38 +159,5 @@ - - - receiveTableView - doubleClicked(QModelIndex) - editButton - click() - - - 334 - 249 - - - 333 - 326 - - - - - sendTableView - doubleClicked(QModelIndex) - editButton - click() - - - 329 - 261 - - - 332 - 326 - - - - + diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 721ab14108..1c11944e2c 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -100,7 +100,7 @@ void SendCoinsDialog::on_pasteButton_clicked() void SendCoinsDialog::on_addressBookButton_clicked() { - AddressBookDialog dlg; + AddressBookDialog dlg(AddressBookDialog::ForSending); dlg.setModel(model->getAddressTableModel()); dlg.setTab(AddressBookDialog::SendingTab); dlg.exec(); -- cgit v1.2.3 From 39cf857db9b926c47f545cba1d7113267260c40e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 13 Jun 2011 16:56:37 +0200 Subject: Internationalization -- initial step, make _ return a std::string to prevent memory leaks --- src/externui.h | 1 + src/qt/bitcoin.cpp | 8 ++++++++ src/qt/transactiondesc.cpp | 1 + src/rpc.cpp | 4 ++-- src/util.cpp | 10 ++++------ src/util.h | 8 ++++---- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/externui.h b/src/externui.h index e58ccc2242..3243164c10 100644 --- a/src/externui.h +++ b/src/externui.h @@ -41,5 +41,6 @@ extern bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, extern void CalledSetStatusBar(const std::string& strText, int nField); extern void UIThreadCall(boost::function0 fn); extern void MainFrameRepaint(); +extern std::string _(const char* psz); #endif diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index e003267426..a71c348254 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -87,6 +87,14 @@ void MainFrameRepaint() { } +/* + Translate string to current locale using Qt. + */ +std::string _(const char* psz) +{ + return QCoreApplication::translate("bitcoin-core", psz).toStdString(); +} + int main(int argc, char *argv[]) { QApplication app(argc, argv); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 4d8a55e99a..a9e55d5a6a 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -2,6 +2,7 @@ #include "guiutil.h" #include "main.h" +#include "externui.h" #include diff --git a/src/rpc.cpp b/src/rpc.cpp index 530bef4a43..ca88bec91b 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -40,13 +40,13 @@ Object JSONRPCError(int code, const string& message) } -void PrintConsole(const char* format, ...) +void PrintConsole(const std::string &format, ...) { char buffer[50000]; int limit = sizeof(buffer); va_list arg_ptr; va_start(arg_ptr, format); - int ret = _vsnprintf(buffer, limit, format, arg_ptr); + int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr); va_end(arg_ptr); if (ret < 0 || ret >= limit) { diff --git a/src/util.cpp b/src/util.cpp index 6199109289..fd4a9e459c 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -255,8 +255,7 @@ int my_snprintf(char* buffer, size_t limit, const char* format, ...) return ret; } - -string strprintf(const char* format, ...) +string strprintf(const std::string &format, ...) { char buffer[50000]; char* p = buffer; @@ -266,7 +265,7 @@ string strprintf(const char* format, ...) { va_list arg_ptr; va_start(arg_ptr, format); - ret = _vsnprintf(p, limit, format, arg_ptr); + ret = _vsnprintf(p, limit, format.c_str(), arg_ptr); va_end(arg_ptr); if (ret >= 0 && ret < limit) break; @@ -283,14 +282,13 @@ string strprintf(const char* format, ...) return str; } - -bool error(const char* format, ...) +bool error(const std::string &format, ...) { char buffer[50000]; int limit = sizeof(buffer); va_list arg_ptr; va_start(arg_ptr, format); - int ret = _vsnprintf(buffer, limit, format, arg_ptr); + int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr); va_end(arg_ptr); if (ret < 0 || ret >= limit) { diff --git a/src/util.h b/src/util.h index 25e1f77fd7..4759e8c1ce 100644 --- a/src/util.h +++ b/src/util.h @@ -140,14 +140,14 @@ inline int myclosesocket(SOCKET& hSocket) return ret; } #define closesocket(s) myclosesocket(s) - +#if 0 #ifndef GUI inline const char* _(const char* psz) { return psz; } #endif - +#endif @@ -177,8 +177,8 @@ void RandAddSeed(); void RandAddSeedPerfmon(); int OutputDebugStringF(const char* pszFormat, ...); int my_snprintf(char* buffer, size_t limit, const char* format, ...); -std::string strprintf(const char* format, ...); -bool error(const char* format, ...); +std::string strprintf(const std::string &format, ...); +bool error(const std::string &format, ...); void LogException(std::exception* pex, const char* pszThread); void PrintException(std::exception* pex, const char* pszThread); void PrintExceptionContinue(std::exception* pex, const char* pszThread); -- cgit v1.2.3 From 6315130e60ae44c4d6c5bac8d83732462b03dfea Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 13 Jun 2011 19:24:53 +0200 Subject: Internationalization -- conversion of strings from bitcoin core --- bitcoin-qt.pro | 3 +- scripts/extract_strings_qt.py | 63 +++++++++++++++++++++++++++++++ src/qt/bitcoinstrings.cpp | 86 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+), 1 deletion(-) create mode 100755 scripts/extract_strings_qt.py create mode 100644 src/qt/bitcoinstrings.cpp diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index ab5ddadd14..c7b7ff954b 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -94,7 +94,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/optionsmodel.cpp \ src/qt/monitoreddatamapper.cpp \ src/qt/transactiondesc.cpp \ - src/qt/transactiondescdialog.cpp + src/qt/transactiondescdialog.cpp \ + src/qt/bitcoinstrings.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/scripts/extract_strings_qt.py b/scripts/extract_strings_qt.py new file mode 100755 index 0000000000..56f47654c1 --- /dev/null +++ b/scripts/extract_strings_qt.py @@ -0,0 +1,63 @@ +#!/usr/bin/python +''' +Extract _("...") strings for translation and convert to Qt4 stringdefs so that +they can be picked up by Qt linguist. +''' +from subprocess import Popen, PIPE + +OUT_CPP="src/qt/bitcoinstrings.cpp" +EMPTY=['""'] + +def parse_po(text): + """ + Parse 'po' format produced by xgettext. + Return a list of (msgid,msgstr) tuples. + """ + messages = [] + msgid = [] + msgstr = [] + in_msgid = False + in_msgstr = False + + for line in text.split('\n'): + line = line.rstrip('\r') + if line.startswith('msgid '): + if in_msgstr: + messages.append((msgid, msgstr)) + in_msgstr = False + # message start + in_msgid = True + + msgid = [line[6:]] + elif line.startswith('msgstr '): + in_msgid = False + in_msgstr = True + msgstr = [line[7:]] + elif line.startswith('"'): + if in_msgid: + msgid.append(line) + if in_msgstr: + msgstr.append(line) + + if in_msgstr: + messages.append((msgid, msgstr)) + + return messages + +files = ['src/base58.h', 'src/bignum.h', 'src/db.cpp', 'src/db.h', 'src/externui.h', 'src/headers.h', 'src/init.cpp', 'src/init.h', 'src/irc.cpp', 'src/irc.h', 'src/key.h', 'src/main.cpp', 'src/main.h', 'src/net.cpp', 'src/net.h', 'src/noui.h', 'src/rpc.cpp', 'src/rpc.h', 'src/script.cpp', 'src/script.h', 'src/serialize.h', 'src/strlcpy.h', 'src/uint256.h', 'src/util.cpp', 'src/util.h'] + +# xgettext -n --keyword=_ $FILES +child = Popen(['xgettext','--output=-','-n','--keyword=_'] + files, stdout=PIPE) +(out, err) = child.communicate() + +messages = parse_po(out) + +f = open(OUT_CPP, 'w') +f.write('#include \n') +f.write('// Automatically generated by extract_strings.py\n') +f.write('static const char *bitcoin_strings[] = {') +for (msgid, msgstr) in messages: + if msgid != EMPTY: + f.write('QT_TRANSLATE_NOOP("bitcoin-core", %s),\n' % ('\n'.join(msgid))) +f.write('};') +f.close() diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp new file mode 100644 index 0000000000..2fa8de052b --- /dev/null +++ b/src/qt/bitcoinstrings.cpp @@ -0,0 +1,86 @@ +#include +// Automatically generated by extract_strings.py +static const char *bitcoin_strings[] = {QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin version"), +QT_TRANSLATE_NOOP("bitcoin-core", "Usage:"), +QT_TRANSLATE_NOOP("bitcoin-core", "Send command to -server or bitcoind\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "List commands\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Get help for a command\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Options:\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify configuration file (default: bitcoin.conf)\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify pid file (default: bitcoind.pid)\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Generate coins\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Don't generate coins\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Start minimized\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify data directory\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify connection timeout (in milliseconds)\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Connect through socks4 proxy\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Allow DNS lookups for addnode and connect\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Add a node to connect to\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Connect only to the specified node\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Don't accept connections from outside\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Don't attempt to use UPnP to map the listening port\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Attempt to use UPnP to map the listening port\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Fee per KB to add to transactions you send\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Accept command line and JSON-RPC commands\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Run in the background as a daemon and accept commands\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Use the test network\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Password for JSON-RPC connections\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Listen for JSON-RPC connections on (default: 8332)\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Allow JSON-RPC connections from specified IP address\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Send commands to node running on (default: 127.0.0.1)\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Set key pool size to (default: 100)\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Rescan the block chain for missing wallet transactions\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"\n" +"SSL options: (see the Bitcoin Wiki for SSL setup instructions)\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Use OpenSSL (https) for JSON-RPC connections\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Server certificate file (default: server.cert)\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Server private key (default: server.pem)\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:" +"@STRENGTH)\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "This help message\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Cannot obtain a lock on data directory %s. Bitcoin is probably already " +"running."), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading addr.dat \n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading blkindex.dat \n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat \n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee="), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Warning: -paytxfee is set very high. This is the transaction fee you will " +"pay if you send a transaction."), +QT_TRANSLATE_NOOP("bitcoin-core", "Warning: Disk space is low "), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Error: This transaction requires a transaction fee of at least %s because of " +"its amount, complexity, or use of recently received funds "), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: Transaction creation failed "), +QT_TRANSLATE_NOOP("bitcoin-core", "Sending..."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Error: The transaction was rejected. This might happen if some of the coins " +"in your wallet were already spent, such as if you used a copy of wallet.dat " +"and coins were spent in the copy but not marked as spent here."), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount"), +QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid bitcoin address"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Unable to bind to port %d on this computer. Bitcoin is probably already " +"running."), +QT_TRANSLATE_NOOP("bitcoin-core", "To use the %s option"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Warning: %s, you must set rpcpassword=\n" +"in the configuration file: %s\n" +"If the file does not exist, create it with owner-readable-only file " +"permissions.\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"You must set rpcpassword= in the configuration file:\n" +"%s\n" +"If the file does not exist, create it with owner-readable-only file " +"permissions."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Warning: Please check that your computer's date and time are correct. If " +"your clock is wrong Bitcoin will not work properly."), +QT_TRANSLATE_NOOP("bitcoin-core", "-beta"), +}; \ No newline at end of file -- cgit v1.2.3 From f15df6bb7a470f75ca61f8c9ddcebfbb39a76f49 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 13 Jun 2011 21:40:07 +0200 Subject: link to -lcrypto as well --- bitcoin-qt.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index c7b7ff954b..f1ce9b1232 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -2,7 +2,7 @@ TEMPLATE = app TARGET = DEPENDPATH += . INCLUDEPATH += src src/json src/cryptopp src/qt -unix:LIBS += -lssl -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx +unix:LIBS += -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 # disable quite some warnings becuase bitcoin core "sins" a lot -- cgit v1.2.3 From 5363cb05f682a7954b6fa5c74fbe589c41b955e3 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 14 Jun 2011 08:13:29 +0200 Subject: Add berkelydb version warning --- README.rst | 15 +++++++++++++++ bitcoin-qt.pro | 6 ++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 2ffc4a92b8..9d7ad10f18 100644 --- a/README.rst +++ b/README.rst @@ -60,3 +60,18 @@ Alternatively, install Qt Creator and open the `bitcoin-qt.pro` file. An executable named `bitcoin-qt` will be built. +Berkely DB version warning +========================== + +A warning for people using the *static binary* version of Bitcoin (tl;dr: **Berkely DB databases are not forward compatible**). + +The static binary version of Bitcoin is linked against libdb4.7 or libdb4.8 (see also `this Debian issue`_). + +Now the nasty thing is that databases from 5.X are not compatible with 4.X. + +If the globally installed development package of Berkely DB installed on your system is 5.X, any source you +build yourself will be linked against that. The first time you run with a 5.X version the database will be upgraded, +and 4.X cannot open the new format. This means that you cannot go back to the old statically linked version without +significant hassle! + +.. _`this Debian issue`: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=621425 diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index f1ce9b1232..f3299554d6 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -2,13 +2,15 @@ TEMPLATE = app TARGET = DEPENDPATH += . INCLUDEPATH += src src/json src/cryptopp src/qt + +# for boost 1.37, add -mt to the boost libraries unix:LIBS += -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 -# disable quite some warnings becuase bitcoin core "sins" a lot +# disable quite some warnings because bitcoin core "sins" a lot QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wno-invalid-offsetof -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare -Wno-char-subscripts -Wno-unused-value -Wno-sequence-point -Wno-parentheses -Wno-unknown-pragmas -Wno-switch -# WINDOWS defines, -DSSL, look at build system +# TODO: WINDOWS defines, -DSSL # Input DEPENDPATH += src/qt src src/cryptopp src json/include -- cgit v1.2.3 From a790ec5884bdec8eadcfc1f31c6a8c94a0240976 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 14 Jun 2011 21:06:00 +0200 Subject: Make status column narrow (icon only, details on tooltip) --- src/qt/bitcoingui.cpp | 4 ++-- src/qt/transactiontablemodel.cpp | 26 ++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 05ac66fe1b..23640fe633 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -71,7 +71,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Address:
: New... : Paste to clipboard QHBoxLayout *hbox_address = new QHBoxLayout(); - hbox_address->addWidget(new QLabel(tr("Your Bitcoin Address:"))); + hbox_address->addWidget(new QLabel(tr("Your Bitcoin address:"))); address = new QLineEdit(); address->setReadOnly(true); address->setFont(GUIUtil::bitcoinAddressFont()); @@ -261,7 +261,7 @@ void BitcoinGUI::setTabsModel(QAbstractItemModel *transaction_model) transaction_table->verticalHeader()->hide(); transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Status, 145); + TransactionTableModel::Status, 23); transaction_table->horizontalHeader()->resizeSection( TransactionTableModel::Date, 120); transaction_table->horizontalHeader()->setResizeMode( diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 64d99a5bc1..47cab1b233 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -443,8 +443,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const /* Delegate to specific column handlers */ switch(index.column()) { - case Status: - return formatTxStatus(rec); + //case Status: + // return formatTxStatus(rec); case Date: return formatTxDate(rec); case Description: @@ -472,6 +472,13 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return rec->credit; } } + else if (role == Qt::ToolTipRole) + { + if(index.column() == Status) + { + return formatTxStatus(rec); + } + } else if (role == Qt::TextAlignmentRole) { return column_alignments[index.column()]; @@ -522,6 +529,21 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat else if (role == Qt::TextAlignmentRole) { return column_alignments[section]; + } else if (role == Qt::ToolTipRole) + { + switch(section) + { + case Status: + return tr("Transaction status. Hover over this field to show number of transactions."); + case Date: + return tr("Date and time that the transaction was received."); + case Description: + return tr("Short description of the transaction."); + case Debit: + return tr("Amount removed from balance."); + case Credit: + return tr("Amount added to balance."); + } } } return QVariant(); -- cgit v1.2.3 From b1ef1b24ced1e8360d1bf62b9fefbc960cdd19be Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 14 Jun 2011 21:34:51 +0200 Subject: add connection meter --- src/qt/bitcoin.qrc | 5 +++++ src/qt/bitcoingui.cpp | 18 ++++++++++++++---- src/qt/bitcoingui.h | 1 + src/qt/res/icons/connect0_16.png | Bin 0 -> 702 bytes src/qt/res/icons/connect1_16.png | Bin 0 -> 612 bytes src/qt/res/icons/connect2_16.png | Bin 0 -> 623 bytes src/qt/res/icons/connect3_16.png | Bin 0 -> 625 bytes src/qt/res/icons/connect4_16.png | Bin 0 -> 611 bytes 8 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/qt/res/icons/connect0_16.png create mode 100644 src/qt/res/icons/connect1_16.png create mode 100644 src/qt/res/icons/connect2_16.png create mode 100644 src/qt/res/icons/connect3_16.png create mode 100644 src/qt/res/icons/connect4_16.png diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 80904b341c..8cf6c3252c 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -5,6 +5,11 @@ res/icons/quit.png res/icons/send.png res/icons/toolbar.png + res/icons/connect0_16.png + res/icons/connect1_16.png + res/icons/connect2_16.png + res/icons/connect3_16.png + res/icons/connect4_16.png res/images/about.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 23640fe633..96afa41aa9 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -108,12 +108,12 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Create status bar statusBar(); - + labelConnections = new QLabel(); labelConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelConnections->setMinimumWidth(130); + labelConnections->setMinimumWidth(150); labelConnections->setToolTip(tr("Number of connections to other clients")); - + labelBlocks = new QLabel(); labelBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); labelBlocks->setMinimumWidth(130); @@ -345,7 +345,17 @@ void BitcoinGUI::setAddress(const QString &addr) void BitcoinGUI::setNumConnections(int count) { - labelConnections->setText(QLocale::system().toString(count)+" "+tr("connections(s)", "", count)); + QString icon; + switch(count) + { + case 0: icon = ":/icons/connect0"; break; + case 1: icon = ":/icons/connect1"; break; + case 2: icon = ":/icons/connect2"; break; + case 3: icon = ":/icons/connect3"; break; + default: icon = ":/icons/connect4"; break; + } + labelConnections->setTextFormat(Qt::RichText); + labelConnections->setText(" " + QLocale::system().toString(count)+" "+tr("connection(s)", "", count)); } void BitcoinGUI::setNumBlocks(int count) diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 96452ef18b..e1b3ef1783 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -40,6 +40,7 @@ private: QLineEdit *address; QLabel *labelBalance; QLabel *labelConnections; + QLabel *labelConnectionsIcon; QLabel *labelBlocks; QLabel *labelTransactions; diff --git a/src/qt/res/icons/connect0_16.png b/src/qt/res/icons/connect0_16.png new file mode 100644 index 0000000000..66f3ae4f86 Binary files /dev/null and b/src/qt/res/icons/connect0_16.png differ diff --git a/src/qt/res/icons/connect1_16.png b/src/qt/res/icons/connect1_16.png new file mode 100644 index 0000000000..76000beee2 Binary files /dev/null and b/src/qt/res/icons/connect1_16.png differ diff --git a/src/qt/res/icons/connect2_16.png b/src/qt/res/icons/connect2_16.png new file mode 100644 index 0000000000..6d9a37281a Binary files /dev/null and b/src/qt/res/icons/connect2_16.png differ diff --git a/src/qt/res/icons/connect3_16.png b/src/qt/res/icons/connect3_16.png new file mode 100644 index 0000000000..a211700785 Binary files /dev/null and b/src/qt/res/icons/connect3_16.png differ diff --git a/src/qt/res/icons/connect4_16.png b/src/qt/res/icons/connect4_16.png new file mode 100644 index 0000000000..e2fe97d496 Binary files /dev/null and b/src/qt/res/icons/connect4_16.png differ -- cgit v1.2.3 From cf450e1b4c5d11b2adeef8d9aa5aadde5c15a96b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 15 Jun 2011 20:07:21 +0200 Subject: icons test --- src/qt/transactiontablemodel.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 47cab1b233..d46704a922 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include const QString TransactionTableModel::Sent = "s"; @@ -112,7 +113,7 @@ struct TransactionTablePriv if(inWallet && !inModel) { - /* Added */ + /* Added -- insert at the right position */ QList toInsert = TransactionRecord::decomposeTransaction(mi->second); if(!toInsert.isEmpty()) /* only if something to insert */ @@ -129,7 +130,7 @@ struct TransactionTablePriv } else if(!inWallet && inModel) { - /* Removed */ + /* Removed -- remove entire transaction from table */ parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); cachedWallet.erase(lower, upper); parent->endRemoveRows(); @@ -413,14 +414,14 @@ QVariant TransactionTableModel::formatTxDecoration(const TransactionRecord *wtx) case TransactionStatus::Unconfirmed: if(wtx->status.depth) { - return QColor(255,0,0); + return QIcon(":/icons/bitcoin"); } else { - return QColor(192,192,192); + return QIcon::fromTheme("stock_lock.png"); } case TransactionStatus::HaveConfirmations: - return QColor(0,255,0); + return QIcon::fromTheme("stock_lock-ok.png"); } return QColor(0,0,0); } -- cgit v1.2.3 From 58557b5aff589cafbd02820d997bb37089b383eb Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 15 Jun 2011 21:03:17 +0200 Subject: transaction status icons --- src/qt/bitcoin.qrc | 3 +++ src/qt/res/icons/transaction0.png | Bin 0 -> 690 bytes src/qt/res/icons/transaction1.png | Bin 0 -> 969 bytes src/qt/res/icons/transaction2.png | Bin 0 -> 413 bytes src/qt/transactiontablemodel.cpp | 6 +++--- 5 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 src/qt/res/icons/transaction0.png create mode 100644 src/qt/res/icons/transaction1.png create mode 100644 src/qt/res/icons/transaction2.png diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 8cf6c3252c..3fa123b364 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -10,6 +10,9 @@ res/icons/connect2_16.png res/icons/connect3_16.png res/icons/connect4_16.png + res/icons/transaction0.png + res/icons/transaction1.png + res/icons/transaction2.png res/images/about.png diff --git a/src/qt/res/icons/transaction0.png b/src/qt/res/icons/transaction0.png new file mode 100644 index 0000000000..6d698b350f Binary files /dev/null and b/src/qt/res/icons/transaction0.png differ diff --git a/src/qt/res/icons/transaction1.png b/src/qt/res/icons/transaction1.png new file mode 100644 index 0000000000..f19450aa9f Binary files /dev/null and b/src/qt/res/icons/transaction1.png differ diff --git a/src/qt/res/icons/transaction2.png b/src/qt/res/icons/transaction2.png new file mode 100644 index 0000000000..01bb558a10 Binary files /dev/null and b/src/qt/res/icons/transaction2.png differ diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index d46704a922..a84b45aee3 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -414,14 +414,14 @@ QVariant TransactionTableModel::formatTxDecoration(const TransactionRecord *wtx) case TransactionStatus::Unconfirmed: if(wtx->status.depth) { - return QIcon(":/icons/bitcoin"); + return QIcon(":/icons/transaction1"); } else { - return QIcon::fromTheme("stock_lock.png"); + return QIcon(":/icons/transaction0"); } case TransactionStatus::HaveConfirmations: - return QIcon::fromTheme("stock_lock-ok.png"); + return QIcon(":/icons/transaction2"); } return QColor(0,0,0); } -- cgit v1.2.3 From aec8763a8e19df73cec333a2d481d492e7e1dc70 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 15 Jun 2011 21:06:51 +0200 Subject: add attribution for icons --- doc/assets-attribution.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 doc/assets-attribution.txt diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt new file mode 100644 index 0000000000..b33251bcfd --- /dev/null +++ b/doc/assets-attribution.txt @@ -0,0 +1,21 @@ +Icon: src/qt/res/icons/send.png +Icon Pack: Vista Style Arrow +Designer: Icons Land +License: Freeware Non-commercial +http://findicons.com/icon/231371/right3green + +Icon: src/qt/res/icons/address-book.png +Icon Pack: Farm-Fresh Web +Designer: FatCow Web Hosting +License: Creative Commons Attribution (by) +http://findicons.com/icon/163938/book_open + +Icon: src/qt/res/icons/connect*.png +Icon Pack: Human-O2 +Designer: schollidesign +License: GNU/GPL +http://findicons.com/icon/93743/blocks_gnome_netstatus_0 + +Icon: src/qt/res/icons/transaction*.png +Designer: md2k7 +https://forum.bitcoin.org/index.php?topic=15276.0 -- cgit v1.2.3 From 8c69f1fb25b91e447324ea7e3e80c5c497cbda01 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 16 Jun 2011 09:08:57 +0200 Subject: show connection meter "full" only at 10+ connections --- src/qt/bitcoingui.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 96afa41aa9..b45a4f9f91 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -349,9 +349,9 @@ void BitcoinGUI::setNumConnections(int count) switch(count) { case 0: icon = ":/icons/connect0"; break; - case 1: icon = ":/icons/connect1"; break; - case 2: icon = ":/icons/connect2"; break; - case 3: icon = ":/icons/connect3"; break; + case 1: case 2: icon = ":/icons/connect1"; break; + case 3: case 4: icon = ":/icons/connect2"; break; + case 5: case 6: icon = ":/icons/connect3"; break; default: icon = ":/icons/connect4"; break; } labelConnections->setTextFormat(Qt::RichText); -- cgit v1.2.3 From 89c94b5578e1882f3fd4bfadc9e39b436579563e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 17 Jun 2011 17:47:40 +0200 Subject: better icons for confirmations --- doc/assets-attribution.txt | 4 ++++ src/qt/bitcoin.qrc | 21 +++++++++++++-------- src/qt/bitcoingui.cpp | 10 +++++----- src/qt/res/icons/clock1.png | Bin 0 -> 946 bytes src/qt/res/icons/clock2.png | Bin 0 -> 944 bytes src/qt/res/icons/clock3.png | Bin 0 -> 946 bytes src/qt/res/icons/clock4.png | Bin 0 -> 962 bytes src/qt/res/icons/clock5.png | Bin 0 -> 956 bytes src/qt/res/icons/transaction0.png | Bin 690 -> 569 bytes src/qt/res/icons/transaction1.png | Bin 969 -> 0 bytes src/qt/transactiontablemodel.cpp | 17 +++++++++-------- 11 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 src/qt/res/icons/clock1.png create mode 100644 src/qt/res/icons/clock2.png create mode 100644 src/qt/res/icons/clock3.png create mode 100644 src/qt/res/icons/clock4.png create mode 100644 src/qt/res/icons/clock5.png delete mode 100644 src/qt/res/icons/transaction1.png diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index b33251bcfd..eced20143e 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -1,3 +1,7 @@ +Icon: src/qt/res/icons/clock*.png +Designer: Wladimir van der Laan +License: Creative Commons Attribution + Icon: src/qt/res/icons/send.png Icon Pack: Vista Style Arrow Designer: Icons Land diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 3fa123b364..63bb26d8b5 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -5,14 +5,19 @@ res/icons/quit.png res/icons/send.png res/icons/toolbar.png - res/icons/connect0_16.png - res/icons/connect1_16.png - res/icons/connect2_16.png - res/icons/connect3_16.png - res/icons/connect4_16.png - res/icons/transaction0.png - res/icons/transaction1.png - res/icons/transaction2.png + res/icons/connect0_16.png + res/icons/connect1_16.png + res/icons/connect2_16.png + res/icons/connect3_16.png + res/icons/connect4_16.png + res/icons/transaction0.png + res/icons/transaction1.png + res/icons/transaction2.png + res/icons/clock1.png + res/icons/clock2.png + res/icons/clock3.png + res/icons/clock4.png + res/icons/clock5.png res/images/about.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index b45a4f9f91..4e9a021351 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -348,11 +348,11 @@ void BitcoinGUI::setNumConnections(int count) QString icon; switch(count) { - case 0: icon = ":/icons/connect0"; break; - case 1: case 2: icon = ":/icons/connect1"; break; - case 3: case 4: icon = ":/icons/connect2"; break; - case 5: case 6: icon = ":/icons/connect3"; break; - default: icon = ":/icons/connect4"; break; + case 0: icon = ":/icons/connect_0"; break; + case 1: case 2: case 3: icon = ":/icons/connect_1"; break; + case 4: case 5: case 6: icon = ":/icons/connect_2"; break; + case 7: case 8: case 9: icon = ":/icons/connect_3"; break; + default: icon = ":/icons/connect_4"; break; } labelConnections->setTextFormat(Qt::RichText); labelConnections->setText(" " + QLocale::system().toString(count)+" "+tr("connection(s)", "", count)); diff --git a/src/qt/res/icons/clock1.png b/src/qt/res/icons/clock1.png new file mode 100644 index 0000000000..448e47f947 Binary files /dev/null and b/src/qt/res/icons/clock1.png differ diff --git a/src/qt/res/icons/clock2.png b/src/qt/res/icons/clock2.png new file mode 100644 index 0000000000..c1a6e99f7f Binary files /dev/null and b/src/qt/res/icons/clock2.png differ diff --git a/src/qt/res/icons/clock3.png b/src/qt/res/icons/clock3.png new file mode 100644 index 0000000000..e429a402cf Binary files /dev/null and b/src/qt/res/icons/clock3.png differ diff --git a/src/qt/res/icons/clock4.png b/src/qt/res/icons/clock4.png new file mode 100644 index 0000000000..ba036f47d3 Binary files /dev/null and b/src/qt/res/icons/clock4.png differ diff --git a/src/qt/res/icons/clock5.png b/src/qt/res/icons/clock5.png new file mode 100644 index 0000000000..411d7a78a0 Binary files /dev/null and b/src/qt/res/icons/clock5.png differ diff --git a/src/qt/res/icons/transaction0.png b/src/qt/res/icons/transaction0.png index 6d698b350f..4578666ee4 100644 Binary files a/src/qt/res/icons/transaction0.png and b/src/qt/res/icons/transaction0.png differ diff --git a/src/qt/res/icons/transaction1.png b/src/qt/res/icons/transaction1.png deleted file mode 100644 index f19450aa9f..0000000000 Binary files a/src/qt/res/icons/transaction1.png and /dev/null differ diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index a84b45aee3..eeb948b616 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -412,16 +412,17 @@ QVariant TransactionTableModel::formatTxDecoration(const TransactionRecord *wtx) case TransactionStatus::Offline: return QColor(192,192,192); case TransactionStatus::Unconfirmed: - if(wtx->status.depth) + switch(wtx->status.depth) { - return QIcon(":/icons/transaction1"); - } - else - { - return QIcon(":/icons/transaction0"); - } + case 0: return QIcon(":/icons/transaction_0"); + case 1: return QIcon(":/icons/transaction_1"); + case 2: return QIcon(":/icons/transaction_2"); + case 3: return QIcon(":/icons/transaction_3"); + case 4: return QIcon(":/icons/transaction_4"); + default: return QIcon(":/icons/transaction_5"); + }; case TransactionStatus::HaveConfirmations: - return QIcon(":/icons/transaction2"); + return QIcon(":/icons/transaction_confirmed"); } return QColor(0,0,0); } -- cgit v1.2.3 From e61cfaf5c1c93d9f851174602153e0d63d1a6432 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 17 Jun 2011 18:25:29 +0200 Subject: add svg sources for icons --- src/qt/res/icons/clock1.svg | 261 +++++++++++++++++++++++++++++++++++++ src/qt/res/icons/clock2.svg | 262 ++++++++++++++++++++++++++++++++++++++ src/qt/res/icons/clock3.svg | 261 +++++++++++++++++++++++++++++++++++++ src/qt/res/icons/clock4.svg | 261 +++++++++++++++++++++++++++++++++++++ src/qt/res/icons/clock5.svg | 262 ++++++++++++++++++++++++++++++++++++++ src/qt/res/icons/clock_green.svg | 262 ++++++++++++++++++++++++++++++++++++++ src/qt/res/icons/questionmark.svg | 159 +++++++++++++++++++++++ 7 files changed, 1728 insertions(+) create mode 100644 src/qt/res/icons/clock1.svg create mode 100644 src/qt/res/icons/clock2.svg create mode 100644 src/qt/res/icons/clock3.svg create mode 100644 src/qt/res/icons/clock4.svg create mode 100644 src/qt/res/icons/clock5.svg create mode 100644 src/qt/res/icons/clock_green.svg create mode 100644 src/qt/res/icons/questionmark.svg diff --git a/src/qt/res/icons/clock1.svg b/src/qt/res/icons/clock1.svg new file mode 100644 index 0000000000..793dc7f91c --- /dev/null +++ b/src/qt/res/icons/clock1.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/icons/clock2.svg b/src/qt/res/icons/clock2.svg new file mode 100644 index 0000000000..6a78adf700 --- /dev/null +++ b/src/qt/res/icons/clock2.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/icons/clock3.svg b/src/qt/res/icons/clock3.svg new file mode 100644 index 0000000000..09ccc2549f --- /dev/null +++ b/src/qt/res/icons/clock3.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/icons/clock4.svg b/src/qt/res/icons/clock4.svg new file mode 100644 index 0000000000..7d9dc37acb --- /dev/null +++ b/src/qt/res/icons/clock4.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/icons/clock5.svg b/src/qt/res/icons/clock5.svg new file mode 100644 index 0000000000..9fd58d9d97 --- /dev/null +++ b/src/qt/res/icons/clock5.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/icons/clock_green.svg b/src/qt/res/icons/clock_green.svg new file mode 100644 index 0000000000..e31f0e7995 --- /dev/null +++ b/src/qt/res/icons/clock_green.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/icons/questionmark.svg b/src/qt/res/icons/questionmark.svg new file mode 100644 index 0000000000..c03c159a5f --- /dev/null +++ b/src/qt/res/icons/questionmark.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + ? + ? + + + ? + + -- cgit v1.2.3 From 553af2f702154c86c1ea97fc827d1d241a216ebd Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 17 Jun 2011 18:43:08 +0200 Subject: remove unused icons --- src/qt/bitcoin.qrc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 63bb26d8b5..ec64770cbe 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -11,7 +11,6 @@ res/icons/connect3_16.png res/icons/connect4_16.png res/icons/transaction0.png - res/icons/transaction1.png res/icons/transaction2.png res/icons/clock1.png res/icons/clock2.png -- cgit v1.2.3 From e4347a43b9fb7c0815c18475952297bbedab00a0 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 17 Jun 2011 20:35:22 +0200 Subject: add license for md2k7's icons --- doc/assets-attribution.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index eced20143e..820183ee93 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -23,3 +23,5 @@ http://findicons.com/icon/93743/blocks_gnome_netstatus_0 Icon: src/qt/res/icons/transaction*.png Designer: md2k7 https://forum.bitcoin.org/index.php?topic=15276.0 +License: You are free to do with these icons as you wish, including selling, + copying, modifying etc. -- cgit v1.2.3 From aa5297266093eccd984a2d8fb20499617ef96c81 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 17 Jun 2011 22:44:15 +0200 Subject: fix issue #3 (dark theme compat) --- src/qt/transactiontablemodel.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index eeb948b616..d2cdd4a623 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -488,11 +488,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const else if (role == Qt::ForegroundRole) { /* Non-confirmed transactions are grey */ - if(rec->status.confirmed) - { - return QColor(0, 0, 0); - } - else + if(!rec->status.confirmed) { return QColor(128, 128, 128); } -- cgit v1.2.3 From 0f3981bea94cea957f0de4b128f7feffbfc2d9c6 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 18 Jun 2011 11:53:25 +0200 Subject: remove commented code, use // for one-line comments and comments inside functions --- src/qt/addressbookdialog.cpp | 17 ++++++------- src/qt/addresstablemodel.cpp | 21 ++++++++-------- src/qt/bitcoin.cpp | 5 ++-- src/qt/bitcoinaddressvalidator.cpp | 6 ++--- src/qt/bitcoingui.cpp | 4 +-- src/qt/clientmodel.cpp | 10 +++----- src/qt/monitoreddatamapper.cpp | 5 ++-- src/qt/optionsdialog.cpp | 4 +-- src/qt/optionsmodel.cpp | 4 +-- src/qt/sendcoinsdialog.cpp | 4 +-- src/qt/transactiondesc.cpp | 7 +++--- src/qt/transactionrecord.cpp | 3 ++- src/qt/transactiontablemodel.cpp | 50 ++++++++++++++++++-------------------- 13 files changed, 66 insertions(+), 74 deletions(-) diff --git a/src/qt/addressbookdialog.cpp b/src/qt/addressbookdialog.cpp index c716cd3eea..9126a645e1 100644 --- a/src/qt/addressbookdialog.cpp +++ b/src/qt/addressbookdialog.cpp @@ -36,10 +36,10 @@ AddressBookDialog::~AddressBookDialog() void AddressBookDialog::setModel(AddressTableModel *model) { this->model = model; - /* Refresh list from core */ + // Refresh list from core model->updateList(); - /* Receive filter */ + // Receive filter QSortFilterProxyModel *receive_model = new QSortFilterProxyModel(this); receive_model->setSourceModel(model); receive_model->setDynamicSortFilter(true); @@ -47,7 +47,7 @@ void AddressBookDialog::setModel(AddressTableModel *model) receive_model->setFilterFixedString(AddressTableModel::Receive); ui->receiveTableView->setModel(receive_model); - /* Send filter */ + // Send filter QSortFilterProxyModel *send_model = new QSortFilterProxyModel(this); send_model->setSourceModel(model); send_model->setDynamicSortFilter(true); @@ -55,7 +55,7 @@ void AddressBookDialog::setModel(AddressTableModel *model) send_model->setFilterFixedString(AddressTableModel::Send); ui->sendTableView->setModel(send_model); - /* Set column widths */ + // Set column widths ui->receiveTableView->horizontalHeader()->resizeSection( AddressTableModel::Address, 320); ui->receiveTableView->horizontalHeader()->setResizeMode( @@ -86,9 +86,8 @@ QTableView *AddressBookDialog::getCurrentTable() void AddressBookDialog::on_copyToClipboard_clicked() { - /* Copy currently selected address to clipboard - (or nothing, if nothing selected) - */ + // Copy currently selected address to clipboard + // (or nothing, if nothing selected) QTableView *table = getCurrentTable(); QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); @@ -106,11 +105,11 @@ void AddressBookDialog::on_editButton_clicked() { return; } - /* Map selected index to source address book model */ + // Map selected index to source address book model QAbstractProxyModel *proxy_model = static_cast(getCurrentTable()->model()); QModelIndex selected = proxy_model->mapToSource(indexes.at(0)); - /* Double click also triggers edit button */ + // Double click also triggers edit button EditAddressDialog dlg( ui->tabWidget->currentIndex() == SendingTab ? EditAddressDialog::EditSendingAddress : diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 91b87fb7f1..2d69df3da0 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -23,7 +23,7 @@ struct AddressTableEntry type(type), label(label), address(address) {} }; -/* Private implementation */ +// Private implementation struct AddressTablePriv { QList cachedAddressTable; @@ -144,12 +144,12 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu rec->label = value.toString(); break; case Address: - /* Double-check that we're not overwriting receiving address */ + // Double-check that we're not overwriting receiving address if(rec->type == AddressTableEntry::Sending) { - /* Remove old entry */ + // Remove old entry CWalletDB().EraseName(rec->address.toStdString()); - /* Add new entry with new address */ + // Add new entry with new address SetAddressBookName(value.toString().toStdString(), rec->label.toStdString()); rec->address = value.toString(); @@ -191,7 +191,7 @@ QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & pa void AddressTableModel::updateList() { - /* Update internal model from Bitcoin core */ + // Update internal model from Bitcoin core beginResetModel(); priv->refreshAddressTable(); endResetModel(); @@ -204,7 +204,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con if(type == Send) { - /* Check for duplicate */ + // Check for duplicate CRITICAL_BLOCK(cs_mapAddressBook) { if(mapAddressBook.count(strAddress)) @@ -215,14 +215,14 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con } else if(type == Receive) { - /* Generate a new address to associate with given label */ + // Generate a new address to associate with given label strAddress = PubKeyToAddress(GetKeyFromKeyPool()); } else { return QString(); } - /* Add entry and update list */ + // Add entry and update list SetAddressBookName(strAddress, strLabel); updateList(); return QString::fromStdString(strAddress); @@ -234,9 +234,8 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex & paren AddressTableEntry *rec = priv->index(row); if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving) { - /* Can only remove one row at a time, and cannot remove rows not in model. - Also refuse to remove receiving addresses. - */ + // Can only remove one row at a time, and cannot remove rows not in model. + // Also refuse to remove receiving addresses. return false; } CWalletDB().EraseName(rec->address.toStdString()); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index a71c348254..cb24c1901d 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -57,9 +57,8 @@ bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindo return true; bool payFee = false; - /* Call slot on GUI thread. - If called from another thread, use a blocking QueuedConnection. - */ + // Call slot on GUI thread. + // If called from another thread, use a blocking QueuedConnection. Qt::ConnectionType connectionType = Qt::DirectConnection; if(QThread::currentThread() != QCoreApplication::instance()->thread()) { diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index 761a266933..92d7daeb88 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -22,7 +22,7 @@ BitcoinAddressValidator::BitcoinAddressValidator(QObject *parent) : QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) const { - /* Correction */ + // Correction for(int idx=0; idx= 'A' && ch<='Z')) && ch != 'l' && ch != 'I' && ch != '0' && ch != 'O') { - /* Alphanumeric and not a 'forbidden' character */ + // Alphanumeric and not a 'forbidden' character } else { diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 4e9a021351..4d3efe26db 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -429,7 +429,7 @@ void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) void BitcoinGUI::transactionDetails(const QModelIndex& idx) { - /* A transaction is doubleclicked */ + // A transaction is doubleclicked TransactionDescDialog dlg(idx); dlg.exec(); } @@ -443,7 +443,7 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int .data(Qt::EditRole).toULongLong(); if((credit+debit)>0) { - /* On incoming transaction, make an info balloon */ + // On incoming transaction, make an info balloon QString date = ttm->index(start, TransactionTableModel::Date, parent) .data().toString(); QString description = ttm->index(start, TransactionTableModel::Description, parent) diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 97391e0938..10cafaf1af 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -11,9 +11,8 @@ ClientModel::ClientModel(QObject *parent) : QObject(parent), optionsModel(0), addressTableModel(0), transactionTableModel(0) { - /* Until signal notifications is built into the bitcoin core, - simply update everything after polling using a timer. - */ + // Until signal notifications is built into the bitcoin core, + // simply update everything after polling using a timer. QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(MODEL_UPDATE_DELAY); @@ -63,9 +62,8 @@ int ClientModel::getNumTransactions() void ClientModel::update() { - /* Plainly emit all signals for now. To be precise this should check - wether the values actually changed first. - */ + // Plainly emit all signals for now. To be more efficient this should check + // whether the values actually changed first. emit balanceChanged(getBalance()); emit addressChanged(getAddress()); emit numConnectionsChanged(getNumConnections()); diff --git a/src/qt/monitoreddatamapper.cpp b/src/qt/monitoreddatamapper.cpp index e70aa7ebf6..7bf4fa6c8f 100644 --- a/src/qt/monitoreddatamapper.cpp +++ b/src/qt/monitoreddatamapper.cpp @@ -26,9 +26,8 @@ void MonitoredDataMapper::addMapping(QWidget *widget, int section, const QByteAr void MonitoredDataMapper::addChangeMonitor(QWidget *widget) { - /* Watch user property of widget for changes, and connect - the signal to our viewModified signal. - */ + // Watch user property of widget for changes, and connect + // the signal to our viewModified signal. QMetaProperty prop = widget->metaObject()->userProperty(); int signal = prop.notifySignalIndex(); int method = this->metaObject()->indexOfMethod("viewModified()"); diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index d46b48fb29..1bf123b285 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -207,7 +207,7 @@ MainOptionsPage::MainOptionsPage(QWidget *parent): layout->addLayout(fee_hbox); - layout->addStretch(1); /* Extra space at bottom */ + layout->addStretch(1); // Extra space at bottom setLayout(layout); @@ -221,7 +221,7 @@ MainOptionsPage::MainOptionsPage(QWidget *parent): void MainOptionsPage::setMapper(MonitoredDataMapper *mapper) { - /* Map model to widgets */ + // Map model to widgets mapper->addMapping(bitcoin_at_startup, OptionsModel::StartAtStartup); mapper->addMapping(minimize_to_tray, OptionsModel::MinimizeToTray); mapper->addMapping(map_port_upnp, OptionsModel::MapPortUPnP); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 1528fdf697..3788f9fd7c 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -75,7 +75,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in break; case ProxyIP: { - /* Use CAddress to parse IP */ + // Use CAddress to parse and check IP CAddress addr(value.toString().toStdString() + ":1"); if (addr.ip != INADDR_NONE) { @@ -111,7 +111,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in } else { - successful = false; /* parse error */ + successful = false; // Parse error } } break; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 1c11944e2c..01072c42e1 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -25,7 +25,7 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : GUIUtil::setupAddressWidget(ui->payTo, this); GUIUtil::setupAmountWidget(ui->payAmount, this); - /* Set initial address if provided */ + // Set initial send-to address if provided if(!address.isEmpty()) { ui->payTo->setText(address); @@ -94,7 +94,7 @@ void SendCoinsDialog::on_sendButton_clicked() void SendCoinsDialog::on_pasteButton_clicked() { - /* Paste text from clipboard into recipient field */ + // Paste text from clipboard into recipient field ui->payTo->setText(QApplication::clipboard()->text()); } diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index a9e55d5a6a..9120f19c7a 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -6,9 +6,10 @@ #include -/* Taken straight from ui.cpp - TODO: Convert to use QStrings, Qt::Escape and tr() - */ +// Taken straight from ui.cpp +// TODO: Convert to use QStrings, Qt::Escape and tr() +// or: refactor and put describeAsHTML() into bitcoin core but that is unneccesary with better +// UI<->core API, no need to put display logic in core. using namespace std; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 6c1f3a5e58..34f30d53d5 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -26,7 +26,8 @@ bool TransactionRecord::showTransaction(const CWalletTx &wtx) return true; } -/* Decompose CWallet transaction to model transaction records. +/* + * Decompose CWallet transaction to model transaction records. */ QList TransactionRecord::decomposeTransaction(const CWalletTx &wtx) { diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index d2cdd4a623..bed08d7802 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -17,7 +17,7 @@ const QString TransactionTableModel::Sent = "s"; const QString TransactionTableModel::Received = "r"; const QString TransactionTableModel::Other = "o"; -/* Comparison operator for sort/binary search of model tx list */ +// Comparison operator for sort/binary search of model tx list struct TxLessThan { bool operator()(const TransactionRecord &a, const TransactionRecord &b) const @@ -34,7 +34,7 @@ struct TxLessThan } }; -/* Private implementation */ +// Private implementation struct TransactionTablePriv { TransactionTablePriv(TransactionTableModel *parent): @@ -50,13 +50,13 @@ struct TransactionTablePriv */ QList cachedWallet; + /* Query entire wallet anew from core. + */ void refreshWallet() { #ifdef WALLET_UPDATE_DEBUG qDebug() << "refreshWallet"; #endif - /* Query entire wallet from core. - */ cachedWallet.clear(); CRITICAL_BLOCK(cs_mapWallet) { @@ -67,20 +67,20 @@ struct TransactionTablePriv } } - /* Update our model of the wallet incrementally. + /* Update our model of the wallet incrementally, to synchronize our model of the wallet + with that of the core. + Call with list of hashes of transactions that were added, removed or changed. */ void updateWallet(const QList &updated) { - /* Walk through updated transactions, update model as needed. - */ + // Walk through updated transactions, update model as needed. #ifdef WALLET_UPDATE_DEBUG qDebug() << "updateWallet"; #endif - /* Sort update list, and iterate through it in reverse, so that model updates - can be emitted from end to beginning (so that earlier updates will not influence - the indices of latter ones). - */ + // Sort update list, and iterate through it in reverse, so that model updates + // can be emitted from end to beginning (so that earlier updates will not influence + // the indices of latter ones). QList updated_sorted = updated; qSort(updated_sorted); @@ -113,7 +113,7 @@ struct TransactionTablePriv if(inWallet && !inModel) { - /* Added -- insert at the right position */ + // Added -- insert at the right position QList toInsert = TransactionRecord::decomposeTransaction(mi->second); if(!toInsert.isEmpty()) /* only if something to insert */ @@ -130,14 +130,14 @@ struct TransactionTablePriv } else if(!inWallet && inModel) { - /* Removed -- remove entire transaction from table */ + // Removed -- remove entire transaction from table parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); cachedWallet.erase(lower, upper); parent->endRemoveRows(); } else if(inWallet && inModel) { - /* Updated -- nothing to do, status update will take care of this */ + // Updated -- nothing to do, status update will take care of this } } } @@ -154,10 +154,9 @@ struct TransactionTablePriv { TransactionRecord *rec = &cachedWallet[idx]; - /* If a status update is needed (blocks came in since last check), - update the status of this transaction from the wallet. Otherwise, - simply re-use the cached status. - */ + // If a status update is needed (blocks came in since last check), + // update the status of this transaction from the wallet. Otherwise, + // simply re-use the cached status. if(rec->statusUpdateNeeded()) { CRITICAL_BLOCK(cs_mapWallet) @@ -193,7 +192,7 @@ struct TransactionTablePriv }; -/* Credit and Debit columns are right-aligned as they contain numbers */ +// Credit and Debit columns are right-aligned as they contain numbers static int column_alignments[] = { Qt::AlignLeft|Qt::AlignVCenter, Qt::AlignLeft|Qt::AlignVCenter, @@ -225,7 +224,7 @@ void TransactionTableModel::update() { QList updated; - /* Check if there are changes to wallet map */ + // Check if there are changes to wallet map TRY_CRITICAL_BLOCK(cs_mapWallet) { if(!vWalletUpdated.empty()) @@ -242,9 +241,8 @@ void TransactionTableModel::update() { priv->updateWallet(updated); - /* Status (number of confirmations) and (possibly) description - columns changed for all rows. - */ + // Status (number of confirmations) and (possibly) description + // columns changed for all rows. emit dataChanged(index(0, Status), index(priv->size()-1, Status)); emit dataChanged(index(0, Description), index(priv->size()-1, Description)); } @@ -442,11 +440,9 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const } else if(role == Qt::DisplayRole) { - /* Delegate to specific column handlers */ + // Delegate to specific column handlers switch(index.column()) { - //case Status: - // return formatTxStatus(rec); case Date: return formatTxDate(rec); case Description: @@ -459,7 +455,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const } else if(role == Qt::EditRole) { - /* Edit role is used for sorting so return the real values */ + // Edit role is used for sorting so return the real values switch(index.column()) { case Status: -- cgit v1.2.3 From 7df70c000a5f687953335a5ab397ab4c74a40dd4 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 18 Jun 2011 13:13:48 +0200 Subject: Prevent notification balloon-spam on initial block download, const-correctness in client model --- src/qt/bitcoingui.cpp | 3 ++- src/qt/clientmodel.cpp | 15 ++++++++++----- src/qt/clientmodel.h | 13 ++++++++----- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 4d3efe26db..028882f49d 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -441,9 +441,10 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int .data(Qt::EditRole).toULongLong(); qint64 debit = ttm->index(start, TransactionTableModel::Debit, parent) .data(Qt::EditRole).toULongLong(); - if((credit+debit)>0) + if((credit+debit)>0 && !model->inInitialBlockDownload()) { // On incoming transaction, make an info balloon + // Unless the initial block download is in progress, to prevent balloon-spam QString date = ttm->index(start, TransactionTableModel::Date, parent) .data().toString(); QString description = ttm->index(start, TransactionTableModel::Description, parent) diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 10cafaf1af..822c03d949 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -22,12 +22,12 @@ ClientModel::ClientModel(QObject *parent) : transactionTableModel = new TransactionTableModel(this); } -qint64 ClientModel::getBalance() +qint64 ClientModel::getBalance() const { return GetBalance(); } -QString ClientModel::getAddress() +QString ClientModel::getAddress() const { std::vector vchPubKey; if (CWalletDB("r").ReadDefaultKey(vchPubKey)) @@ -40,17 +40,17 @@ QString ClientModel::getAddress() } } -int ClientModel::getNumConnections() +int ClientModel::getNumConnections() const { return vNodes.size(); } -int ClientModel::getNumBlocks() +int ClientModel::getNumBlocks() const { return nBestHeight; } -int ClientModel::getNumTransactions() +int ClientModel::getNumTransactions() const { int numTransactions = 0; CRITICAL_BLOCK(cs_mapWallet) @@ -138,6 +138,11 @@ ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payA return OK; } +bool ClientModel::inInitialBlockDownload() const +{ + return IsInitialBlockDownload(); +} + OptionsModel *ClientModel::getOptionsModel() { return optionsModel; diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 09d1fc921e..f5f12fcfd9 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -28,11 +28,14 @@ public: AddressTableModel *getAddressTableModel(); TransactionTableModel *getTransactionTableModel(); - qint64 getBalance(); - QString getAddress(); - int getNumConnections(); - int getNumBlocks(); - int getNumTransactions(); + qint64 getBalance() const; + QString getAddress() const; + int getNumConnections() const; + int getNumBlocks() const; + int getNumTransactions() const; + + /* Return true if core is doing initial block download */ + bool inInitialBlockDownload() const; /* Set default address */ void setAddress(const QString &defaultAddress); -- cgit v1.2.3 From 45c4a0b3545b74832007a486ca9b605005977338 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 18 Jun 2011 14:25:24 +0200 Subject: Use explicit resource initialization, apparently needed on some platforms --- src/qt/bitcoin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index cb24c1901d..917355e764 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -96,6 +96,7 @@ std::string _(const char* psz) int main(int argc, char *argv[]) { + Q_INIT_RESOURCE(bitcoin); QApplication app(argc, argv); app.setQuitOnLastWindowClosed(false); -- cgit v1.2.3 From 245ab4d0ac50095712abd6518b2fc6945d5e24a9 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 18 Jun 2011 17:26:31 +0200 Subject: add configure and receive icon --- doc/assets-attribution.txt | 25 +++++++++++++++++++++---- src/qt/bitcoin.qrc | 2 ++ src/qt/bitcoingui.cpp | 2 +- src/qt/res/icons/configure.png | Bin 0 -> 1055 bytes src/qt/res/icons/receive.png | Bin 0 -> 716 bytes 5 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 src/qt/res/icons/configure.png create mode 100644 src/qt/res/icons/receive.png diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index 820183ee93..623a5027ca 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -6,22 +6,39 @@ Icon: src/qt/res/icons/send.png Icon Pack: Vista Style Arrow Designer: Icons Land License: Freeware Non-commercial -http://findicons.com/icon/231371/right3green +Site: http://findicons.com/icon/231371/right3green Icon: src/qt/res/icons/address-book.png Icon Pack: Farm-Fresh Web Designer: FatCow Web Hosting License: Creative Commons Attribution (by) -http://findicons.com/icon/163938/book_open +Site: http://findicons.com/icon/163938/book_open Icon: src/qt/res/icons/connect*.png Icon Pack: Human-O2 Designer: schollidesign License: GNU/GPL -http://findicons.com/icon/93743/blocks_gnome_netstatus_0 +Site: http://findicons.com/icon/93743/blocks_gnome_netstatus_0 Icon: src/qt/res/icons/transaction*.png Designer: md2k7 -https://forum.bitcoin.org/index.php?topic=15276.0 +Site: https://forum.bitcoin.org/index.php?topic=15276.0 License: You are free to do with these icons as you wish, including selling, copying, modifying etc. + +Icon: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png +Designer: http://www.everaldo.com +Icon Pack: Crystal SVG +License: LGPL + +Icon: src/qt/res/icons/receive.png +Designer: Oxygen team +Icon Pack: Oxygen +License: Creative Common Attribution-ShareAlike 3.0 License or LGPL +Site: http://www.oxygen-icons.org/ + +Icon: src/qt/res/icons/bitcoin.png, src/qt/res/icons/toolbar.png +Designer: Bitboy (optimized for 16x16 by Wladimir van der Laan) +License: Public Domain +Site: http://forum.bitcoin.org/?topic=1756.0 + diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index ec64770cbe..3d4a3e163b 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -17,6 +17,8 @@ res/icons/clock3.png res/icons/clock4.png res/icons/clock5.png + res/icons/configure.png + res/icons/receive.png res/images/about.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 028882f49d..f487da709f 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -145,7 +145,7 @@ void BitcoinGUI::createActions() addressbook->setToolTip(tr("Edit the list of stored addresses and labels")); about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); about->setToolTip(tr("Show information about Bitcoin")); - receivingAddresses = new QAction(QIcon(":/icons/receiving-addresses"), tr("Your &Receiving Addresses..."), this); + receivingAddresses = new QAction(QIcon(":/icons/receiving_addresses"), tr("Your &Receiving Addresses..."), this); receivingAddresses->setToolTip(tr("Show the list of receiving addresses and edit their labels")); options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); options->setToolTip(tr("Modify configuration options for bitcoin")); diff --git a/src/qt/res/icons/configure.png b/src/qt/res/icons/configure.png new file mode 100644 index 0000000000..95bd319ce1 Binary files /dev/null and b/src/qt/res/icons/configure.png differ diff --git a/src/qt/res/icons/receive.png b/src/qt/res/icons/receive.png new file mode 100644 index 0000000000..ea9eac679f Binary files /dev/null and b/src/qt/res/icons/receive.png differ -- cgit v1.2.3 From 0eeb4f5d5b4c6b634dea4a5ec5b2e6c322c9230e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 18 Jun 2011 18:46:01 +0200 Subject: update bitcoin core from git (eeac8727bc0a951631bd) --- src/db.cpp | 3 +++ src/headers.h | 30 +----------------------------- src/init.cpp | 7 +++++++ src/irc.cpp | 3 +++ src/main.cpp | 4 ++++ src/main.h | 10 ++++++++++ src/net.cpp | 5 +++++ src/net.h | 1 + src/rpc.cpp | 5 +++++ src/util.cpp | 13 +++++++++++-- src/util.h | 1 - 11 files changed, 50 insertions(+), 32 deletions(-) diff --git a/src/db.cpp b/src/db.cpp index c2c239db2f..b67e2a6452 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -3,6 +3,9 @@ // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" +#include "db.h" +#include "net.h" +#include using namespace std; using namespace boost; diff --git a/src/headers.h b/src/headers.h index 33aeef3383..f682f746ed 100644 --- a/src/headers.h +++ b/src/headers.h @@ -48,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -56,29 +55,8 @@ #include #include #include -#include -#include -#include + #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #ifdef __WXMSW__ #include @@ -110,7 +88,6 @@ #pragma hdrstop -#include "strlcpy.h" #include "serialize.h" #include "uint256.h" #include "util.h" @@ -118,18 +95,13 @@ #include "bignum.h" #include "base58.h" #include "script.h" -#include "db.h" -#include "net.h" -#include "irc.h" #include "main.h" -#include "rpc.h" #ifdef GUI #include "uibase.h" #include "ui.h" #else #include "externui.h" #endif -#include "init.h" #ifdef GUI #include "xpm/addressbook16.xpm" diff --git a/src/init.cpp b/src/init.cpp index 51fdf11b69..013fb6bda2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2,6 +2,13 @@ // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" +#include "db.h" +#include "rpc.h" +#include "net.h" +#include "init.h" +#include "strlcpy.h" +#include +#include using namespace std; using namespace boost; diff --git a/src/irc.cpp b/src/irc.cpp index a76374d143..cde934e80c 100644 --- a/src/irc.cpp +++ b/src/irc.cpp @@ -3,6 +3,9 @@ // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" +#include "irc.h" +#include "net.h" +#include "strlcpy.h" using namespace std; using namespace boost; diff --git a/src/main.cpp b/src/main.cpp index 0456041ee5..108842f3a1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,7 +2,11 @@ // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" +#include "db.h" +#include "net.h" +#include "init.h" #include "cryptopp/sha.h" +#include using namespace std; using namespace boost; diff --git a/src/main.h b/src/main.h index 436ffbecbd..7aa6d41c30 100644 --- a/src/main.h +++ b/src/main.h @@ -24,6 +24,13 @@ class CBlockIndex; class CWalletTx; class CKeyItem; +class CMessageHeader; +class CAddress; +class CInv; +class CRequestTracker; +class CNode; +class CBlockIndex; + static const unsigned int MAX_BLOCK_SIZE = 1000000; static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; @@ -78,6 +85,9 @@ extern int fUseUPnP; +class CReserveKey; +class CTxDB; +class CTxIndex; bool CheckDiskSpace(uint64 nAdditionalBytes=0); FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); diff --git a/src/net.cpp b/src/net.cpp index ca6380fc76..8b439efdca 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -3,6 +3,11 @@ // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" +#include "irc.h" +#include "db.h" +#include "net.h" +#include "init.h" +#include "strlcpy.h" #ifdef USE_UPNP #include diff --git a/src/net.h b/src/net.h index 8a55856eed..cafb175d94 100644 --- a/src/net.h +++ b/src/net.h @@ -6,6 +6,7 @@ #include #include +#include #include #ifndef __WXMSW__ diff --git a/src/rpc.cpp b/src/rpc.cpp index ca88bec91b..bc9b224518 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -4,12 +4,17 @@ #include "headers.h" #include "cryptopp/sha.h" +#include "db.h" +#include "net.h" +#include "init.h" #undef printf #include #include #include +#include #ifdef USE_SSL #include +#include typedef boost::asio::ssl::stream SSLStream; #endif #include "json/json_spirit_reader_template.h" diff --git a/src/util.cpp b/src/util.cpp index fd4a9e459c..2c1efc4042 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -2,6 +2,13 @@ // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" +#include "strlcpy.h" +#include +#include +#include +#include +#include +#include using namespace std; using namespace boost; @@ -893,8 +900,10 @@ string FormatVersion(int nVersion) string FormatFullVersion() { string s = FormatVersion(VERSION) + pszSubVer; - if (VERSION_IS_BETA) - s += _("-beta"); + if (VERSION_IS_BETA) { + s += "-"; + s += _("beta"); + } return s; } diff --git a/src/util.h b/src/util.h index 4759e8c1ce..a1f1694929 100644 --- a/src/util.h +++ b/src/util.h @@ -15,7 +15,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From 6cab66354d5523ec3f977cfe8c4028a4c42a693d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 18 Jun 2011 21:25:38 +0200 Subject: On initial block chain download, show a progress bar --- src/main.cpp | 16 +++++++++++++++- src/main.h | 1 + src/qt/bitcoingui.cpp | 26 +++++++++++++++++++++++++- src/qt/bitcoingui.h | 3 +++ src/qt/clientmodel.cpp | 6 ++++++ src/qt/clientmodel.h | 2 ++ 6 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 108842f3a1..5a8cc24caf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,6 +25,7 @@ map mapNextTx; map mapBlockIndex; uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); +const int nTotalBlocksEstimate = 131000; // Conservative estimate of total nr of blocks on main chain CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; CBigNum bnBestChainWork = 0; @@ -1156,9 +1157,22 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits) return true; } +// Return conservative estimate of total number of blocks, 0 if unknown +int GetTotalBlocksEstimate() +{ + if(fTestNet) + { + return 0; + } + else + { + return nTotalBlocksEstimate; + } +} + bool IsInitialBlockDownload() { - if (pindexBest == NULL || (!fTestNet && nBestHeight < 118000)) + if (pindexBest == NULL || nBestHeight < GetTotalBlocksEstimate()) return true; static int64 nLastUpdate; static CBlockIndex* pindexLastBest; diff --git a/src/main.h b/src/main.h index 7aa6d41c30..73935bcee2 100644 --- a/src/main.h +++ b/src/main.h @@ -118,6 +118,7 @@ void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash bool CheckWork(CBlock* pblock, CReserveKey& reservekey); void BitcoinMiner(); bool CheckProofOfWork(uint256 hash, unsigned int nBits); +int GetTotalBlocksEstimate(); bool IsInitialBlockDownload(); std::string GetWarnings(std::string strFor); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index f487da709f..2dfcd40df3 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -124,10 +125,19 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): labelTransactions->setMinimumWidth(130); labelTransactions->setToolTip(tr("Number of transactions in your wallet")); + // Progress bar for blocks download + progressBarLabel = new QLabel(tr("Downloading initial data...")); + progressBarLabel->setVisible(false); + progressBar = new QProgressBar(); + progressBar->setToolTip(tr("Initial block chain download in progress")); + progressBar->setVisible(false); + + statusBar()->addWidget(progressBarLabel); + statusBar()->addWidget(progressBar); statusBar()->addPermanentWidget(labelConnections); statusBar()->addPermanentWidget(labelBlocks); statusBar()->addPermanentWidget(labelTransactions); - + // Action bindings connect(button_new, SIGNAL(clicked()), this, SLOT(newAddressClicked())); connect(button_clipboard, SIGNAL(clicked()), this, SLOT(copyClipboardClicked())); @@ -360,6 +370,20 @@ void BitcoinGUI::setNumConnections(int count) void BitcoinGUI::setNumBlocks(int count) { + int total = model->getTotalBlocksEstimate(); + if(count < total) + { + progressBarLabel->setVisible(true); + progressBar->setVisible(true); + progressBar->setMaximum(total); + progressBar->setValue(count); + } + else + { + progressBarLabel->setVisible(false); + progressBar->setVisible(false); + } + labelBlocks->setText(QLocale::system().toString(count)+" "+tr("block(s)", "", count)); } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index e1b3ef1783..b3559c3bcd 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -13,6 +13,7 @@ class QLineEdit; class QTableView; class QAbstractItemModel; class QModelIndex; +class QProgressBar; QT_END_NAMESPACE class BitcoinGUI : public QMainWindow @@ -43,6 +44,8 @@ private: QLabel *labelConnectionsIcon; QLabel *labelBlocks; QLabel *labelTransactions; + QLabel *progressBarLabel; + QProgressBar *progressBar; QAction *quit; QAction *sendcoins; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 822c03d949..86fc8b32d3 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -143,6 +143,12 @@ bool ClientModel::inInitialBlockDownload() const return IsInitialBlockDownload(); } +int ClientModel::getTotalBlocksEstimate() const +{ + return GetTotalBlocksEstimate(); +} + + OptionsModel *ClientModel::getOptionsModel() { return optionsModel; diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index f5f12fcfd9..7141937441 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -36,6 +36,8 @@ public: /* Return true if core is doing initial block download */ bool inInitialBlockDownload() const; + /* Return conservative estimate of total number of blocks, or 0 if unknown */ + int getTotalBlocksEstimate() const; /* Set default address */ void setAddress(const QString &defaultAddress); -- cgit v1.2.3 From 0f9ee792d9831732c567cb8228aa229836dd139a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 19 Jun 2011 00:15:28 +0200 Subject: initial block download spans total blocks minus threshold --- src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 5a8cc24caf..61426a3ef9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,6 +26,7 @@ map mapBlockIndex; uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); const int nTotalBlocksEstimate = 131000; // Conservative estimate of total nr of blocks on main chain +const int nInitialBlockThreshold = 10000; // Regard blocks up until N-threshold as "initial download" CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; CBigNum bnBestChainWork = 0; @@ -1172,7 +1173,7 @@ int GetTotalBlocksEstimate() bool IsInitialBlockDownload() { - if (pindexBest == NULL || nBestHeight < GetTotalBlocksEstimate()) + if (pindexBest == NULL || nBestHeight < (GetTotalBlocksEstimate()-nInitialBlockThreshold)) return true; static int64 nLastUpdate; static CBlockIndex* pindexLastBest; -- cgit v1.2.3 From 3c7ebaedcd871d03033651b1ba3f4f2333e7d69b Mon Sep 17 00:00:00 2001 From: mark Date: Mon, 20 Jun 2011 07:30:54 -0400 Subject: fixes for mac build --- bitcoin-qt.pro | 3 ++- src/db.cpp | 1 + src/init.cpp | 1 + src/main.cpp | 1 + src/util.cpp | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index f3299554d6..27703592eb 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -5,7 +5,8 @@ INCLUDEPATH += src src/json src/cryptopp src/qt # for boost 1.37, add -mt to the boost libraries unix:LIBS += -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx -macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 +macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 BOOST_FILESYSTEM_VERSION=3 +macx:LIBS += -lboost_thread-mt # disable quite some warnings because bitcoin core "sins" a lot QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wno-invalid-offsetof -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare -Wno-char-subscripts -Wno-unused-value -Wno-sequence-point -Wno-parentheses -Wno-unknown-pragmas -Wno-switch diff --git a/src/db.cpp b/src/db.cpp index b67e2a6452..a7fb4bd6ea 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -5,6 +5,7 @@ #include "headers.h" #include "db.h" #include "net.h" +#include #include using namespace std; diff --git a/src/init.cpp b/src/init.cpp index 013fb6bda2..f306185317 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -7,6 +7,7 @@ #include "net.h" #include "init.h" #include "strlcpy.h" +#include #include #include diff --git a/src/main.cpp b/src/main.cpp index 61426a3ef9..2dbbd674a7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include "net.h" #include "init.h" #include "cryptopp/sha.h" +#include #include using namespace std; diff --git a/src/util.cpp b/src/util.cpp index 2c1efc4042..688605ef5c 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -5,6 +5,7 @@ #include "strlcpy.h" #include #include +#include #include #include #include -- cgit v1.2.3 From 18b99e3f69e039f8f431e49c3d1e6c9f96fe3896 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 20 Jun 2011 21:31:06 +0200 Subject: number of confirmations is no longer magic value --- src/qt/transactionrecord.cpp | 2 +- src/qt/transactionrecord.h | 3 +++ src/qt/transactiontablemodel.cpp | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 34f30d53d5..2f00fa8752 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -211,7 +211,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) { status.status = TransactionStatus::Offline; } - else if (status.depth < 6) + else if (status.depth < NumConfirmations) { status.status = TransactionStatus::Unconfirmed; } diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index c082fffe9c..a7f6537b3f 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -59,6 +59,9 @@ public: SendToSelf }; + /* Number of confirmation needed for transaction */ + static const int NumConfirmations = 6; + TransactionRecord(): hash(), time(0), type(Other), address(""), debit(0), credit(0), idx(0) { diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index bed08d7802..e25ee1d1da 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -276,7 +276,7 @@ QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) con status = tr("Offline (%1)").arg(wtx->status.depth); break; case TransactionStatus::Unconfirmed: - status = tr("Unconfirmed (%1)").arg(wtx->status.depth); + status = tr("Unconfirmed (%1/%2)").arg(wtx->status.depth).arg(TransactionRecord::NumConfirmations); break; case TransactionStatus::HaveConfirmations: status = tr("Confirmed (%1)").arg(wtx->status.depth); -- cgit v1.2.3 From f193c57a63d8e66835873ff05ef8028fa87b427f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 20 Jun 2011 21:31:42 +0200 Subject: introduce bitcoin amount field with split amount/decimals, to protect against mistakes (https://forum.bitcoin.org/index.php?topic=19168.0) --- bitcoin-qt.pro | 6 ++-- src/qt/bitcoinamountfield.cpp | 67 +++++++++++++++++++++++++++++++++++++++++ src/qt/bitcoinamountfield.h | 33 ++++++++++++++++++++ src/qt/forms/sendcoinsdialog.ui | 39 ++++++++++++++---------- src/qt/optionsdialog.cpp | 8 ++--- src/qt/sendcoinsdialog.cpp | 1 - 6 files changed, 130 insertions(+), 24 deletions(-) create mode 100644 src/qt/bitcoinamountfield.cpp create mode 100644 src/qt/bitcoinamountfield.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 27703592eb..501695bd05 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -68,7 +68,8 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/monitoreddatamapper.h \ src/externui.h \ src/qt/transactiondesc.h \ - src/qt/transactiondescdialog.h + src/qt/transactiondescdialog.h \ + src/qt/bitcoinamountfield.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -98,7 +99,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/monitoreddatamapper.cpp \ src/qt/transactiondesc.cpp \ src/qt/transactiondescdialog.cpp \ - src/qt/bitcoinstrings.cpp + src/qt/bitcoinstrings.cpp \ + src/qt/bitcoinamountfield.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp new file mode 100644 index 0000000000..e475441f13 --- /dev/null +++ b/src/qt/bitcoinamountfield.cpp @@ -0,0 +1,67 @@ +#include "bitcoinamountfield.h" + +#include +#include +#include +#include +#include + +BitcoinAmountField::BitcoinAmountField(QWidget *parent): + QWidget(parent), amount(0), decimals(0) +{ + amount = new QLineEdit(this); + amount->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this)); + amount->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + amount->installEventFilter(this); + amount->setMaximumWidth(80); + decimals = new QLineEdit(this); + decimals->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this)); + decimals->setMaxLength(8); + decimals->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + decimals->setMaximumWidth(75); + + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setSpacing(0); + layout->addWidget(amount); + layout->addWidget(new QLabel(QString("."))); + layout->addWidget(decimals); + layout->addStretch(1); + + setFocusPolicy(Qt::TabFocus); + setLayout(layout); + setFocusProxy(amount); + + // If one if the widgets changes, the combined content changes as well + connect(amount, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged())); + connect(decimals, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged())); +} + +void BitcoinAmountField::setText(const QString &text) +{ + const QStringList parts = text.split(QString(".")); + if(parts.size() == 2) + { + amount->setText(parts[0]); + decimals->setText(parts[1]); + } +} + +QString BitcoinAmountField::text() const +{ + return amount->text() + QString(".") + decimals->text(); +} + +// Intercept '.' and ',' keys, if pressed focus a specified widget +bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event) +{ + Q_UNUSED(object); + if(event->type() == QEvent::KeyPress) + { + QKeyEvent *keyEvent = static_cast(event); + if(keyEvent->key() == Qt::Key_Period || keyEvent->key() == Qt::Key_Comma) + { + decimals->setFocus(); + } + } + return false; +} diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h new file mode 100644 index 0000000000..2171578eea --- /dev/null +++ b/src/qt/bitcoinamountfield.h @@ -0,0 +1,33 @@ +#ifndef BITCOINFIELD_H +#define BITCOINFIELD_H + +#include + +QT_BEGIN_NAMESPACE +class QLineEdit; +QT_END_NAMESPACE + +class BitcoinAmountField: public QWidget +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged USER true); +public: + explicit BitcoinAmountField(QWidget *parent = 0); + + void setText(const QString &text); + QString text() const; + +signals: + void textChanged(); + +protected: + // Intercept '.' and ',' keys, if pressed focus a specified widget + bool eventFilter(QObject *object, QEvent *event); + +private: + QLineEdit *amount; + QLineEdit *decimals; +}; + + +#endif // BITCOINFIELD_H diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 595b7f40ff..3aaec1b53c 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -52,22 +52,6 @@ - - - - - 145 - 16777215 - - - - Amount of bitcoins to send (e.g. 0.05) - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - @@ -106,6 +90,13 @@ + + + + Qt::TabFocus + + + @@ -173,6 +164,22 @@ + + + BitcoinAmountField + QWidget +
bitcoinamountfield.h
+ 1 +
+
+ + payTo + payAmount + pasteButton + addressBookButton + sendButton + buttonBox + diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 1bf123b285..1b5b2fefeb 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -1,5 +1,6 @@ #include "optionsdialog.h" #include "optionsmodel.h" +#include "bitcoinamountfield.h" #include "monitoreddatamapper.h" #include "guiutil.h" @@ -31,7 +32,7 @@ private: QCheckBox *connect_socks4; QLineEdit *proxy_ip; QLineEdit *proxy_port; - QLineEdit *fee_edit; + BitcoinAmountField *fee_edit; signals: @@ -195,12 +196,9 @@ MainOptionsPage::MainOptionsPage(QWidget *parent): fee_hbox->addSpacing(18); QLabel *fee_label = new QLabel(tr("Pay transaction &fee")); fee_hbox->addWidget(fee_label); - fee_edit = new QLineEdit(); - fee_edit->setMaximumWidth(100); + fee_edit = new BitcoinAmountField(); fee_edit->setToolTip(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended.")); - GUIUtil::setupAmountWidget(fee_edit, this); - fee_label->setBuddy(fee_edit); fee_hbox->addWidget(fee_edit); fee_hbox->addStretch(1); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 01072c42e1..5f9ee18a37 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -23,7 +23,6 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : ui->setupUi(this); GUIUtil::setupAddressWidget(ui->payTo, this); - GUIUtil::setupAmountWidget(ui->payAmount, this); // Set initial send-to address if provided if(!address.isEmpty()) -- cgit v1.2.3 From 84114e341df1db77fb099ef58b7547f5b2b8be57 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 21 Jun 2011 07:35:59 +0200 Subject: Fix some padding and focus issues with the new BitcoinAmountWidget --- src/qt/bitcoinamountfield.cpp | 1 + src/qt/bitcoinamountfield.h | 2 ++ src/qt/forms/sendcoinsdialog.ui | 6 +----- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index e475441f13..19fa230183 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -26,6 +26,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): layout->addWidget(new QLabel(QString("."))); layout->addWidget(decimals); layout->addStretch(1); + layout->setContentsMargins(0,0,0,0); setFocusPolicy(Qt::TabFocus); setLayout(layout); diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 2171578eea..67304c8b3a 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -7,6 +7,8 @@ QT_BEGIN_NAMESPACE class QLineEdit; QT_END_NAMESPACE +// Coin amount entry widget with separate parts for whole +// coins and decimals. class BitcoinAmountField: public QWidget { Q_OBJECT diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 3aaec1b53c..b88839a7ca 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -91,11 +91,7 @@ - - - Qt::TabFocus - - + -- cgit v1.2.3 From 54e903a543454a28260090e3a2142e3a56300e4c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 21 Jun 2011 07:56:44 +0200 Subject: Add comment to tooltip that only sending addresses can be deleted --- src/qt/forms/addressbookdialog.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/forms/addressbookdialog.ui b/src/qt/forms/addressbookdialog.ui index 2fc6ca785c..f9b95c40c5 100644 --- a/src/qt/forms/addressbookdialog.ui +++ b/src/qt/forms/addressbookdialog.ui @@ -134,7 +134,7 @@ - Delete the currently selected address from the list + Delete the currently selected address from the list. Only sending addresses can be deleted. &Delete -- cgit v1.2.3 From 50d08dc1e1a04b386e3863b229764fe8763799fb Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 21 Jun 2011 15:33:10 +0200 Subject: fix issue #7 --- src/qt/addressbookdialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/addressbookdialog.cpp b/src/qt/addressbookdialog.cpp index 9126a645e1..4f99a3c7ea 100644 --- a/src/qt/addressbookdialog.cpp +++ b/src/qt/addressbookdialog.cpp @@ -69,6 +69,7 @@ void AddressBookDialog::setModel(AddressTableModel *model) void AddressBookDialog::setTab(int tab) { ui->tabWidget->setCurrentIndex(tab); + on_tabWidget_currentChanged(tab); } QTableView *AddressBookDialog::getCurrentTable() -- cgit v1.2.3 From c92fc340a2a1fa019a1f392e97c3220aa738441b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 21 Jun 2011 19:14:35 +0200 Subject: when going to decimals field using ./, select it all, so that entry starts from scratch instead of appending to previous value --- src/qt/bitcoinamountfield.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 19fa230183..935bd13a44 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -62,6 +62,7 @@ bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event) if(keyEvent->key() == Qt::Key_Period || keyEvent->key() == Qt::Key_Comma) { decimals->setFocus(); + decimals->selectAll(); } } return false; -- cgit v1.2.3 From f5927f5b32c9e28033fb7a00dd43f3162781c1d0 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 21 Jun 2011 19:54:09 +0200 Subject: highlight default address --- src/qt/addresstablemodel.cpp | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 2d69df3da0..0558bfa453 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -3,6 +3,7 @@ #include "main.h" #include +#include const QString AddressTableModel::Send = "S"; const QString AddressTableModel::Receive = "R"; @@ -21,6 +22,16 @@ struct AddressTableEntry AddressTableEntry() {} AddressTableEntry(Type type, const QString &label, const QString &address): type(type), label(label), address(address) {} + + bool isDefaultAddress() const + { + std::vector vchPubKey; + if (CWalletDB("r").ReadDefaultKey(vchPubKey)) + { + return address == QString::fromStdString(PubKeyToAddress(vchPubKey)); + } + return false; + } }; // Private implementation @@ -110,9 +121,30 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const } else if (role == Qt::FontRole) { + QFont font; if(index.column() == Address) { - return GUIUtil::bitcoinAddressFont(); + font = GUIUtil::bitcoinAddressFont(); + } + if(rec->isDefaultAddress()) + { + font.setBold(true); + } + return font; + } + else if (role == Qt::ForegroundRole) + { + // Show default address in alternative color + if(rec->isDefaultAddress()) + { + return QColor(0,0,255); + } + } + else if (role == Qt::ToolTipRole) + { + if(rec->isDefaultAddress()) + { + return tr("Default receiving address"); } } else if (role == TypeRole) -- cgit v1.2.3 From b9e80983a5c076fed655a7c3c67b53bd9ecc3dda Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 21 Jun 2011 20:34:43 +0200 Subject: Allow changing default address (fixes issue #6) --- src/qt/addresstablemodel.cpp | 46 +++++++++++++++++++++++++++++++++++++-- src/qt/addresstablemodel.h | 14 +++++++++--- src/qt/bitcoingui.cpp | 10 +++------ src/qt/clientmodel.cpp | 28 +++--------------------- src/qt/clientmodel.h | 4 ---- src/qt/editaddressdialog.cpp | 7 +++++- src/qt/forms/editaddressdialog.ui | 7 ++++++ 7 files changed, 74 insertions(+), 42 deletions(-) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 0558bfa453..1cd82b7626 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -117,6 +117,8 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const return rec->label; case Address: return rec->address; + case IsDefaultAddress: + return rec->isDefaultAddress(); } } else if (role == Qt::FontRole) @@ -187,6 +189,12 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu rec->address = value.toString(); } break; + case IsDefaultAddress: + if(value.toBool()) + { + setDefaultAddress(rec->address); + } + break; } emit dataChanged(index, index); @@ -229,7 +237,7 @@ void AddressTableModel::updateList() endResetModel(); } -QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address) +QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address, bool setAsDefault) { std::string strLabel = label.toStdString(); std::string strAddress = address.toStdString(); @@ -247,8 +255,13 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con } else if(type == Receive) { - // Generate a new address to associate with given label + // Generate a new address to associate with given label, optionally + // set as default receiving address. strAddress = PubKeyToAddress(GetKeyFromKeyPool()); + if(setAsDefault) + { + setDefaultAddress(QString::fromStdString(strAddress)); + } } else { @@ -274,3 +287,32 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex & paren updateList(); return true; } + +QString AddressTableModel::getDefaultAddress() const +{ + std::vector vchPubKey; + if (CWalletDB("r").ReadDefaultKey(vchPubKey)) + { + return QString::fromStdString(PubKeyToAddress(vchPubKey)); + } + else + { + return QString(); + } +} + +void AddressTableModel::setDefaultAddress(const QString &defaultAddress) +{ + uint160 hash160; + std::string strAddress = defaultAddress.toStdString(); + if (!AddressToHash160(strAddress, hash160)) + return; + if (!mapPubKeys.count(hash160)) + return; + CWalletDB().WriteDefaultKey(mapPubKeys[hash160]); +} + +void AddressTableModel::update() +{ + emit defaultAddressChanged(getDefaultAddress()); +} diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 8799414334..32dd4d9f1d 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -15,7 +15,8 @@ public: enum ColumnIndex { Label = 0, /* User specified label */ - Address = 1 /* Bitcoin address */ + Address = 1, /* Bitcoin address */ + IsDefaultAddress = 2 /* Is default address? */ }; enum { @@ -37,18 +38,25 @@ public: /* Add an address to the model. Returns the added address on success, and an empty string otherwise. */ - QString addRow(const QString &type, const QString &label, const QString &address); + QString addRow(const QString &type, const QString &label, const QString &address, bool setAsDefault); + + /* Set and get default address */ + QString getDefaultAddress() const; + void setDefaultAddress(const QString &defaultAddress); /* Update address list from core. Invalidates any indices. */ void updateList(); + private: AddressTablePriv *priv; QStringList columns; + signals: + void defaultAddressChanged(const QString &address); public slots: - + void update(); }; #endif // ADDRESSTABLEMODEL_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 2dfcd40df3..bc986dcf9f 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -14,6 +14,7 @@ #include "editaddressdialog.h" #include "optionsmodel.h" #include "transactiondescdialog.h" +#include "addresstablemodel.h" #include "main.h" @@ -188,8 +189,8 @@ void BitcoinGUI::setModel(ClientModel *model) setNumBlocks(model->getNumBlocks()); connect(model, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int))); - setAddress(model->getAddress()); - connect(model, SIGNAL(addressChanged(QString)), this, SLOT(setAddress(QString))); + setAddress(model->getAddressTableModel()->getDefaultAddress()); + connect(model->getAddressTableModel(), SIGNAL(defaultAddressChanged(QString)), this, SLOT(setAddress(QString))); // Report errors from network/worker thread connect(model, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); @@ -329,11 +330,6 @@ void BitcoinGUI::newAddressClicked() if(dlg.exec()) { QString newAddress = dlg.saveCurrentRow(); - // Set returned address as new default addres - if(!newAddress.isEmpty()) - { - model->setAddress(newAddress); - } } } diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 86fc8b32d3..4e6a34c79f 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -27,19 +27,6 @@ qint64 ClientModel::getBalance() const return GetBalance(); } -QString ClientModel::getAddress() const -{ - std::vector vchPubKey; - if (CWalletDB("r").ReadDefaultKey(vchPubKey)) - { - return QString::fromStdString(PubKeyToAddress(vchPubKey)); - } - else - { - return QString(); - } -} - int ClientModel::getNumConnections() const { return vNodes.size(); @@ -63,23 +50,14 @@ int ClientModel::getNumTransactions() const void ClientModel::update() { // Plainly emit all signals for now. To be more efficient this should check - // whether the values actually changed first. + // whether the values actually changed first, although it'd be even better if these + // were events coming in from the bitcoin core. emit balanceChanged(getBalance()); - emit addressChanged(getAddress()); emit numConnectionsChanged(getNumConnections()); emit numBlocksChanged(getNumBlocks()); emit numTransactionsChanged(getNumTransactions()); -} -void ClientModel::setAddress(const QString &defaultAddress) -{ - uint160 hash160; - std::string strAddress = defaultAddress.toStdString(); - if (!AddressToHash160(strAddress, hash160)) - return; - if (!mapPubKeys.count(hash160)) - return; - CWalletDB().WriteDefaultKey(mapPubKeys[hash160]); + addressTableModel->update(); } ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payAmount) diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 7141937441..169ed8c4c9 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -29,7 +29,6 @@ public: TransactionTableModel *getTransactionTableModel(); qint64 getBalance() const; - QString getAddress() const; int getNumConnections() const; int getNumBlocks() const; int getNumTransactions() const; @@ -39,8 +38,6 @@ public: /* Return conservative estimate of total number of blocks, or 0 if unknown */ int getTotalBlocksEstimate() const; - /* Set default address */ - void setAddress(const QString &defaultAddress); /* Send coins */ StatusCode sendCoins(const QString &payTo, qint64 payAmount); private: @@ -50,7 +47,6 @@ private: signals: void balanceChanged(qint64 balance); - void addressChanged(const QString &address); void numConnectionsChanged(int count); void numBlocksChanged(int count); void numTransactionsChanged(int count); diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index dd0541760b..58ecb49486 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -19,9 +19,11 @@ EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : case NewReceivingAddress: setWindowTitle(tr("New receiving address")); ui->addressEdit->setEnabled(false); + ui->setAsDefault->setChecked(true); break; case NewSendingAddress: setWindowTitle(tr("New sending address")); + ui->setAsDefault->setVisible(false); break; case EditReceivingAddress: setWindowTitle(tr("Edit receiving address")); @@ -29,6 +31,7 @@ EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : break; case EditSendingAddress: setWindowTitle(tr("Edit sending address")); + ui->setAsDefault->setVisible(false); break; } @@ -47,6 +50,7 @@ void EditAddressDialog::setModel(AddressTableModel *model) mapper->setModel(model); mapper->addMapping(ui->labelEdit, AddressTableModel::Label); mapper->addMapping(ui->addressEdit, AddressTableModel::Address); + mapper->addMapping(ui->setAsDefault, AddressTableModel::IsDefaultAddress); } void EditAddressDialog::loadRow(int row) @@ -64,7 +68,8 @@ QString EditAddressDialog::saveCurrentRow() address = model->addRow( mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive, ui->labelEdit->text(), - ui->addressEdit->text()); + ui->addressEdit->text(), + ui->setAsDefault->isChecked()); if(address.isEmpty()) { QMessageBox::warning(this, windowTitle(), diff --git a/src/qt/forms/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui index f0ba28a854..ecad196429 100644 --- a/src/qt/forms/editaddressdialog.ui +++ b/src/qt/forms/editaddressdialog.ui @@ -53,6 +53,13 @@ + + + + Set as default address + + + -- cgit v1.2.3 From 5f280ff5578554750d53391b1e089cb891748c59 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 21 Jun 2011 20:39:37 +0200 Subject: clarify text --- src/qt/forms/editaddressdialog.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qt/forms/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui index ecad196429..95909fb964 100644 --- a/src/qt/forms/editaddressdialog.ui +++ b/src/qt/forms/editaddressdialog.ui @@ -7,7 +7,7 @@ 0 0 458 - 113 + 114 @@ -56,7 +56,7 @@ - Set as default address + Set as default receiving address -- cgit v1.2.3 From 47c6215c223ac85eeb31504aa2a56369bdfb3789 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 22 Jun 2011 20:11:12 +0200 Subject: use #ifdef QT_UI to distinguish Qt UI instead of hardcoded #if 0 --- bitcoin-qt.pro | 3 ++- src/externui.h | 46 ---------------------------------------------- src/headers.h | 6 +++++- src/init.cpp | 12 +++--------- src/qtui.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 57 deletions(-) delete mode 100644 src/externui.h create mode 100644 src/qtui.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 501695bd05..7e918f144f 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -2,6 +2,7 @@ TEMPLATE = app TARGET = DEPENDPATH += . INCLUDEPATH += src src/json src/cryptopp src/qt +DEFINES += QT_GUI # for boost 1.37, add -mt to the boost libraries unix:LIBS += -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx @@ -66,7 +67,7 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/guiconstants.h \ src/qt/optionsmodel.h \ src/qt/monitoreddatamapper.h \ - src/externui.h \ + src/qtui.h \ src/qt/transactiondesc.h \ src/qt/transactiondescdialog.h \ src/qt/bitcoinamountfield.h diff --git a/src/externui.h b/src/externui.h deleted file mode 100644 index 3243164c10..0000000000 --- a/src/externui.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_EXTERNUI_H -#define BITCOIN_EXTERNUI_H - -#include - -typedef void wxWindow; -#define wxYES 0x00000002 -#define wxOK 0x00000004 -#define wxNO 0x00000008 -#define wxYES_NO (wxYES|wxNO) -#define wxCANCEL 0x00000010 -#define wxAPPLY 0x00000020 -#define wxCLOSE 0x00000040 -#define wxOK_DEFAULT 0x00000000 -#define wxYES_DEFAULT 0x00000000 -#define wxNO_DEFAULT 0x00000080 -#define wxCANCEL_DEFAULT 0x80000000 -#define wxICON_EXCLAMATION 0x00000100 -#define wxICON_HAND 0x00000200 -#define wxICON_WARNING wxICON_EXCLAMATION -#define wxICON_ERROR wxICON_HAND -#define wxICON_QUESTION 0x00000400 -#define wxICON_INFORMATION 0x00000800 -#define wxICON_STOP wxICON_HAND -#define wxICON_ASTERISK wxICON_INFORMATION -#define wxICON_MASK (0x00000100|0x00000200|0x00000400|0x00000800) -#define wxFORWARD 0x00001000 -#define wxBACKWARD 0x00002000 -#define wxRESET 0x00004000 -#define wxHELP 0x00008000 -#define wxMORE 0x00010000 -#define wxSETUP 0x00020000 - -extern int MyMessageBox(const std::string& message, const std::string& caption="Message", int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1); -#define wxMessageBox MyMessageBox -extern int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1); -extern bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent); -extern void CalledSetStatusBar(const std::string& strText, int nField); -extern void UIThreadCall(boost::function0 fn); -extern void MainFrameRepaint(); -extern std::string _(const char* psz); - -#endif diff --git a/src/headers.h b/src/headers.h index f682f746ed..38d9566baa 100644 --- a/src/headers.h +++ b/src/headers.h @@ -100,7 +100,11 @@ #include "uibase.h" #include "ui.h" #else -#include "externui.h" +#ifdef QT_GUI +#include "qtui.h" +#else +#include "noui.h" +#endif #endif #ifdef GUI diff --git a/src/init.cpp b/src/init.cpp index f306185317..cbd9fc0219 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -75,8 +75,7 @@ void HandleSIGTERM(int) // // Start // -#if 0 -#ifndef GUI +#if !defined(QT_GUI) && !defined(GUI) int main(int argc, char* argv[]) { bool fRet = false; @@ -88,7 +87,6 @@ int main(int argc, char* argv[]) return 1; } #endif -#endif bool AppInit(int argc, char* argv[]) { @@ -228,10 +226,8 @@ bool AppInit2(int argc, char* argv[]) fServer = GetBoolArg("-server"); /* force fServer when running without GUI */ -#if 0 -#ifndef GUI +#if !defined(QT_GUI) && !defined(GUI) fServer = true; -#endif #endif fPrintToConsole = GetBoolArg("-printtoconsole"); fPrintToDebugger = GetBoolArg("-printtodebugger"); @@ -529,11 +525,9 @@ bool AppInit2(int argc, char* argv[]) SetStartOnSystemStartup(true); #endif -#if 0 -#ifndef GUI +#if !defined(QT_GUI) && !defined(GUI) while (1) Sleep(5000); -#endif #endif return true; diff --git a/src/qtui.h b/src/qtui.h new file mode 100644 index 0000000000..3243164c10 --- /dev/null +++ b/src/qtui.h @@ -0,0 +1,46 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_EXTERNUI_H +#define BITCOIN_EXTERNUI_H + +#include + +typedef void wxWindow; +#define wxYES 0x00000002 +#define wxOK 0x00000004 +#define wxNO 0x00000008 +#define wxYES_NO (wxYES|wxNO) +#define wxCANCEL 0x00000010 +#define wxAPPLY 0x00000020 +#define wxCLOSE 0x00000040 +#define wxOK_DEFAULT 0x00000000 +#define wxYES_DEFAULT 0x00000000 +#define wxNO_DEFAULT 0x00000080 +#define wxCANCEL_DEFAULT 0x80000000 +#define wxICON_EXCLAMATION 0x00000100 +#define wxICON_HAND 0x00000200 +#define wxICON_WARNING wxICON_EXCLAMATION +#define wxICON_ERROR wxICON_HAND +#define wxICON_QUESTION 0x00000400 +#define wxICON_INFORMATION 0x00000800 +#define wxICON_STOP wxICON_HAND +#define wxICON_ASTERISK wxICON_INFORMATION +#define wxICON_MASK (0x00000100|0x00000200|0x00000400|0x00000800) +#define wxFORWARD 0x00001000 +#define wxBACKWARD 0x00002000 +#define wxRESET 0x00004000 +#define wxHELP 0x00008000 +#define wxMORE 0x00010000 +#define wxSETUP 0x00020000 + +extern int MyMessageBox(const std::string& message, const std::string& caption="Message", int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1); +#define wxMessageBox MyMessageBox +extern int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1); +extern bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent); +extern void CalledSetStatusBar(const std::string& strText, int nField); +extern void UIThreadCall(boost::function0 fn); +extern void MainFrameRepaint(); +extern std::string _(const char* psz); + +#endif -- cgit v1.2.3 From daaee738fcdcb6b319b522ee40bc26a95c8a7ad3 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 23 Jun 2011 22:35:30 +0200 Subject: experiment with internationalization (nl), unbreak build (externui.h->qtui.h) --- src/qt/bitcoin.cpp | 11 +- src/qt/forms/aboutdialog.ui | 2 +- src/qt/locale/bitcoin_nl.ts | 1111 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1122 insertions(+), 2 deletions(-) create mode 100644 src/qt/locale/bitcoin_nl.ts diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 917355e764..7d5712f4aa 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -6,11 +6,13 @@ #include "util.h" #include "init.h" #include "main.h" -#include "externui.h" +#include "qtui.h" #include #include #include +#include +#include // Need a global reference for the notifications to find the GUI BitcoinGUI *guiref; @@ -98,6 +100,13 @@ int main(int argc, char *argv[]) { Q_INIT_RESOURCE(bitcoin); QApplication app(argc, argv); + + // Load language file for system locale + QString locale = QLocale::system().name(); + QTranslator translator; + translator.load("bitcoin_"+locale); + app.installTranslator(&translator); + app.setQuitOnLastWindowClosed(false); try diff --git a/src/qt/forms/aboutdialog.ui b/src/qt/forms/aboutdialog.ui index 45915c85bf..cf7997326c 100644 --- a/src/qt/forms/aboutdialog.ui +++ b/src/qt/forms/aboutdialog.ui @@ -57,7 +57,7 @@ - 0.3.666-beta + 0.3.666-beta Qt::RichText diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts new file mode 100644 index 0000000000..8a6acf114b --- /dev/null +++ b/src/qt/locale/bitcoin_nl.ts @@ -0,0 +1,1111 @@ + + + +UTF-8 + + AboutDialog + + + About Bitcoin + Over Bitcoin + + + + + <b>Bitcoin</b> version + <b>Bitcoin</b> versie + + + + Copyright © 2009-2011 Bitcoin Developers + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file license.txt or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + Copyright © 2009-2011 Bitcoin-ontwikkelaars + +Dit is experimentele software. + +Gedistributeerd onder de MIT/X11 software licentie, zie het bijgevoegde bestand +license.txt of kijk op http://www.opensource.org/licenses/mit-license.php. + +Dit product bevat software ontwikkeld door het OpenSSL project for gebruik +in de OpenSSL Toolkit (http://www.openssl.org/), en cryptografische +software geschreven door Eric Young (eay@cryptsoft.com)) en UPnP software geschreven +door Thomas Bernard. + + + + + AddressBookDialog + + + Address Book + + + + + Sending + + + + + Receiving + + + + + These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. The highlighted address is displayed in the main window. + + + + + Create a new address + + + + + &New Address... + + + + + Copy the currently selected address to the system clipboard + + + + + &Copy to Clipboard + + + + + Edit the currently selected address + + + + + &Edit... + + + + + Delete the currently selected address from the list. Only sending addresses can be deleted. + + + + + &Delete + + + + + AddressTableModel + + + Label + + + + + Address + + + + + Default receiving address + + + + + BitcoinGUI + + + Bitcoin + + + + + Your Bitcoin address: + + + + + Your current default receiving address + + + + + &New... + + + + + Create new receiving address + + + + + &Copy to clipboard + + + + + Copy current receiving address to the system clipboard + + + + + Balance: + + + + + Your current balance + + + + + Number of connections to other clients + + + + + Number of blocks in the block chain + + + + + Number of transactions in your wallet + + + + + Downloading initial data... + + + + + Initial block chain download in progress + + + + + &Exit + + + + + Quit application + + + + + &Send coins + + + + + Send coins to a bitcoin address + + + + + &Address Book + + + + + Edit the list of stored addresses and labels + + + + + &About + + + + + Show information about Bitcoin + + + + + Your &Receiving Addresses... + + + + + Show the list of receiving addresses and edit their labels + + + + + &Options... + + + + + Modify configuration options for bitcoin + + + + + Show the Bitcoin window + + + + + All transactions + + + + + Sent/Received + + + + + Sent + + + + + Received + + + + + connection(s) + + + + + + + block(s) + + + + + + + transaction(s) + + + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Sending... + + + + + Incoming transaction + + + + + ClientModel + + + Sending... + + + + + EditAddressDialog + + + Edit Address + + + + + &Label + + + + + &Address + + + + + The label associated with this address book entry + + + + + The address associated with this address book entry. This can only be modified for sending addresses. + + + + + Set as default receiving address + + + + + New receiving address + + + + + New sending address + + + + + Edit receiving address + + + + + Edit sending address + + + + + The address %1 is already in the address book. + + + + + MainOptionsPage + + + &Start Bitcoin on window system startup + + + + + Automatically start Bitcoin after the computer is turned on + + + + + &Minimize to the tray instead of the taskbar + + + + + Show only a tray icon after minimizing the window + + + + + Map port using &UPnP + + + + + Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + M&inimize on close + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + &Connect through SOCKS4 proxy: + + + + + Connect to the Bitcon network through a SOCKS4 proxy (e.g. when connecting through Tor) + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 1234) + + + + + Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended. + + + + + Pay transaction &fee + + + + + Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended. + + + + + OptionsDialog + + + Main + + + + + OK + + + + + Cancel + + + + + Apply + + + + + Options + + + + + SendCoinsDialog + + + + + + + + Send Coins + + + + + &Amount: + + + + + Pay &To: + + + + + The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + + + + + Paste address from system clipboard + + + + + &Paste + + + + + Look up adress in address book + + + + + Address &Book... + + + + + Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + + + + + Confirm the send action + + + + + &Send + + + + + Abort the send action + + + + + The amount to pay must be a valid number. + + + + + The recepient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + + + + + Amount exceeds your balance + + + + + Total exceeds your balance when the %1 transaction fee is included + + + + + TransactionDescDialog + + + Transaction details + + + + + This pane shows a detailed description of the transaction + + + + + TransactionTableModel + + + Status + + + + + Date + + + + + Description + + + + + Debit + + + + + Credit + + + + + Open for %n block(s) + + + + + + + Open until + + + + + Offline (%1) + + + + + Unconfirmed (%1/%2) + + + + + Confirmed (%1) + + + + + Received with: + + + + + Received from IP: + + + + + Sent to: + + + + + Sent to IP: + + + + + Payment to yourself + + + + + Generated (matures in %n more blocks) + + + + + + + Generated + + + + + Generated - Warning: This block was not received by any other nodes and will probably not be accepted! + + + + + Generated (not accepted) + + + + + Transaction status. Hover over this field to show number of transactions. + + + + + Date and time that the transaction was received. + + + + + Short description of the transaction. + + + + + Amount removed from balance. + + + + + Amount added to balance. + + + + + bitcoin-core + + + Bitcoin version + Bitcoin + + + + Usage: + Gebruik: + + + + Send command to -server or bitcoind + + Zend commando naar -server of bitcoind + + + + + List commands + + List van commando's + + + + + Get help for a command + + Toon hulp voor een commando + + + + + Options: + + Opties: + + + + + Specify configuration file (default: bitcoin.conf) + + Specifieer configuratiebestand (standaard: bitcoin.conf) + + + + + Specify pid file (default: bitcoind.pid) + + Specifieer pid-bestand (standaard: bitcoind.pid) + + + + + Generate coins + + Genereer coins + + + + + Don't generate coins + + Genereer geen coins + + + + + Start minimized + + Geminimaliseerd starten + + + + + Specify data directory + + Stel datamap in + + + + + Specify connection timeout (in milliseconds) + + Gelieve de time-out tijd te specifieren (in milliseconden) + + + + + Connect through socks4 proxy + + Verbind via socks4 proxy + + + + + Allow DNS lookups for addnode and connect + + Sta DNS-opzoeking toe voor addnode en connect + + + + + Add a node to connect to + + Voeg een node toe om mee te verbinden + + + + + Connect only to the specified node + + Verbind alleen met deze node + + + + + Don't accept connections from outside + + Sta geen verbindingen van buitenaf toe + + + + + Don't attempt to use UPnP to map the listening port + + Probeer geen UPnP te gebruiken om de poort waarop geluisterd wordt te mappen + + + + + Attempt to use UPnP to map the listening port + + Probeer UPnP te gebruiken om de poort waarop geluisterd wordt te mappen + + + + + Fee per KB to add to transactions you send + + Fooi per KB om aan transacties die gezonden worden toe te voegen + + + + + Accept command line and JSON-RPC commands + + Aanvaard commandolijn en JSON-RPC commando's + + + + + Run in the background as a daemon and accept commands + + Draai in de achtergrond als daemon en aanvaard commando's + + + + + Use the test network + + Gebruik het test-netwerk + + + + + Username for JSON-RPC connections + + Gebruikersnaam voor JSON-RPC verbindingen + + + + + Password for JSON-RPC connections + + Wachtwoord voor JSON-RPC verbindingen + + + + + Listen for JSON-RPC connections on <port> (default: 8332) + + Luister voor JSON-RPC verbindingen op <poort> (standaard: 8332) + + + + + Allow JSON-RPC connections from specified IP address + + Enkel JSON-RPC verbindingen van opgegeven IP adres toestaan + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + Zend commando's naar proces dat op <ip> draait (standaard: 127.0.0.1) + + + + + Set key pool size to <n> (default: 100) + + Stel sleutelpoelgrootte in op <n> (standaard: 100) + + + + + Rescan the block chain for missing wallet transactions + + Doorzoek de blokken database op ontbrekende portefeuille-transacties + + + + + +SSL options: (see the Bitcoin Wiki for SSL setup instructions) + + +SSL opties: (zie de Bitcoin wiki voor SSL instructies) + + + + + Use OpenSSL (https) for JSON-RPC connections + + Gebruik OpenSSL (https) voor JSON-RPC verbindingen + + + + + Server certificate file (default: server.cert) + + Certificaat-bestand voor server (standaard: server.cert) + + + + + Server private key (default: server.pem) + + Geheime sleutel voor server (standaard: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + Aanvaardbare ciphers (standaard: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + + Dit helpbericht + + + + + Cannot obtain a lock on data directory %s. Bitcoin is probably already running. + Kan geen lock op de gegevensdirectory %s verkrijgen. Bitcoin draait vermoedelijk reeds. + + + + Error loading addr.dat + + Fout bij laden van bestand addr.dat + + + + + Error loading blkindex.dat + + Fout bij laden van bestand addr.dat + + + + + Error loading wallet.dat + + Fout bij laden van bestand wallet.dat + + + + + Invalid -proxy address + Foutief -proxy adres + + + + Invalid amount for -paytxfee=<amount> + Ongeldig bedrag voor -paytxfee=<bedrag> + + + + Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction. + Waarschuwing: -paytxfee is zeer hoog ingesteld. Dit is de fooi die betaald wordt bij het zenden van een transactie. + + + + Warning: Disk space is low + Waarschuwing: Weinig schijfruimte over + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds + Fout: Deze transactie vergt een fooi van ten minste %s omwille van zijn bedrag, complexiteit, of gebruik van recent ontvangen coins + + + + Error: Transaction creation failed + Fout: Aanmaken van transactie mislukt + + + + Sending... + Versturen... + + + + Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here. + Fout: De transactie is afgekeurd. Dit kan gebeuren als bepaalde coins in je Portefeuille al zijn uitgegeven. Dit kan veroorzaakt worden doordat je een kopie van wallet.dat gebruikt hebt en enkel daar je uitgave geregistreerd is. + + + + Invalid amount + Foutieve hoeveelheid + + + + Insufficient funds + Onvoldoende saldo + + + + Invalid bitcoin address + Foutief bitcoin-adres + + + + Unable to bind to port %d on this computer. Bitcoin is probably already running. + Kan niet binden met poort %d op deze computer. Bitcoin draait vermoedelijk reeds. + + + + To use the %s option + Om de %s optie te gebruiken + + + + Warning: %s, you must set rpcpassword=<password> +in the configuration file: %s +If the file does not exist, create it with owner-readable-only file permissions. + + Waarschuwing: %s, rpcpassword=<password> moet ingesteld zijn +in het configuratie bestand: %s +Als het bestand nog niet bestaat, maak het dan aan met enkel-leesbaar-door-eigenaar rechten. + + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + rpcpassword=<password> moet ingesteld in het configuratie bestand: +%s +Als het bestand nog niet bestaat, maak het dan aan met enkel-leesbaar-door-eigenaar rechten. + + + + Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly. + Waarschuwing: Controleer of uw computers datum en tijd correct ingesteld zijn. Als uw klok fout staat zal Bitcoin niet correct werken. + + + + -beta + -beta + + + -- cgit v1.2.3 From 6665c2431bb572bb710073de5dc1b6270980101c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 24 Jun 2011 21:23:43 +0200 Subject: use buttonbox for options dialog --- src/qt/bitcoingui.cpp | 6 +++--- src/qt/optionsdialog.cpp | 22 ++++++++-------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bc986dcf9f..52b33fb838 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -361,7 +361,7 @@ void BitcoinGUI::setNumConnections(int count) default: icon = ":/icons/connect_4"; break; } labelConnections->setTextFormat(Qt::RichText); - labelConnections->setText(" " + QLocale::system().toString(count)+" "+tr("connection(s)", "", count)); + labelConnections->setText(" " + tr("%n connection(s)", "", count)); } void BitcoinGUI::setNumBlocks(int count) @@ -380,12 +380,12 @@ void BitcoinGUI::setNumBlocks(int count) progressBar->setVisible(false); } - labelBlocks->setText(QLocale::system().toString(count)+" "+tr("block(s)", "", count)); + labelBlocks->setText(tr("%n block(s)", "", count)); } void BitcoinGUI::setNumTransactions(int count) { - labelTransactions->setText(QLocale::system().toString(count)+" "+tr("transaction(s)", "", count)); + labelTransactions->setText(tr("%n transaction(s)", "", count)); } void BitcoinGUI::error(const QString &title, const QString &message) diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 1b5b2fefeb..3697b9fe41 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -16,6 +16,7 @@ #include #include #include +#include /* First (currently only) page of options */ class MainOptionsPage : public QWidget @@ -64,17 +65,10 @@ OptionsDialog::OptionsDialog(QWidget *parent): QVBoxLayout *layout = new QVBoxLayout(); layout->addLayout(main_layout); - QHBoxLayout *buttons = new QHBoxLayout(); - buttons->addStretch(1); - QPushButton *ok_button = new QPushButton(tr("OK")); - buttons->addWidget(ok_button); - QPushButton *cancel_button = new QPushButton(tr("Cancel")); - buttons->addWidget(cancel_button); - apply_button = new QPushButton(tr("Apply")); - apply_button->setEnabled(false); - buttons->addWidget(apply_button); - - layout->addLayout(buttons); + QDialogButtonBox *buttonbox = new QDialogButtonBox(); + buttonbox->setStandardButtons(QDialogButtonBox::Apply|QDialogButtonBox::Ok|QDialogButtonBox::Cancel); + apply_button = buttonbox->button(QDialogButtonBox::Apply); + layout->addWidget(buttonbox); setLayout(layout); setWindowTitle(tr("Options")); @@ -89,9 +83,9 @@ OptionsDialog::OptionsDialog(QWidget *parent): connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApply())); /* Event bindings */ - connect(ok_button, SIGNAL(clicked()), this, SLOT(okClicked())); - connect(cancel_button, SIGNAL(clicked()), this, SLOT(cancelClicked())); - connect(apply_button, SIGNAL(clicked()), this, SLOT(applyClicked())); + connect(buttonbox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(okClicked())); + connect(buttonbox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(cancelClicked())); + connect(buttonbox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(applyClicked())); } void OptionsDialog::setModel(OptionsModel *model) -- cgit v1.2.3 From 40951d81a71711825134b0f87c8999351d807a17 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 24 Jun 2011 21:45:33 +0200 Subject: finish nl translation --- src/qt/locale/bitcoin_nl.ts | 332 +++++++++++++++++++-------------------- src/qt/transactiontablemodel.cpp | 4 +- 2 files changed, 163 insertions(+), 173 deletions(-) diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts index 8a6acf114b..3464894182 100644 --- a/src/qt/locale/bitcoin_nl.ts +++ b/src/qt/locale/bitcoin_nl.ts @@ -43,62 +43,62 @@ door Thomas Bernard. Address Book - + Adresboek Sending - + Versturen Receiving - + Ontvangen These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. The highlighted address is displayed in the main window. - + Dit zijn je bitcoin-adressen voor het ontvangen van betalingen. Het is een goed idee iedere afzender een ander adres te geven zodat je bij kunt houden wie je een betaling stuurt. Het geselecteerde adres is zichtbaar in het hoofdscherm. Create a new address - + Voeg een nieuw adres toe &New Address... - + &Nieuw adres... Copy the currently selected address to the system clipboard - + Kopieer het geselecteerde adres naar het plakbord &Copy to Clipboard - + &Kopieer naar plakbord Edit the currently selected address - + Bewerk het geselecteerde adres &Edit... - + &Bewerken... Delete the currently selected address from the list. Only sending addresses can be deleted. - + Verwijder het geselecteerde adres. Alleen verstuur-adressen kunnen worden verwijderd. &Delete - + &Verwijderen @@ -106,17 +106,17 @@ door Thomas Bernard. Label - + Label Address - + Adres Default receiving address - + Standaard ontvangst-adres @@ -124,193 +124,196 @@ door Thomas Bernard. Bitcoin - + Bitcoin Your Bitcoin address: - + Uw bitcoin-adres: Your current default receiving address - + Uw huidig standaard ontvangst-adres &New... - + &Nieuw... Create new receiving address - + Maak nieuw ontvangst-adres &Copy to clipboard - + &Kopieer naar plakbord Copy current receiving address to the system clipboard - + Kopieer huidig ontvangst-adres naar plakbord Balance: - + Saldo: Your current balance - + Uw huidige saldo Number of connections to other clients - + Aantal verbindingen met andere clients Number of blocks in the block chain - + Aantal blokken in de block-chain Number of transactions in your wallet - + Aantal transacties in je portefeuille Downloading initial data... - + initiële download... Initial block chain download in progress - + initiële block-chain download is bezig &Exit - + A&fsluiten Quit application - + De applicatie afsluiten &Send coins - + &Verstuur coins Send coins to a bitcoin address - + Coins versturen naar een bitcoin-adres &Address Book - + &Adresboek Edit the list of stored addresses and labels - + Bewerk de lijst van adressen en labels &About - + &Over Bitcoin Show information about Bitcoin - + Laat informatie zien over Bitcoin Your &Receiving Addresses... - + &Uw ontvangstadressen... Show the list of receiving addresses and edit their labels - + Laat de lijst zien van ontvangst-adressen en bewerk de labels &Options... - + &Opties... Modify configuration options for bitcoin - + Verander instellingen van Bitcoin Show the Bitcoin window - + Laat het Bitcoin-venster zien All transactions - + Alle transacties Sent/Received - + Verzonden/Ontvangen Sent - + Verzonden Received - + Ontvangen - connection(s) - - + %n connection(s) + + %n verbinding + %n verbindingen - block(s) - - + %n block(s) + + %n blok + %n blokken - transaction(s) - - + %n transaction(s) + + %n transactie + %n transacties This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? - + Deze transactie overschrijdt de limiet. Om de transactie alsnog te verwerken kun je een fooi betalen van %1. Deze zal betaald worden aan de node die uw transactie verwerkt. Wil je doorgaan en deze fooi betalen? Sending... - + Versturen... Incoming transaction - + Binnenkomende transactie @@ -318,7 +321,7 @@ door Thomas Bernard. Sending... - + Versturen... @@ -326,173 +329,158 @@ door Thomas Bernard. Edit Address - + Bewerk Adres &Label - + &Label &Address - + &Adres The label associated with this address book entry - + Het label dat is geassocieerd met dit adres The address associated with this address book entry. This can only be modified for sending addresses. - + Het adres dat is geassocieerd met de label. Dit kan alleen worden veranderd voor verzend-adressen. Set as default receiving address - + Stel in as standaard ontvangst-adres New receiving address - + Nieuw ontvangst-adres New sending address - + Nieuw verzend-adres Edit receiving address - + Bewerk ontvangst-adres Edit sending address - + Bewerk ontvangst-adres The address %1 is already in the address book. - + Het adres %1 staat al in het adresboek. MainOptionsPage - + &Start Bitcoin on window system startup - + &Start Bitcoin wanneer het systeem opstart - + Automatically start Bitcoin after the computer is turned on - + Start Bitcoin automatisch wanneer het systeem start - + &Minimize to the tray instead of the taskbar - + &Minimaliseer tot systeemvak in plaats van de taakbalk - + Show only a tray icon after minimizing the window - + Laat alleen een systeemvak-icoon zien wanneer het venster geminimaliseerd is - + Map port using &UPnP - + Portmapping via &UPnP - + Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled. - + Open de Bitcoin-poort automatisch op de router. Dit werkt alleen als de router UPnP ondersteunt. - + M&inimize on close - + &Minimaliseer bij sluiten van het venster - + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. - + Minimiliseer het venster in de plaats van de applicatie af te sluiten als het venster gesloten wordt. Wanneer deze optie aan staan, kan de applicatie alleen worden afgesloten door Afsluiten te kiezen in het menu. - + &Connect through SOCKS4 proxy: - + &Verbind via socks4 proxy: - + Connect to the Bitcon network through a SOCKS4 proxy (e.g. when connecting through Tor) - + Verbind met het Bitcoin-netwerk door een SOCKS4 proxy (bijvoorbeeld Tor) - + Proxy &IP: - + Proxy &IP: - + IP address of the proxy (e.g. 127.0.0.1) - + IP adres van de proxy (bijv. 127.0.0.1) - + &Port: - + &Poort: - + Port of the proxy (e.g. 1234) - + Poort waarop de proxy luistert (bijv. 1234) - + Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended. - + Optionele transactiefooi per KB die helpt ervoor zorgen dat uw transacties snel verwerkt worden. De meeste transacties zijn 1KB. Fooi 0.01 is aangeraden. - + Pay transaction &fee - + Transactie&fooi - + Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended. - + Optionele transactiefooi per KB die helpt ervoor zorgen dat uw transacties snel verwerkt worden. De meeste transacties zijn 1KB. Fooi 0.01 is aangeraden. OptionsDialog - + Main - - - - - OK - + Algemeen - - Cancel - - - - - Apply - - - - + Options - + Opties @@ -505,87 +493,87 @@ door Thomas Bernard. Send Coins - + Verstuur coins &Amount: - + &Hoeveelheid: Pay &To: - + Betaan &aan: The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - + Voer een bitcoin-adres (bijvoorbeeld: 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) Paste address from system clipboard - + Plak adres uit systeemplakbord &Paste - + &Plakken Look up adress in address book - + Adres opzoeken in adresboek Address &Book... - + Adres&boek.... Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - + Voer een bitcoin-adres (bijvoorbeeld: 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) Confirm the send action - + Bevestig de zend-transactie &Send - + &Versturen Abort the send action - + De transactie annuleren The amount to pay must be a valid number. - + Het ingevoerde bedrag is geen geldig getal. The recepient address is not valid, please recheck. - + Het verzendadres is niet geld. The amount to pay must be larger than 0. - + Het ingevoerde gedrag moet groter zijn dan 0. Amount exceeds your balance - + Hoeveelheid overschrijdt uw huidige balans Total exceeds your balance when the %1 transaction fee is included - + Totaal overschrijdt uw huidige balans wanneer de %1 transactiefooi is meegerekend @@ -593,12 +581,12 @@ door Thomas Bernard. Transaction details - + Transactiedetails This pane shows a detailed description of the transaction - + Dit venster laat een uitgebreide beschrijving van de transactie zien @@ -606,126 +594,128 @@ door Thomas Bernard. Status - + Status Date - + Datum Description - + Beschrijving Debit - + Debet Credit - + Credit Open for %n block(s) - - + + Open gedurende %n blok + Open gedurende %n blokken - Open until - + Open until %1 + Open tot %1 Offline (%1) - + Offline (%1) Unconfirmed (%1/%2) - + Niet bevestigd (%1/%2) Confirmed (%1) - + Bevestigd (%1) Received with: - + Ontvangen met: Received from IP: - + Ontvangen van IP: Sent to: - + Verzonden aan: Sent to IP: - + Verzonden aan IP: Payment to yourself - + Betaling aan uzelf Generated (matures in %n more blocks) - - + + Gegenereerd (wordt volwassen na %n blok) + Gegenereerd (wordt volwassen na %n blokken) Generated - + Gegenereerd Generated - Warning: This block was not received by any other nodes and will probably not be accepted! - + Gegenereerd - Waarschuwing: Dit blok is niet ontvangen door andere nodes en zal waarschijnlijk niet geaccepteerd worden! Generated (not accepted) - + Gegenereerd (niet geaccepteerd) - Transaction status. Hover over this field to show number of transactions. - + Transaction status. Hover over this field to show number of confirmations. + Transactiestatus. Hou de muiscursor boven dit veld om het aantal bevestigingen te laten zien. Date and time that the transaction was received. - + Datum en tijd waarop deze transactie is ontvangen. Short description of the transaction. - + Korte beschrijving van de transactie. Amount removed from balance. - + Bedrag afgeschreven van saldo. Amount added to balance. - + Bedrag bijgeschreven bij saldo. @@ -738,7 +728,7 @@ door Thomas Bernard. Usage: - Gebruik: + Gebruik: diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index e25ee1d1da..dc3ef245e0 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -270,7 +270,7 @@ QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) con status = tr("Open for %n block(s)","",wtx->status.open_for); break; case TransactionStatus::OpenUntilDate: - status = tr("Open until ") + GUIUtil::DateTimeStr(wtx->status.open_for); + status = tr("Open until %1").arg(GUIUtil::DateTimeStr(wtx->status.open_for)); break; case TransactionStatus::Offline: status = tr("Offline (%1)").arg(wtx->status.depth); @@ -528,7 +528,7 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat switch(section) { case Status: - return tr("Transaction status. Hover over this field to show number of transactions."); + return tr("Transaction status. Hover over this field to show number of confirmations."); case Date: return tr("Date and time that the transaction was received."); case Description: -- cgit v1.2.3 From c88e14fe26b7b8bfd8f5828c7060ac1196b34f50 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 25 Jun 2011 12:39:08 +0200 Subject: Call "initial download" "synchronizing with network" instead --- src/qt/bitcoingui.cpp | 4 ++-- src/qt/locale/bitcoin_nl.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 52b33fb838..ed09ac9c3e 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -127,10 +127,10 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): labelTransactions->setToolTip(tr("Number of transactions in your wallet")); // Progress bar for blocks download - progressBarLabel = new QLabel(tr("Downloading initial data...")); + progressBarLabel = new QLabel(tr("Synchronizing with network...")); progressBarLabel->setVisible(false); progressBar = new QProgressBar(); - progressBar->setToolTip(tr("Initial block chain download in progress")); + progressBar->setToolTip(tr("Block chain synchronization in progress")); progressBar->setVisible(false); statusBar()->addWidget(progressBarLabel); diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts index 3464894182..f9155d532e 100644 --- a/src/qt/locale/bitcoin_nl.ts +++ b/src/qt/locale/bitcoin_nl.ts @@ -183,13 +183,13 @@ door Thomas Bernard. - Downloading initial data... - initiële download... + Synchronizing with network... + Synchroniseren met netwerk... - Initial block chain download in progress - initiële block-chain download is bezig + Block chain synchronization in progress + Bezig met blokken-database-synchronisatie -- cgit v1.2.3 From 38deedc1b52e915f2eac733e0fd7f5112361241b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 25 Jun 2011 19:32:36 +0200 Subject: allow adding address to address book in send dialog --- src/qt/clientmodel.cpp | 7 ++++--- src/qt/clientmodel.h | 2 +- src/qt/forms/sendcoinsdialog.ui | 30 ++++++++++++++++++++++++++++-- src/qt/sendcoinsdialog.cpp | 14 +++++++++++++- src/qt/sendcoinsdialog.h | 1 + 5 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 4e6a34c79f..e39bb7ecb1 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -60,7 +60,7 @@ void ClientModel::update() addressTableModel->update(); } -ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payAmount) +ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs) { uint160 hash160 = 0; bool valid = false; @@ -95,7 +95,7 @@ ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payA std::string strError = SendMoney(scriptPubKey, payAmount, wtx, true); if (strError == "") { - return OK; + // OK } else if (strError == "ABORTED") { @@ -107,11 +107,12 @@ ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payA return MiscError; } } + // Add addresses that we've sent to to the address book std::string strAddress = payTo.toStdString(); CRITICAL_BLOCK(cs_mapAddressBook) if (!mapAddressBook.count(strAddress)) - SetAddressBookName(strAddress, ""); + SetAddressBookName(strAddress, addToAddressBookAs.toStdString()); return OK; } diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 169ed8c4c9..da3e52e20e 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -39,7 +39,7 @@ public: int getTotalBlocksEstimate() const; /* Send coins */ - StatusCode sendCoins(const QString &payTo, qint64 payAmount); + StatusCode sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs=QString()); private: OptionsModel *optionsModel; AddressTableModel *addressTableModel; diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index b88839a7ca..46c71456af 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -16,7 +16,7 @@ - + &Amount: @@ -90,9 +90,33 @@ - + + + + + + + Add specified destination address to address book + + + Add to address book as + + + + + + + false + + + Label to add address as + + + + + @@ -170,6 +194,8 @@ payTo + addToAddressBook + addAsLabel payAmount pasteButton addressBookButton diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 5f9ee18a37..67c270e6f8 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -46,6 +46,7 @@ void SendCoinsDialog::on_sendButton_clicked() { bool valid; QString payAmount = ui->payAmount->text(); + QString label; qint64 payAmountParsed; valid = ParseMoney(payAmount.toStdString(), payAmountParsed); @@ -58,7 +59,13 @@ void SendCoinsDialog::on_sendButton_clicked() return; } - switch(model->sendCoins(ui->payTo->text(), payAmountParsed)) + if(ui->addToAddressBook->isChecked()) + { + // Add address to address book under label, if specified + label = ui->addAsLabel->text(); + } + + switch(model->sendCoins(ui->payTo->text(), payAmountParsed, label)) { case ClientModel::InvalidAddress: QMessageBox::warning(this, tr("Send Coins"), @@ -110,3 +117,8 @@ void SendCoinsDialog::on_buttonBox_rejected() { reject(); } + +void SendCoinsDialog::on_addToAddressBook_toggled(bool checked) +{ + ui->addAsLabel->setEnabled(checked); +} diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index f73c38d63a..206a854ee1 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -23,6 +23,7 @@ private: ClientModel *model; private slots: + void on_addToAddressBook_toggled(bool checked); void on_buttonBox_rejected(); void on_addressBookButton_clicked(); void on_pasteButton_clicked(); -- cgit v1.2.3 From a404b1512a0938091770e1f0de45412f4df12923 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 25 Jun 2011 22:57:24 +0200 Subject: Improve look/usablity of send coins dialog --- doc/assets-attribution.txt | 3 +- src/qt/bitcoin.qrc | 2 + src/qt/bitcoingui.cpp | 2 +- src/qt/forms/sendcoinsdialog.ui | 108 ++++++++++++++++++++++++---------------- src/qt/res/icons/editcopy.png | Bin 0 -> 879 bytes src/qt/res/icons/editpaste.png | Bin 0 -> 1458 bytes 6 files changed, 71 insertions(+), 44 deletions(-) create mode 100644 src/qt/res/icons/editcopy.png create mode 100644 src/qt/res/icons/editpaste.png diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index 623a5027ca..b3c9c7c7aa 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -26,7 +26,8 @@ Site: https://forum.bitcoin.org/index.php?topic=15276.0 License: You are free to do with these icons as you wish, including selling, copying, modifying etc. -Icon: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png +Icon: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png, + src/qt/res/icons/editcopy.png, src/qt/res/icons/editpaste.png Designer: http://www.everaldo.com Icon Pack: Crystal SVG License: LGPL diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 3d4a3e163b..663eb4869d 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -19,6 +19,8 @@ res/icons/clock5.png res/icons/configure.png res/icons/receive.png + res/icons/editpaste.png + res/icons/editcopy.png
res/images/about.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index ed09ac9c3e..1b4e13c420 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -93,7 +93,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ labelBalance = new QLabel(); - labelBalance->setFont(QFont("Monospace")); + labelBalance->setFont(QFont("Monospace", -1, QFont::Bold)); labelBalance->setToolTip(tr("Your current balance")); hbox_balance->addWidget(labelBalance); hbox_balance->addStretch(1); diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 46c71456af..3212424896 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -7,7 +7,7 @@ 0 0 736 - 149 + 193 @@ -16,10 +16,70 @@ + + + + 0 + + + + + The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + + + 34 + + + + + + + Look up adress in address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + false + + + false + + + + + + + Paste address from system clipboard + + + + + + + :/icons/editpaste:/icons/editpaste + + + Alt+P + + + false + + + + + - &Amount: + A&mount: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -42,42 +102,6 @@ - - - - The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - - - 34 - - - - - - - Paste address from system clipboard - - - &Paste - - - false - - - - - - - Look up adress in address book - - - Address &Book... - - - false - - - @@ -91,7 +115,7 @@ - + @@ -101,7 +125,7 @@ Add specified destination address to address book - Add to address book as + A&dd to address book as @@ -187,7 +211,7 @@ BitcoinAmountField - QWidget + QLineEdit
bitcoinamountfield.h
1
@@ -197,8 +221,8 @@ addToAddressBook addAsLabel payAmount - pasteButton addressBookButton + pasteButton sendButton buttonBox diff --git a/src/qt/res/icons/editcopy.png b/src/qt/res/icons/editcopy.png new file mode 100644 index 0000000000..f882aa2ad8 Binary files /dev/null and b/src/qt/res/icons/editcopy.png differ diff --git a/src/qt/res/icons/editpaste.png b/src/qt/res/icons/editpaste.png new file mode 100644 index 0000000000..a192060bdd Binary files /dev/null and b/src/qt/res/icons/editpaste.png differ -- cgit v1.2.3 From 0030c1bd6cdee44d9f59af9a023cd39cbd335bd5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 25 Jun 2011 23:14:10 +0200 Subject: compile fixes by Unthinkingbit --- src/qt/bitcoinamountfield.cpp | 2 +- src/qt/forms/sendcoinsdialog.ui | 4 ++-- src/qt/guiutil.cpp | 2 +- src/qt/transactiondesc.cpp | 2 +- src/qtui.h | 1 + 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 935bd13a44..8166ce6e6e 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -13,7 +13,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): amount->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this)); amount->setAlignment(Qt::AlignRight|Qt::AlignVCenter); amount->installEventFilter(this); - amount->setMaximumWidth(80); + amount->setMaximumWidth(100); decimals = new QLineEdit(this); decimals->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this)); decimals->setMaxLength(8); diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 3212424896..efefe6ab9c 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -6,8 +6,8 @@ 0 0 - 736 - 193 + 660 + 151 diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index a81cc14fe8..ec8b43526d 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -9,7 +9,7 @@ QString GUIUtil::DateTimeStr(qint64 nTime) { - QDateTime date = QDateTime::fromMSecsSinceEpoch(nTime*1000); + QDateTime date = QDateTime::fromTime_t((qint32)nTime); return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm"); } diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 9120f19c7a..84b617b7c4 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -2,7 +2,7 @@ #include "guiutil.h" #include "main.h" -#include "externui.h" +#include "qtui.h" #include diff --git a/src/qtui.h b/src/qtui.h index 3243164c10..42f44e0d04 100644 --- a/src/qtui.h +++ b/src/qtui.h @@ -5,6 +5,7 @@ #define BITCOIN_EXTERNUI_H #include +#include typedef void wxWindow; #define wxYES 0x00000002 -- cgit v1.2.3 From 42e950aa694f0e4ce885087b8f493df7b9dcff6c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 25 Jun 2011 23:20:42 +0200 Subject: update dutch translation --- src/qt/locale/bitcoin_nl.ts | 79 ++++++++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts index f9155d532e..79c2fdb1fa 100644 --- a/src/qt/locale/bitcoin_nl.ts +++ b/src/qt/locale/bitcoin_nl.ts @@ -487,91 +487,118 @@ door Thomas Bernard. SendCoinsDialog - - - - - + + + + + Send Coins Verstuur coins - &Amount: - &Hoeveelheid: + &Hoeveelheid: - + Pay &To: - Betaan &aan: + Betaal &aan: - + The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) Voer een bitcoin-adres (bijvoorbeeld: 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - + + Alt+A + Alt+A + + + Paste address from system clipboard Plak adres uit systeemplakbord - &Paste - &Plakken + &Plakken - + Look up adress in address book - Adres opzoeken in adresboek + Opzoeken in adresboek - Address &Book... - Adres&boek.... + Adres&boek.... + + + + Alt+P + Alt+P + + + + A&mount: + &Hoeveelheid: - + Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) Voer een bitcoin-adres (bijvoorbeeld: 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - + + Add specified destination address to address book + Voeg het ingevoerde doel-adres toe aan het adresboek + + + + A&dd to address book as + &Voeg toe aan adresboek als + + + + Label to add address as + Label om toe te kennen aan adres in adresboek + + + Confirm the send action Bevestig de zend-transactie - + &Send &Versturen - + Abort the send action De transactie annuleren - + The amount to pay must be a valid number. Het ingevoerde bedrag is geen geldig getal. - + The recepient address is not valid, please recheck. Het verzendadres is niet geld. - + The amount to pay must be larger than 0. Het ingevoerde gedrag moet groter zijn dan 0. - + Amount exceeds your balance Hoeveelheid overschrijdt uw huidige balans - + Total exceeds your balance when the %1 transaction fee is included Totaal overschrijdt uw huidige balans wanneer de %1 transactiefooi is meegerekend -- cgit v1.2.3 From cae5264a4c0d224a632d3806d4f066eb4d0d97a8 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 25 Jun 2011 23:27:19 +0200 Subject: fix typo in dutch translation --- src/qt/locale/bitcoin_nl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts index 79c2fdb1fa..935815c48a 100644 --- a/src/qt/locale/bitcoin_nl.ts +++ b/src/qt/locale/bitcoin_nl.ts @@ -545,7 +545,7 @@ door Thomas Bernard. Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - Voer een bitcoin-adres (bijvoorbeeld: 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + Voer een bitcoin-adres in (bijvoorbeeld: 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) -- cgit v1.2.3 From d99f5a470c8c4e80223f7a78679db58db78ee979 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 26 Jun 2011 12:01:25 +0200 Subject: reduce spacing between "Add to address book as" and the text field --- src/qt/forms/sendcoinsdialog.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index efefe6ab9c..3641f61eca 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -119,6 +119,9 @@ + + 0 + -- cgit v1.2.3 From e8ef3da7133dd9fc411fa8b3cc8b8fc2f9c58a98 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 26 Jun 2011 19:23:24 +0200 Subject: update core to d0d80170a2ca73004e08fb85007fe055cbf4e411 (CWallet class) --- bitcoin-qt.pro | 8 +- src/db.cpp | 168 ++--- src/db.h | 118 +--- src/headers.h | 2 - src/init.cpp | 37 +- src/init.h | 2 + src/keystore.cpp | 33 + src/keystore.h | 30 + src/main.cpp | 1443 +++++++------------------------------- src/main.h | 567 +-------------- src/net.cpp | 4 +- src/noui.h | 2 + src/qt/addresstablemodel.cpp | 76 +- src/qt/addresstablemodel.h | 4 +- src/qt/bitcoin.cpp | 7 +- src/qt/bitcoingui.cpp | 2 +- src/qt/clientmodel.cpp | 29 +- src/qt/clientmodel.h | 5 +- src/qt/optionsmodel.cpp | 11 +- src/qt/optionsmodel.h | 7 +- src/qt/transactiondesc.cpp | 54 +- src/qt/transactiondesc.h | 3 +- src/qt/transactionrecord.cpp | 21 +- src/qt/transactionrecord.h | 4 +- src/qt/transactiontablemodel.cpp | 58 +- src/qt/transactiontablemodel.h | 5 +- src/qtui.h | 1 + src/rpc.cpp | 149 ++-- src/script.cpp | 33 +- src/script.h | 8 +- src/util.cpp | 2 +- src/util.h | 4 +- src/wallet.cpp | 1176 +++++++++++++++++++++++++++++++ src/wallet.h | 611 ++++++++++++++++ 34 files changed, 2481 insertions(+), 2203 deletions(-) create mode 100644 src/keystore.cpp create mode 100644 src/keystore.h create mode 100644 src/wallet.cpp create mode 100644 src/wallet.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 7e918f144f..4036c14191 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -70,7 +70,9 @@ HEADERS += src/qt/bitcoingui.h \ src/qtui.h \ src/qt/transactiondesc.h \ src/qt/transactiondescdialog.h \ - src/qt/bitcoinamountfield.h + src/qt/bitcoinamountfield.h \ + src/wallet.h \ + src/keystore.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -101,7 +103,9 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiondesc.cpp \ src/qt/transactiondescdialog.cpp \ src/qt/bitcoinstrings.cpp \ - src/qt/bitcoinamountfield.cpp + src/qt/bitcoinamountfield.cpp \ + src/wallet.cpp \ + src/keystore.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/src/db.cpp b/src/db.cpp index a7fb4bd6ea..f044355a35 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -5,14 +5,12 @@ #include "headers.h" #include "db.h" #include "net.h" -#include +#include #include using namespace std; using namespace boost; -void ThreadFlushWalletDB(void* parg); - unsigned int nWalletDBUpdated; uint64 nAccountingEntryNumber = 0; @@ -151,7 +149,7 @@ void CDB::Close() --mapFileUseCount[strFile]; } -void CloseDb(const string& strFile) +void static CloseDb(const string& strFile) { CRITICAL_BLOCK(cs_db) { @@ -360,7 +358,7 @@ bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork) return Write(string("bnBestInvalidWork"), bnBestInvalidWork); } -CBlockIndex* InsertBlockIndex(uint256 hash) +CBlockIndex static * InsertBlockIndex(uint256 hash) { if (hash == 0) return NULL; @@ -585,8 +583,19 @@ bool LoadAddresses() // CWalletDB // -static set setKeyPool; -static CCriticalSection cs_setKeyPool; +bool CWalletDB::WriteName(const string& strAddress, const string& strName) +{ + nWalletDBUpdated++; + return Write(make_pair(string("name"), strAddress), strName); +} + +bool CWalletDB::EraseName(const string& strAddress) +{ + // This should only be used for sending addresses, never for receiving addresses, + // receiving addresses must always have an address book entry if they're not change return. + nWalletDBUpdated++; + return Erase(make_pair(string("name"), strAddress)); +} bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) { @@ -661,9 +670,9 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, listvchDefaultKey.clear(); int nFileVersion = 0; vector vWalletUpgrade; @@ -675,8 +684,8 @@ bool CWalletDB::LoadWallet() #endif //// todo: shouldn't we catch exceptions and try to recover and continue? - CRITICAL_BLOCK(cs_mapWallet) - CRITICAL_BLOCK(cs_mapKeys) + CRITICAL_BLOCK(pwallet->cs_mapWallet) + CRITICAL_BLOCK(pwallet->cs_mapKeys) { // Get cursor Dbc* pcursor = GetCursor(); @@ -703,14 +712,15 @@ bool CWalletDB::LoadWallet() { string strAddress; ssKey >> strAddress; - ssValue >> mapAddressBook[strAddress]; + ssValue >> pwallet->mapAddressBook[strAddress]; } else if (strType == "tx") { uint256 hash; ssKey >> hash; - CWalletTx& wtx = mapWallet[hash]; + CWalletTx& wtx = pwallet->mapWallet[hash]; ssValue >> wtx; + wtx.pwallet = pwallet; if (wtx.GetHash() != hash) printf("Error in wallet.dat, hash mismatch\n"); @@ -761,18 +771,18 @@ bool CWalletDB::LoadWallet() else ssValue >> wkey; - mapKeys[vchPubKey] = wkey.vchPrivKey; + pwallet->mapKeys[vchPubKey] = wkey.vchPrivKey; mapPubKeys[Hash160(vchPubKey)] = vchPubKey; } else if (strType == "defaultkey") { - ssValue >> vchDefaultKey; + ssValue >> pwallet->vchDefaultKey; } else if (strType == "pool") { int64 nIndex; ssKey >> nIndex; - setKeyPool.insert(nIndex); + pwallet->setKeyPool.insert(nIndex); } else if (strType == "version") { @@ -804,7 +814,7 @@ bool CWalletDB::LoadWallet() } BOOST_FOREACH(uint256 hash, vWalletUpgrade) - WriteTx(hash, mapWallet[hash]); + WriteTx(hash, pwallet->mapWallet[hash]); printf("nFileVersion = %d\n", nFileVersion); printf("fGenerateBitcoins = %d\n", fGenerateBitcoins); @@ -832,36 +842,9 @@ bool CWalletDB::LoadWallet() return true; } -bool LoadWallet(bool& fFirstRunRet) -{ - fFirstRunRet = false; - if (!CWalletDB("cr+").LoadWallet()) - return false; - fFirstRunRet = vchDefaultKey.empty(); - - if (mapKeys.count(vchDefaultKey)) - { - // Set keyUser - keyUser.SetPubKey(vchDefaultKey); - keyUser.SetPrivKey(mapKeys[vchDefaultKey]); - } - else - { - // Create new keyUser and set as default key - RandAddSeedPerfmon(); - - CWalletDB walletdb; - vchDefaultKey = GetKeyFromKeyPool(); - walletdb.WriteDefaultKey(vchDefaultKey); - walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); - } - - CreateThread(ThreadFlushWalletDB, NULL); - return true; -} - void ThreadFlushWalletDB(void* parg) { + const string& strFile = ((const string*)parg)[0]; static bool fOneThread; if (fOneThread) return; @@ -897,7 +880,6 @@ void ThreadFlushWalletDB(void* parg) if (nRefCount == 0 && !fShutdown) { - string strFile = "wallet.dat"; map::iterator mi = mapFileUseCount.find(strFile); if (mi != mapFileUseCount.end()) { @@ -920,26 +902,27 @@ void ThreadFlushWalletDB(void* parg) } } -void BackupWallet(const string& strDest) +bool BackupWallet(const CWallet& wallet, const string& strDest) { + if (!wallet.fFileBacked) + return false; while (!fShutdown) { CRITICAL_BLOCK(cs_db) { - const string strFile = "wallet.dat"; - if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0) + if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0) { // Flush log data to the dat file - CloseDb(strFile); + CloseDb(wallet.strWalletFile); dbenv.txn_checkpoint(0, 0, 0); - dbenv.lsn_reset(strFile.c_str(), 0); - mapFileUseCount.erase(strFile); + dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0); + mapFileUseCount.erase(wallet.strWalletFile); // Copy wallet.dat - filesystem::path pathSrc(GetDataDir() + "/" + strFile); + filesystem::path pathSrc(GetDataDir() + "/" + wallet.strWalletFile); filesystem::path pathDest(strDest); if (filesystem::is_directory(pathDest)) - pathDest = pathDest / strFile; + pathDest = pathDest / wallet.strWalletFile; #if BOOST_VERSION >= 104000 filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists); #else @@ -947,83 +930,10 @@ void BackupWallet(const string& strDest) #endif printf("copied wallet.dat to %s\n", pathDest.string().c_str()); - return; + return true; } } Sleep(100); } -} - - -void CWalletDB::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) -{ - nIndex = -1; - keypool.vchPubKey.clear(); - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - CRITICAL_BLOCK(cs_setKeyPool) - { - // Top up key pool - int64 nTargetSize = max(GetArg("-keypool", 100), (int64)0); - while (setKeyPool.size() < nTargetSize+1) - { - int64 nEnd = 1; - if (!setKeyPool.empty()) - nEnd = *(--setKeyPool.end()) + 1; - if (!Write(make_pair(string("pool"), nEnd), CKeyPool(GenerateNewKey()))) - throw runtime_error("ReserveKeyFromKeyPool() : writing generated key failed"); - setKeyPool.insert(nEnd); - printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size()); - } - - // Get the oldest key - assert(!setKeyPool.empty()); - nIndex = *(setKeyPool.begin()); - setKeyPool.erase(setKeyPool.begin()); - if (!Read(make_pair(string("pool"), nIndex), keypool)) - throw runtime_error("ReserveKeyFromKeyPool() : read failed"); - if (!mapKeys.count(keypool.vchPubKey)) - throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); - assert(!keypool.vchPubKey.empty()); - printf("keypool reserve %"PRI64d"\n", nIndex); - } -} - -void CWalletDB::KeepKey(int64 nIndex) -{ - // Remove from key pool - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - { - Erase(make_pair(string("pool"), nIndex)); - } - printf("keypool keep %"PRI64d"\n", nIndex); -} - -void CWalletDB::ReturnKey(int64 nIndex) -{ - // Return to key pool - CRITICAL_BLOCK(cs_setKeyPool) - setKeyPool.insert(nIndex); - printf("keypool return %"PRI64d"\n", nIndex); -} - -vector GetKeyFromKeyPool() -{ - CWalletDB walletdb; - int64 nIndex = 0; - CKeyPool keypool; - walletdb.ReserveKeyFromKeyPool(nIndex, keypool); - walletdb.KeepKey(nIndex); - return keypool.vchPubKey; -} - -int64 GetOldestKeyPoolTime() -{ - CWalletDB walletdb; - int64 nIndex = 0; - CKeyPool keypool; - walletdb.ReserveKeyFromKeyPool(nIndex, keypool); - walletdb.ReturnKey(nIndex); - return keypool.nTime; + return false; } diff --git a/src/db.h b/src/db.h index 9826194ed0..b89b34e009 100644 --- a/src/db.h +++ b/src/db.h @@ -12,33 +12,25 @@ #include -class CTransaction; class CTxIndex; class CDiskBlockIndex; class CDiskTxPos; class COutPoint; -class CUser; -class CReview; class CAddress; class CWalletTx; +class CWallet; class CAccount; class CAccountingEntry; class CBlockLocator; -extern std::map mapAddressBook; -extern CCriticalSection cs_mapAddressBook; -extern std::vector vchDefaultKey; -extern bool fClient; -extern int nBestHeight; - extern unsigned int nWalletDBUpdated; extern DbEnv dbenv; extern void DBFlush(bool fShutdown); -extern std::vector GetKeyFromKeyPool(); -extern int64 GetOldestKeyPoolTime(); +void ThreadFlushWalletDB(void* parg); +bool BackupWallet(const CWallet& wallet, const std::string& strDest); @@ -321,9 +313,6 @@ bool LoadAddresses(); - - - class CKeyPool { public: @@ -356,7 +345,7 @@ public: class CWalletDB : public CDB { public: - CWalletDB(const char* pszMode="r+") : CDB("wallet.dat", pszMode) + CWalletDB(std::string strFilename, const char* pszMode="r+") : CDB(strFilename.c_str(), pszMode) { } private: @@ -369,23 +358,9 @@ public: return Read(std::make_pair(std::string("name"), strAddress), strName); } - bool WriteName(const std::string& strAddress, const std::string& strName) - { - CRITICAL_BLOCK(cs_mapAddressBook) - mapAddressBook[strAddress] = strName; - nWalletDBUpdated++; - return Write(std::make_pair(std::string("name"), strAddress), strName); - } + bool WriteName(const std::string& strAddress, const std::string& strName); - bool EraseName(const std::string& strAddress) - { - // This should only be used for sending addresses, never for receiving addresses, - // receiving addresses must always have an address book entry if they're not change return. - CRITICAL_BLOCK(cs_mapAddressBook) - mapAddressBook.erase(strAddress); - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("name"), strAddress)); - } + bool EraseName(const std::string& strAddress); bool ReadTx(uint256 hash, CWalletTx& wtx) { @@ -435,11 +410,27 @@ public: bool WriteDefaultKey(const std::vector& vchPubKey) { - vchDefaultKey = vchPubKey; nWalletDBUpdated++; return Write(std::string("defaultkey"), vchPubKey); } + bool ReadPool(int64 nPool, CKeyPool& keypool) + { + return Read(std::make_pair(std::string("pool"), nPool), keypool); + } + + bool WritePool(int64 nPool, const CKeyPool& keypool) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("pool"), nPool), keypool); + } + + bool ErasePool(int64 nPool) + { + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("pool"), nPool)); + } + template bool ReadSetting(const std::string& strKey, T& value) { @@ -459,68 +450,7 @@ public: int64 GetAccountCreditDebit(const std::string& strAccount); void ListAccountCreditDebit(const std::string& strAccount, std::list& acentries); - bool LoadWallet(); -protected: - void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool); - void KeepKey(int64 nIndex); - static void ReturnKey(int64 nIndex); - friend class CReserveKey; - friend std::vector GetKeyFromKeyPool(); - friend int64 GetOldestKeyPoolTime(); -}; - -bool LoadWallet(bool& fFirstRunRet); -void BackupWallet(const std::string& strDest); - -inline bool SetAddressBookName(const std::string& strAddress, const std::string& strName) -{ - return CWalletDB().WriteName(strAddress, strName); -} - -class CReserveKey -{ -protected: - int64 nIndex; - std::vector vchPubKey; -public: - CReserveKey() - { - nIndex = -1; - } - - ~CReserveKey() - { - if (!fShutdown) - ReturnKey(); - } - - std::vector GetReservedKey() - { - if (nIndex == -1) - { - CKeyPool keypool; - CWalletDB().ReserveKeyFromKeyPool(nIndex, keypool); - vchPubKey = keypool.vchPubKey; - } - assert(!vchPubKey.empty()); - return vchPubKey; - } - - void KeepKey() - { - if (nIndex != -1) - CWalletDB().KeepKey(nIndex); - nIndex = -1; - vchPubKey.clear(); - } - - void ReturnKey() - { - if (nIndex != -1) - CWalletDB::ReturnKey(nIndex); - nIndex = -1; - vchPubKey.clear(); - } + bool LoadWallet(CWallet* pwallet); }; #endif diff --git a/src/headers.h b/src/headers.h index 38d9566baa..02dba30ae2 100644 --- a/src/headers.h +++ b/src/headers.h @@ -91,10 +91,8 @@ #include "serialize.h" #include "uint256.h" #include "util.h" -#include "key.h" #include "bignum.h" #include "base58.h" -#include "script.h" #include "main.h" #ifdef GUI #include "uibase.h" diff --git a/src/init.cpp b/src/init.cpp index cbd9fc0219..e4605a2b42 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -7,13 +7,15 @@ #include "net.h" #include "init.h" #include "strlcpy.h" -#include +#include #include #include using namespace std; using namespace boost; +CWallet* pwalletMain; + ////////////////////////////////////////////////////////////////////////////// // // Shutdown @@ -46,6 +48,8 @@ void Shutdown(void* parg) StopNode(); DBFlush(true); boost::filesystem::remove(GetPidFile()); + UnregisterWallet(pwalletMain); + delete pwalletMain; CreateThread(ExitTimeout, NULL); Sleep(50); printf("Bitcoin exiting\n\n"); @@ -137,10 +141,19 @@ bool AppInit2(int argc, char* argv[]) if (mapArgs.count("-datadir")) { - filesystem::path pathDataDir = filesystem::system_complete(mapArgs["-datadir"]); - strlcpy(pszSetDataDir, pathDataDir.string().c_str(), sizeof(pszSetDataDir)); + if (filesystem::is_directory(filesystem::system_complete(mapArgs["-datadir"]))) + { + filesystem::path pathDataDir = filesystem::system_complete(mapArgs["-datadir"]); + strlcpy(pszSetDataDir, pathDataDir.string().c_str(), sizeof(pszSetDataDir)); + } + else + { + fprintf(stderr, "Error: Specified directory does not exist\n"); + Shutdown(NULL); + } } + ReadConfigFile(mapArgs, mapMultiArgs); // Must be done after processing datadir if (mapArgs.count("-?") || mapArgs.count("--help")) @@ -372,16 +385,19 @@ bool AppInit2(int argc, char* argv[]) printf("Loading wallet...\n"); nStart = GetTimeMillis(); bool fFirstRun; - if (!LoadWallet(fFirstRun)) + pwalletMain = new CWallet("wallet.dat"); + if (!pwalletMain->LoadWallet(fFirstRun)) strErrors += _("Error loading wallet.dat \n"); printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart); + RegisterWallet(pwalletMain); + CBlockIndex *pindexRescan = pindexBest; if (GetBoolArg("-rescan")) pindexRescan = pindexGenesisBlock; else { - CWalletDB walletdb; + CWalletDB walletdb("wallet.dat"); CBlockLocator locator; if (walletdb.ReadBestBlock(locator)) pindexRescan = locator.GetBlockIndex(); @@ -390,7 +406,7 @@ bool AppInit2(int argc, char* argv[]) { printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); nStart = GetTimeMillis(); - ScanForWalletTransactions(pindexRescan, true); + pwalletMain->ScanForWalletTransactions(pindexRescan, true); printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); } @@ -399,10 +415,11 @@ bool AppInit2(int argc, char* argv[]) //// debug print printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size()); printf("nBestHeight = %d\n", nBestHeight); - printf("mapKeys.size() = %d\n", mapKeys.size()); + printf("mapKeys.size() = %d\n", pwalletMain->mapKeys.size()); + printf("setKeyPool.size() = %d\n", pwalletMain->setKeyPool.size()); printf("mapPubKeys.size() = %d\n", mapPubKeys.size()); - printf("mapWallet.size() = %d\n", mapWallet.size()); - printf("mapAddressBook.size() = %d\n", mapAddressBook.size()); + printf("mapWallet.size() = %d\n", pwalletMain->mapWallet.size()); + printf("mapAddressBook.size() = %d\n", pwalletMain->mapAddressBook.size()); if (!strErrors.empty()) { @@ -411,7 +428,7 @@ bool AppInit2(int argc, char* argv[]) } // Add wallet transactions that aren't already in a block to mapTransactions - ReacceptWalletTransactions(); + pwalletMain->ReacceptWalletTransactions(); // // Parameters diff --git a/src/init.h b/src/init.h index 61b2728576..a02260c293 100644 --- a/src/init.h +++ b/src/init.h @@ -4,6 +4,8 @@ #ifndef BITCOIN_INIT_H #define BITCOIN_INIT_H +extern CWallet* pwalletMain; + void Shutdown(void* parg); bool AppInit(int argc, char* argv[]); bool AppInit2(int argc, char* argv[]); diff --git a/src/keystore.cpp b/src/keystore.cpp new file mode 100644 index 0000000000..7dd045fe5f --- /dev/null +++ b/src/keystore.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2009-2011 Satoshi Nakamoto & Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" +#include "db.h" + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapKeys +// + +std::vector CKeyStore::GenerateNewKey() +{ + RandAddSeedPerfmon(); + CKey key; + key.MakeNewKey(); + if (!AddKey(key)) + throw std::runtime_error("GenerateNewKey() : AddKey failed"); + return key.GetPubKey(); +} + +bool CKeyStore::AddKey(const CKey& key) +{ + CRITICAL_BLOCK(cs_mapKeys) + { + mapKeys[key.GetPubKey()] = key.GetPrivKey(); + mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey(); + } +} + diff --git a/src/keystore.h b/src/keystore.h new file mode 100644 index 0000000000..6080d7d7f5 --- /dev/null +++ b/src/keystore.h @@ -0,0 +1,30 @@ +// Copyright (c) 2009-2011 Satoshi Nakamoto & Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_KEYSTORE_H +#define BITCOIN_KEYSTORE_H + +class CKeyStore +{ +public: + std::map, CPrivKey> mapKeys; + mutable CCriticalSection cs_mapKeys; + virtual bool AddKey(const CKey& key); + bool HaveKey(const std::vector &vchPubKey) const + { + return (mapKeys.count(vchPubKey) > 0); + } + bool GetPrivKey(const std::vector &vchPubKey, CPrivKey& keyOut) const + { + std::map, CPrivKey>::const_iterator mi = mapKeys.find(vchPubKey); + if (mi != mapKeys.end()) + { + keyOut = (*mi).second; + return true; + } + return false; + } + std::vector GenerateNewKey(); +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp index 2dbbd674a7..54902e82eb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,7 @@ #include "net.h" #include "init.h" #include "cryptopp/sha.h" -#include +#include #include using namespace std; @@ -16,8 +16,14 @@ using namespace boost; // Global state // +CCriticalSection cs_setpwalletRegistered; +set setpwalletRegistered; + CCriticalSection cs_main; +CCriticalSection cs_mapPubKeys; +map > mapPubKeys; + map mapTransactions; CCriticalSection cs_mapTransactions; unsigned int nTransactionsUpdated = 0; @@ -42,22 +48,6 @@ multimap mapOrphanBlocksByPrev; map mapOrphanTransactions; multimap mapOrphanTransactionsByPrev; -map mapWallet; -vector vWalletUpdated; -CCriticalSection cs_mapWallet; - -map, CPrivKey> mapKeys; -map > mapPubKeys; -CCriticalSection cs_mapKeys; -CKey keyUser; - -map mapRequestCount; -CCriticalSection cs_mapRequestCount; - -map mapAddressBook; -CCriticalSection cs_mapAddressBook; - -vector vchDefaultKey; double dHashesPerSec; int64 nHPSTimerStart; @@ -84,151 +74,82 @@ int fUseUPnP = false; ////////////////////////////////////////////////////////////////////////////// // -// mapKeys +// dispatching functions // -bool AddKey(const CKey& key) +void RegisterWallet(CWallet* pwalletIn) { - CRITICAL_BLOCK(cs_mapKeys) + CRITICAL_BLOCK(cs_setpwalletRegistered) { - mapKeys[key.GetPubKey()] = key.GetPrivKey(); - mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey(); + setpwalletRegistered.insert(pwalletIn); } - return CWalletDB().WriteKey(key.GetPubKey(), key.GetPrivKey()); } -vector GenerateNewKey() +void UnregisterWallet(CWallet* pwalletIn) { - RandAddSeedPerfmon(); - CKey key; - key.MakeNewKey(); - if (!AddKey(key)) - throw runtime_error("GenerateNewKey() : AddKey failed"); - return key.GetPubKey(); + CRITICAL_BLOCK(cs_setpwalletRegistered) + { + setpwalletRegistered.erase(pwalletIn); + } } - - - -////////////////////////////////////////////////////////////////////////////// -// -// mapWallet -// - -bool AddToWallet(const CWalletTx& wtxIn) +bool static IsFromMe(CTransaction& tx) { - uint256 hash = wtxIn.GetHash(); - CRITICAL_BLOCK(cs_mapWallet) - { - // Inserts only if not already there, returns tx inserted or tx found - pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); - CWalletTx& wtx = (*ret.first).second; - bool fInsertedNew = ret.second; - if (fInsertedNew) - wtx.nTimeReceived = GetAdjustedTime(); - - bool fUpdated = false; - if (!fInsertedNew) - { - // Merge - if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) - { - wtx.hashBlock = wtxIn.hashBlock; - fUpdated = true; - } - if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) - { - wtx.vMerkleBranch = wtxIn.vMerkleBranch; - wtx.nIndex = wtxIn.nIndex; - fUpdated = true; - } - if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) - { - wtx.fFromMe = wtxIn.fFromMe; - fUpdated = true; - } - fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent); - } - - //// debug print - printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + if (pwallet->IsFromMe(tx)) + return true; + return false; +} - // Write to disk - if (fInsertedNew || fUpdated) - if (!wtx.WriteToDisk()) - return false; +bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + if (pwallet->GetTransaction(hashTx,wtx)) + return true; + return false; +} - // If default receiving address gets used, replace it with a new one - CScript scriptDefaultKey; - scriptDefaultKey.SetBitcoinAddress(vchDefaultKey); - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - if (txout.scriptPubKey == scriptDefaultKey) - { - CWalletDB walletdb; - vchDefaultKey = GetKeyFromKeyPool(); - walletdb.WriteDefaultKey(vchDefaultKey); - walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); - } - } +void static EraseFromWallets(uint256 hash) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->EraseFromWallet(hash); +} - // Notify UI - vWalletUpdated.push_back(hash); - } +void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); +} - // Refresh UI - MainFrameRepaint(); - return true; +void static SetBestChain(const CBlockLocator& loc) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->SetBestChain(loc); } -bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false) +void static UpdatedTransaction(const uint256& hashTx) { - uint256 hash = tx.GetHash(); - bool fExisted = mapWallet.count(hash); - if (fExisted && !fUpdate) return false; - if (fExisted || tx.IsMine() || tx.IsFromMe()) - { - CWalletTx wtx(tx); - // Get merkle branch if transaction was found in a block - if (pblock) - wtx.SetMerkleBranch(pblock); - return AddToWallet(wtx); - } - return false; + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->UpdatedTransaction(hashTx); } -bool EraseFromWallet(uint256 hash) +void static PrintWallets(const CBlock& block) { - CRITICAL_BLOCK(cs_mapWallet) - { - if (mapWallet.erase(hash)) - CWalletDB().EraseTx(hash); - } - return true; + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->PrintWallet(block); } -void WalletUpdateSpent(const COutPoint& prevout) +void static Inventory(const uint256& hash) { - // Anytime a signature is successfully verified, it's proof the outpoint is spent. - // Update the wallet spent flag if it doesn't know due to wallet.dat being - // restored from backup or the user making copies of wallet.dat. - CRITICAL_BLOCK(cs_mapWallet) - { - map::iterator mi = mapWallet.find(prevout.hash); - if (mi != mapWallet.end()) - { - CWalletTx& wtx = (*mi).second; - if (!wtx.IsSpent(prevout.n) && wtx.vout[prevout.n].IsMine()) - { - printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); - wtx.MarkSpent(prevout.n); - wtx.WriteToDisk(); - vWalletUpdated.push_back(prevout.hash); - } - } - } + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->Inventory(hash); } +void static ResendWalletTransactions() +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->ResendWalletTransactions(); +} @@ -241,7 +162,7 @@ void WalletUpdateSpent(const COutPoint& prevout) // mapOrphanTransactions // -void AddOrphanTx(const CDataStream& vMsg) +void static AddOrphanTx(const CDataStream& vMsg) { CTransaction tx; CDataStream(vMsg) >> tx; @@ -253,7 +174,7 @@ void AddOrphanTx(const CDataStream& vMsg) mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg)); } -void EraseOrphanTx(uint256 hash) +void static EraseOrphanTx(uint256 hash) { if (!mapOrphanTransactions.count(hash)) return; @@ -315,190 +236,6 @@ bool CTransaction::ReadFromDisk(COutPoint prevout) return ReadFromDisk(txdb, prevout, txindex); } -bool CTxIn::IsMine() const -{ - CRITICAL_BLOCK(cs_mapWallet) - { - map::iterator mi = mapWallet.find(prevout.hash); - if (mi != mapWallet.end()) - { - const CWalletTx& prev = (*mi).second; - if (prevout.n < prev.vout.size()) - if (prev.vout[prevout.n].IsMine()) - return true; - } - } - return false; -} - -int64 CTxIn::GetDebit() const -{ - CRITICAL_BLOCK(cs_mapWallet) - { - map::iterator mi = mapWallet.find(prevout.hash); - if (mi != mapWallet.end()) - { - const CWalletTx& prev = (*mi).second; - if (prevout.n < prev.vout.size()) - if (prev.vout[prevout.n].IsMine()) - return prev.vout[prevout.n].nValue; - } - } - return 0; -} - -int64 CWalletTx::GetTxTime() const -{ - if (!fTimeReceivedIsTxTime && hashBlock != 0) - { - // If we did not receive the transaction directly, we rely on the block's - // time to figure out when it happened. We use the median over a range - // of blocks to try to filter out inaccurate block times. - map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex) - return pindex->GetMedianTime(); - } - } - return nTimeReceived; -} - -int CWalletTx::GetRequestCount() const -{ - // Returns -1 if it wasn't being tracked - int nRequests = -1; - CRITICAL_BLOCK(cs_mapRequestCount) - { - if (IsCoinBase()) - { - // Generated block - if (hashBlock != 0) - { - map::iterator mi = mapRequestCount.find(hashBlock); - if (mi != mapRequestCount.end()) - nRequests = (*mi).second; - } - } - else - { - // Did anyone request this transaction? - map::iterator mi = mapRequestCount.find(GetHash()); - if (mi != mapRequestCount.end()) - { - nRequests = (*mi).second; - - // How about the block it's in? - if (nRequests == 0 && hashBlock != 0) - { - map::iterator mi = mapRequestCount.find(hashBlock); - if (mi != mapRequestCount.end()) - nRequests = (*mi).second; - else - nRequests = 1; // If it's in someone else's block it must have got out - } - } - } - } - return nRequests; -} - -void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list >& listReceived, - list >& listSent, int64& nFee, string& strSentAccount) const -{ - nGeneratedImmature = nGeneratedMature = nFee = 0; - listReceived.clear(); - listSent.clear(); - strSentAccount = strFromAccount; - - if (IsCoinBase()) - { - if (GetBlocksToMaturity() > 0) - nGeneratedImmature = CTransaction::GetCredit(); - else - nGeneratedMature = GetCredit(); - return; - } - - // Compute fee: - int64 nDebit = GetDebit(); - if (nDebit > 0) // debit>0 means we signed/sent this transaction - { - int64 nValueOut = GetValueOut(); - nFee = nDebit - nValueOut; - } - - // Sent/received. Standard client will never generate a send-to-multiple-recipients, - // but non-standard clients might (so return a list of address/amount pairs) - BOOST_FOREACH(const CTxOut& txout, vout) - { - string address; - uint160 hash160; - vector vchPubKey; - if (ExtractHash160(txout.scriptPubKey, hash160)) - address = Hash160ToAddress(hash160); - else if (ExtractPubKey(txout.scriptPubKey, false, vchPubKey)) - address = PubKeyToAddress(vchPubKey); - else - { - printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", - this->GetHash().ToString().c_str()); - address = " unknown "; - } - - // Don't report 'change' txouts - if (nDebit > 0 && txout.IsChange()) - continue; - - if (nDebit > 0) - listSent.push_back(make_pair(address, txout.nValue)); - - if (txout.IsMine()) - listReceived.push_back(make_pair(address, txout.nValue)); - } - -} - -void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, - int64& nSent, int64& nFee) const -{ - nGenerated = nReceived = nSent = nFee = 0; - - int64 allGeneratedImmature, allGeneratedMature, allFee; - allGeneratedImmature = allGeneratedMature = allFee = 0; - string strSentAccount; - list > listReceived; - list > listSent; - GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); - - if (strAccount == "") - nGenerated = allGeneratedMature; - if (strAccount == strSentAccount) - { - BOOST_FOREACH(const PAIRTYPE(string,int64)& s, listSent) - nSent += s.second; - nFee = allFee; - } - CRITICAL_BLOCK(cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) - { - if (mapAddressBook.count(r.first)) - { - if (mapAddressBook[r.first] == strAccount) - { - nReceived += r.second; - } - } - else if (strAccount.empty()) - { - nReceived += r.second; - } - } - } -} - int CMerkleTx::SetMerkleBranch(const CBlock* pblock) @@ -554,69 +291,6 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) -void CWalletTx::AddSupportingTransactions(CTxDB& txdb) -{ - vtxPrev.clear(); - - const int COPY_DEPTH = 3; - if (SetMerkleBranch() < COPY_DEPTH) - { - vector vWorkQueue; - BOOST_FOREACH(const CTxIn& txin, vin) - vWorkQueue.push_back(txin.prevout.hash); - - // This critsect is OK because txdb is already open - CRITICAL_BLOCK(cs_mapWallet) - { - map mapWalletPrev; - set setAlreadyDone; - for (int i = 0; i < vWorkQueue.size(); i++) - { - uint256 hash = vWorkQueue[i]; - if (setAlreadyDone.count(hash)) - continue; - setAlreadyDone.insert(hash); - - CMerkleTx tx; - if (mapWallet.count(hash)) - { - tx = mapWallet[hash]; - BOOST_FOREACH(const CMerkleTx& txWalletPrev, mapWallet[hash].vtxPrev) - mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; - } - else if (mapWalletPrev.count(hash)) - { - tx = *mapWalletPrev[hash]; - } - else if (!fClient && txdb.ReadDiskTx(hash, tx)) - { - ; - } - else - { - printf("ERROR: AddSupportingTransactions() : unsupported transaction\n"); - continue; - } - - int nDepth = tx.SetMerkleBranch(); - vtxPrev.push_back(tx); - - if (nDepth < COPY_DEPTH) - BOOST_FOREACH(const CTxIn& txin, tx.vin) - vWorkQueue.push_back(txin.prevout.hash); - } - } - } - - reverse(vtxPrev.begin(), vtxPrev.end()); -} - - - - - - - @@ -758,7 +432,7 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi nLastTime = nNow; // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB - if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe()) + if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(*this)) return error("AcceptToMemoryPool() : free transaction rejected by rate limiter"); if (fDebug) printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); @@ -781,12 +455,17 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi ///// are we sure this is ok when loading transactions or restoring block txes // If updated, erase old tx from wallet if (ptxOld) - EraseFromWallet(ptxOld->GetHash()); + EraseFromWallets(ptxOld->GetHash()); printf("AcceptToMemoryPool(): accepted %s\n", hash.ToString().substr(0,10).c_str()); return true; } +bool CTransaction::AcceptToMemoryPool(bool fCheckInputs, bool* pfMissingInputs) +{ + CTxDB txdb("r"); + return AcceptToMemoryPool(txdb, fCheckInputs, pfMissingInputs); +} bool CTransaction::AddToMemoryPoolUnchecked() { @@ -870,6 +549,12 @@ bool CMerkleTx::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs) } } +bool CMerkleTx::AcceptToMemoryPool() +{ + CTxDB txdb("r"); + return AcceptToMemoryPool(txdb); +} + bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) @@ -891,148 +576,10 @@ bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) return false; } -int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) -{ - int ret = 0; - - CBlockIndex* pindex = pindexStart; - CRITICAL_BLOCK(cs_mapWallet) - { - while (pindex) - { - CBlock block; - block.ReadFromDisk(pindex, true); - BOOST_FOREACH(CTransaction& tx, block.vtx) - { - if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) - ret++; - } - pindex = pindex->pnext; - } - } - return ret; -} - -void ReacceptWalletTransactions() -{ - CTxDB txdb("r"); - bool fRepeat = true; - while (fRepeat) CRITICAL_BLOCK(cs_mapWallet) - { - fRepeat = false; - vector vMissingTx; - BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) - { - CWalletTx& wtx = item.second; - if (wtx.IsCoinBase() && wtx.IsSpent(0)) - continue; - - CTxIndex txindex; - bool fUpdated = false; - if (txdb.ReadTxIndex(wtx.GetHash(), txindex)) - { - // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat - if (txindex.vSpent.size() != wtx.vout.size()) - { - printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size()); - continue; - } - for (int i = 0; i < txindex.vSpent.size(); i++) - { - if (wtx.IsSpent(i)) - continue; - if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine()) - { - wtx.MarkSpent(i); - fUpdated = true; - vMissingTx.push_back(txindex.vSpent[i]); - } - } - if (fUpdated) - { - printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); - wtx.MarkDirty(); - wtx.WriteToDisk(); - } - } - else - { - // Reaccept any txes of ours that aren't already in a block - if (!wtx.IsCoinBase()) - wtx.AcceptWalletTransaction(txdb, false); - } - } - if (!vMissingTx.empty()) - { - // TODO: optimize this to scan just part of the block chain? - if (ScanForWalletTransactions(pindexGenesisBlock)) - fRepeat = true; // Found missing transactions: re-do Reaccept. - } - } -} - - -void CWalletTx::RelayWalletTransaction(CTxDB& txdb) -{ - BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) - { - if (!tx.IsCoinBase()) - { - uint256 hash = tx.GetHash(); - if (!txdb.ContainsTx(hash)) - RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); - } - } - if (!IsCoinBase()) - { - uint256 hash = GetHash(); - if (!txdb.ContainsTx(hash)) - { - printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); - RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); - } - } -} - -void ResendWalletTransactions() +bool CWalletTx::AcceptWalletTransaction() { - // Do this infrequently and randomly to avoid giving away - // that these are our transactions. - static int64 nNextTime; - if (GetTime() < nNextTime) - return; - bool fFirst = (nNextTime == 0); - nNextTime = GetTime() + GetRand(30 * 60); - if (fFirst) - return; - - // Only do it if there's been a new block since last time - static int64 nLastTime; - if (nTimeBestReceived < nLastTime) - return; - nLastTime = GetTime(); - - // Rebroadcast any of our txes that aren't in a block yet - printf("ResendWalletTransactions()\n"); CTxDB txdb("r"); - CRITICAL_BLOCK(cs_mapWallet) - { - // Sort them in chronological order - multimap mapSorted; - BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) - { - CWalletTx& wtx = item.second; - // Don't rebroadcast until it's had plenty of time that - // it should have gotten in already by now. - if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) - mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); - } - BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) - { - CWalletTx& wtx = *item.second; - wtx.RelayWalletTransaction(txdb); - } - } + return AcceptWalletTransaction(txdb); } int CTxIndex::GetDepthInMainChain() const @@ -1079,7 +626,7 @@ bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions) return true; } -uint256 GetOrphanRoot(const CBlock* pblock) +uint256 static GetOrphanRoot(const CBlock* pblock) { // Work back to the first block in the orphan chain while (mapOrphanBlocks.count(pblock->hashPrevBlock)) @@ -1087,7 +634,7 @@ uint256 GetOrphanRoot(const CBlock* pblock) return pblock->GetHash(); } -int64 GetBlockValue(int nHeight, int64 nFees) +int64 static GetBlockValue(int nHeight, int64 nFees) { int64 nSubsidy = 50 * COIN; @@ -1097,7 +644,7 @@ int64 GetBlockValue(int nHeight, int64 nFees) return nSubsidy + nFees; } -unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast) +unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast) { const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks const int64 nTargetSpacing = 10 * 60; @@ -1187,7 +734,7 @@ bool IsInitialBlockDownload() pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); } -void InvalidChainFound(CBlockIndex* pindexNew) +void static InvalidChainFound(CBlockIndex* pindexNew) { if (pindexNew->bnChainWork > bnBestInvalidWork) { @@ -1463,12 +1010,12 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) // Watch for transactions paying to me BOOST_FOREACH(CTransaction& tx, vtx) - AddToWalletIfInvolvingMe(tx, this, true); + SyncWithWallets(tx, this, true); return true; } -bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) +bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) { printf("REORGANIZE\n"); @@ -1606,10 +1153,8 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) // Update best block in wallet (so we can detect restored wallets) if (!IsInitialBlockDownload()) { - CWalletDB walletdb; const CBlockLocator locator(pindexNew); - if (!walletdb.WriteBestBlock(locator)) - return error("SetBestChain() : WriteWalletBest failed"); + ::SetBestChain(locator); } // New best block @@ -1663,8 +1208,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) { // Notify UI to display prev block's coinbase if it was ours static uint256 hashPrevBestCoinBase; - CRITICAL_BLOCK(cs_mapWallet) - vWalletUpdated.push_back(hashPrevBestCoinBase); + UpdatedTransaction(hashPrevBestCoinBase); hashPrevBestCoinBase = vtx[0].GetHash(); } @@ -1773,7 +1317,7 @@ bool CBlock::AcceptBlock() return true; } -bool ProcessBlock(CNode* pfrom, CBlock* pblock) +bool static ProcessBlock(CNode* pfrom, CBlock* pblock) { // Check for duplicate uint256 hash = pblock->GetHash(); @@ -1835,7 +1379,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) template -bool ScanMessageStart(Stream& s) +bool static ScanMessageStart(Stream& s) { // Scan ahead to the next pchMessageStart, which should normally be immediately // at the file pointer. Leaves file pointer at end of pchMessageStart. @@ -2050,7 +1594,7 @@ void PrintBlockTree() for (int i = 0; i < nCol; i++) printf("| "); printf("|\n"); - } + } nPrevCol = nCol; // print columns @@ -2068,16 +1612,7 @@ void PrintBlockTree() DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(), block.vtx.size()); - CRITICAL_BLOCK(cs_mapWallet) - { - if (mapWallet.count(block.vtx[0].GetHash())) - { - CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; - printf(" mine: %d %d %d", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); - } - } - printf("\n"); - + PrintWallets(block); // put the main timechain first vector& vNext = mapNext[pindex]; @@ -2217,7 +1752,7 @@ bool CAlert::ProcessAlert() // -bool AlreadyHave(CTxDB& txdb, const CInv& inv) +bool static AlreadyHave(CTxDB& txdb, const CInv& inv) { switch (inv.type) { @@ -2237,128 +1772,7 @@ bool AlreadyHave(CTxDB& txdb, const CInv& inv) char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; -bool ProcessMessages(CNode* pfrom) -{ - CDataStream& vRecv = pfrom->vRecv; - if (vRecv.empty()) - return true; - //if (fDebug) - // printf("ProcessMessages(%u bytes)\n", vRecv.size()); - - // - // Message format - // (4) message start - // (12) command - // (4) size - // (4) checksum - // (x) data - // - - loop - { - // Scan for message start - CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart)); - int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader()); - if (vRecv.end() - pstart < nHeaderSize) - { - if (vRecv.size() > nHeaderSize) - { - printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n"); - vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); - } - break; - } - if (pstart - vRecv.begin() > 0) - printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", pstart - vRecv.begin()); - vRecv.erase(vRecv.begin(), pstart); - - // Read header - vector vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize); - CMessageHeader hdr; - vRecv >> hdr; - if (!hdr.IsValid()) - { - printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); - continue; - } - string strCommand = hdr.GetCommand(); - - // Message size - unsigned int nMessageSize = hdr.nMessageSize; - if (nMessageSize > MAX_SIZE) - { - printf("ProcessMessage(%s, %u bytes) : nMessageSize > MAX_SIZE\n", strCommand.c_str(), nMessageSize); - continue; - } - if (nMessageSize > vRecv.size()) - { - // Rewind and wait for rest of message - vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end()); - break; - } - - // Checksum - if (vRecv.GetVersion() >= 209) - { - uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); - unsigned int nChecksum = 0; - memcpy(&nChecksum, &hash, sizeof(nChecksum)); - if (nChecksum != hdr.nChecksum) - { - printf("ProcessMessage(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", - strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); - continue; - } - } - - // Copy message to its own buffer - CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion); - vRecv.ignore(nMessageSize); - - // Process message - bool fRet = false; - try - { - CRITICAL_BLOCK(cs_main) - fRet = ProcessMessage(pfrom, strCommand, vMsg); - if (fShutdown) - return true; - } - catch (std::ios_base::failure& e) - { - if (strstr(e.what(), "end of data")) - { - // Allow exceptions from underlength message on vRecv - printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what()); - } - else if (strstr(e.what(), "size too large")) - { - // Allow exceptions from overlong size - printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); - } - else - { - PrintExceptionContinue(&e, "ProcessMessage()"); - } - } - catch (std::exception& e) { - PrintExceptionContinue(&e, "ProcessMessage()"); - } catch (...) { - PrintExceptionContinue(NULL, "ProcessMessage()"); - } - - if (!fRet) - printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); - } - - vRecv.Compact(); - return true; -} - - - - -bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) +bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { static map > mapReuseKey; RandAddSeedPerfmon(); @@ -2555,12 +1969,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); // Track requests for our stuff - CRITICAL_BLOCK(cs_mapRequestCount) - { - map::iterator mi = mapRequestCount.find(inv.hash); - if (mi != mapRequestCount.end()) - (*mi).second++; - } + Inventory(inv.hash); } } @@ -2613,12 +2022,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } // Track requests for our stuff - CRITICAL_BLOCK(cs_mapRequestCount) - { - map::iterator mi = mapRequestCount.find(inv.hash); - if (mi != mapRequestCount.end()) - (*mi).second++; - } + Inventory(inv.hash); } } @@ -2706,7 +2110,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) bool fMissingInputs = false; if (tx.AcceptToMemoryPool(true, &fMissingInputs)) { - AddToWalletIfInvolvingMe(tx, NULL, true); + SyncWithWallets(tx, NULL, true); RelayMessage(inv, vMsg); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); @@ -2727,7 +2131,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (tx.AcceptToMemoryPool(true)) { printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); - AddToWalletIfInvolvingMe(tx, NULL, true); + SyncWithWallets(tx, NULL, true); RelayMessage(inv, vMsg); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); @@ -2804,7 +2208,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Keep giving the same key to the same ip until they use it if (!mapReuseKey.count(pfrom->addr.ip)) - mapReuseKey[pfrom->addr.ip] = GetKeyFromKeyPool(); + mapReuseKey[pfrom->addr.ip] = pwalletMain->GetKeyFromKeyPool(); // Send back approval of order and pubkey to use CScript scriptPubKey; @@ -2813,37 +2217,6 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } - else if (strCommand == "submitorder") - { - uint256 hashReply; - vRecv >> hashReply; - - if (!GetBoolArg("-allowreceivebyip")) - { - pfrom->PushMessage("reply", hashReply, (int)2); - return true; - } - - CWalletTx wtxNew; - vRecv >> wtxNew; - wtxNew.fFromMe = false; - - // Broadcast - if (!wtxNew.AcceptWalletTransaction()) - { - pfrom->PushMessage("reply", hashReply, (int)1); - return error("submitorder AcceptWalletTransaction() failed, returning error 1"); - } - wtxNew.fTimeReceivedIsTxTime = true; - AddToWallet(wtxNew); - wtxNew.RelayWalletTransaction(); - mapReuseKey.erase(pfrom->addr.ip); - - // Send back confirmation - pfrom->PushMessage("reply", hashReply, (int)0); - } - - else if (strCommand == "reply") { uint256 hashReply; @@ -2900,12 +2273,123 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return true; } +bool ProcessMessages(CNode* pfrom) +{ + CDataStream& vRecv = pfrom->vRecv; + if (vRecv.empty()) + return true; + //if (fDebug) + // printf("ProcessMessages(%u bytes)\n", vRecv.size()); + // + // Message format + // (4) message start + // (12) command + // (4) size + // (4) checksum + // (x) data + // + loop + { + // Scan for message start + CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart)); + int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader()); + if (vRecv.end() - pstart < nHeaderSize) + { + if (vRecv.size() > nHeaderSize) + { + printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n"); + vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); + } + break; + } + if (pstart - vRecv.begin() > 0) + printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", pstart - vRecv.begin()); + vRecv.erase(vRecv.begin(), pstart); + // Read header + vector vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize); + CMessageHeader hdr; + vRecv >> hdr; + if (!hdr.IsValid()) + { + printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); + continue; + } + string strCommand = hdr.GetCommand(); + // Message size + unsigned int nMessageSize = hdr.nMessageSize; + if (nMessageSize > MAX_SIZE) + { + printf("ProcessMessage(%s, %u bytes) : nMessageSize > MAX_SIZE\n", strCommand.c_str(), nMessageSize); + continue; + } + if (nMessageSize > vRecv.size()) + { + // Rewind and wait for rest of message + vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end()); + break; + } + // Checksum + if (vRecv.GetVersion() >= 209) + { + uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + if (nChecksum != hdr.nChecksum) + { + printf("ProcessMessage(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", + strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); + continue; + } + } + // Copy message to its own buffer + CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion); + vRecv.ignore(nMessageSize); + + // Process message + bool fRet = false; + try + { + CRITICAL_BLOCK(cs_main) + fRet = ProcessMessage(pfrom, strCommand, vMsg); + if (fShutdown) + return true; + } + catch (std::ios_base::failure& e) + { + if (strstr(e.what(), "end of data")) + { + // Allow exceptions from underlength message on vRecv + printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what()); + } + else if (strstr(e.what(), "size too large")) + { + // Allow exceptions from overlong size + printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); + } + else + { + PrintExceptionContinue(&e, "ProcessMessage()"); + } + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "ProcessMessage()"); + } catch (...) { + PrintExceptionContinue(NULL, "ProcessMessage()"); + } + + if (!fRet) + printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); + } + + vRecv.Compact(); + return true; +} bool SendMessages(CNode* pto, bool fSendTrickle) @@ -3030,16 +2514,10 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // always trickle our own transactions if (!fTrickleWait) { - TRY_CRITICAL_BLOCK(cs_mapWallet) - { - map::iterator mi = mapWallet.find(inv.hash); - if (mi != mapWallet.end()) - { - CWalletTx& wtx = (*mi).second; - if (wtx.fFromMe) - fTrickleWait = true; - } - } + CWalletTx wtx; + if (GetTransaction(inv.hash, wtx)) + if (wtx.fFromMe) + fTrickleWait = true; } if (fTrickleWait) @@ -3112,57 +2590,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // BitcoinMiner // -void GenerateBitcoins(bool fGenerate) -{ - if (fGenerateBitcoins != fGenerate) - { - fGenerateBitcoins = fGenerate; - CWalletDB().WriteSetting("fGenerateBitcoins", fGenerateBitcoins); - MainFrameRepaint(); - } - if (fGenerateBitcoins) - { - int nProcessors = boost::thread::hardware_concurrency(); - printf("%d processors\n", nProcessors); - if (nProcessors < 1) - nProcessors = 1; - if (fLimitProcessors && nProcessors > nLimitProcessors) - nProcessors = nLimitProcessors; - int nAddThreads = nProcessors - vnThreadsRunning[3]; - printf("Starting %d BitcoinMiner threads\n", nAddThreads); - for (int i = 0; i < nAddThreads; i++) - { - if (!CreateThread(ThreadBitcoinMiner, NULL)) - printf("Error: CreateThread(ThreadBitcoinMiner) failed\n"); - Sleep(10); - } - } -} - -void ThreadBitcoinMiner(void* parg) -{ - try - { - vnThreadsRunning[3]++; - BitcoinMiner(); - vnThreadsRunning[3]--; - } - catch (std::exception& e) { - vnThreadsRunning[3]--; - PrintException(&e, "ThreadBitcoinMiner()"); - } catch (...) { - vnThreadsRunning[3]--; - PrintException(NULL, "ThreadBitcoinMiner()"); - } - UIThreadCall(boost::bind(CalledSetStatusBar, "", 0)); - nHPSTimerStart = 0; - if (vnThreadsRunning[3] == 0) - dHashesPerSec = 0; - printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[3]); -} - - -int FormatHashBlocks(void* pbuffer, unsigned int len) +int static FormatHashBlocks(void* pbuffer, unsigned int len) { unsigned char* pdata = (unsigned char*)pbuffer; unsigned int blocks = 1 + ((len + 8) / 64); @@ -3195,7 +2623,7 @@ inline void SHA256Transform(void* pstate, void* pinput, const void* pinit) // between calls, but periodically or if nNonce is 0xffff0000 or above, // the block is rebuilt and nNonce starts over at zero. // -unsigned int ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone) +unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone) { unsigned int& nNonce = *(unsigned int*)(pdata + 12); for (;;) @@ -3452,7 +2880,7 @@ void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash } -bool CheckWork(CBlock* pblock, CReserveKey& reservekey) +bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) { uint256 hash = pblock->GetHash(); uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); @@ -3477,8 +2905,8 @@ bool CheckWork(CBlock* pblock, CReserveKey& reservekey) reservekey.KeepKey(); // Track how many getdata requests this block gets - CRITICAL_BLOCK(cs_mapRequestCount) - mapRequestCount[pblock->GetHash()] = 0; + CRITICAL_BLOCK(wallet.cs_mapRequestCount) + wallet.mapRequestCount[pblock->GetHash()] = 0; // Process this block the same as if we had received it from another node if (!ProcessBlock(NULL, pblock)) @@ -3489,14 +2917,15 @@ bool CheckWork(CBlock* pblock, CReserveKey& reservekey) return true; } +void static ThreadBitcoinMiner(void* parg); -void BitcoinMiner() +void static BitcoinMiner(CWallet *pwallet) { printf("BitcoinMiner started\n"); SetThreadPriority(THREAD_PRIORITY_LOWEST); // Each thread has its own key and counter - CReserveKey reservekey; + CReserveKey reservekey(pwallet); unsigned int nExtraNonce = 0; int64 nPrevTime = 0; @@ -3572,7 +3001,7 @@ void BitcoinMiner() assert(hash == pblock->GetHash()); SetThreadPriority(THREAD_PRIORITY_NORMAL); - CheckWork(pblock.get(), reservekey); + CheckWork(pblock.get(), *pwalletMain, reservekey); SetThreadPriority(THREAD_PRIORITY_LOWEST); break; } @@ -3633,421 +3062,53 @@ void BitcoinMiner() } } - - - - - - - - - - - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// Actions -// - - -int64 GetBalance() +void static ThreadBitcoinMiner(void* parg) { - int64 nStart = GetTimeMillis(); - - int64 nTotal = 0; - CRITICAL_BLOCK(cs_mapWallet) - { - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - CWalletTx* pcoin = &(*it).second; - if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) - continue; - nTotal += pcoin->GetAvailableCredit(); - } - } - - //printf("GetBalance() %"PRI64d"ms\n", GetTimeMillis() - nStart); - return nTotal; -} - - -bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) -{ - setCoinsRet.clear(); - nValueRet = 0; - - // List of values less than target - pair > coinLowestLarger; - coinLowestLarger.first = INT64_MAX; - coinLowestLarger.second.first = NULL; - vector > > vValue; - int64 nTotalLower = 0; - - CRITICAL_BLOCK(cs_mapWallet) - { - vector vCoins; - vCoins.reserve(mapWallet.size()); - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - vCoins.push_back(&(*it).second); - random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); - - BOOST_FOREACH(CWalletTx* pcoin, vCoins) - { - if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) - continue; - - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) - continue; - - int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) - continue; - - for (int i = 0; i < pcoin->vout.size(); i++) - { - if (pcoin->IsSpent(i) || !pcoin->vout[i].IsMine()) - continue; - - int64 n = pcoin->vout[i].nValue; - - if (n <= 0) - continue; - - pair > coin = make_pair(n,make_pair(pcoin,i)); - - if (n == nTargetValue) - { - setCoinsRet.insert(coin.second); - nValueRet += coin.first; - return true; - } - else if (n < nTargetValue + CENT) - { - vValue.push_back(coin); - nTotalLower += n; - } - else if (n < coinLowestLarger.first) - { - coinLowestLarger = coin; - } - } - } - } - - if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT) - { - for (int i = 0; i < vValue.size(); ++i) - { - setCoinsRet.insert(vValue[i].second); - nValueRet += vValue[i].first; - } - return true; - } - - if (nTotalLower < nTargetValue + (coinLowestLarger.second.first ? CENT : 0)) - { - if (coinLowestLarger.second.first == NULL) - return false; - setCoinsRet.insert(coinLowestLarger.second); - nValueRet += coinLowestLarger.first; - return true; - } - - if (nTotalLower >= nTargetValue + CENT) - nTargetValue += CENT; - - // Solve subset sum by stochastic approximation - sort(vValue.rbegin(), vValue.rend()); - vector vfIncluded; - vector vfBest(vValue.size(), true); - int64 nBest = nTotalLower; - - for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++) - { - vfIncluded.assign(vValue.size(), false); - int64 nTotal = 0; - bool fReachedTarget = false; - for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) - { - for (int i = 0; i < vValue.size(); i++) - { - if (nPass == 0 ? rand() % 2 : !vfIncluded[i]) - { - nTotal += vValue[i].first; - vfIncluded[i] = true; - if (nTotal >= nTargetValue) - { - fReachedTarget = true; - if (nTotal < nBest) - { - nBest = nTotal; - vfBest = vfIncluded; - } - nTotal -= vValue[i].first; - vfIncluded[i] = false; - } - } - } - } - } - - // If the next larger is still closer, return it - if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue) + CWallet* pwallet = (CWallet*)parg; + try { - setCoinsRet.insert(coinLowestLarger.second); - nValueRet += coinLowestLarger.first; + vnThreadsRunning[3]++; + BitcoinMiner(pwallet); + vnThreadsRunning[3]--; } - else { - for (int i = 0; i < vValue.size(); i++) - if (vfBest[i]) - { - setCoinsRet.insert(vValue[i].second); - nValueRet += vValue[i].first; - } - - //// debug print - printf("SelectCoins() best subset: "); - for (int i = 0; i < vValue.size(); i++) - if (vfBest[i]) - printf("%s ", FormatMoney(vValue[i].first).c_str()); - printf("total %s\n", FormatMoney(nBest).c_str()); + catch (std::exception& e) { + vnThreadsRunning[3]--; + PrintException(&e, "ThreadBitcoinMiner()"); + } catch (...) { + vnThreadsRunning[3]--; + PrintException(NULL, "ThreadBitcoinMiner()"); } - - return true; -} - -bool SelectCoins(int64 nTargetValue, set >& setCoinsRet, int64& nValueRet) -{ - return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet)); + UIThreadCall(boost::bind(CalledSetStatusBar, "", 0)); + nHPSTimerStart = 0; + if (vnThreadsRunning[3] == 0) + dHashesPerSec = 0; + printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[3]); } - - -bool CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +void GenerateBitcoins(bool fGenerate, CWallet* pwallet) { - int64 nValue = 0; - BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) - { - if (nValue < 0) - return false; - nValue += s.second; - } - if (vecSend.empty() || nValue < 0) - return false; - - CRITICAL_BLOCK(cs_main) + if (fGenerateBitcoins != fGenerate) { - // txdb must be opened before the mapWallet lock - CTxDB txdb("r"); - CRITICAL_BLOCK(cs_mapWallet) - { - nFeeRet = nTransactionFee; - loop - { - wtxNew.vin.clear(); - wtxNew.vout.clear(); - wtxNew.fFromMe = true; - - int64 nTotalValue = nValue + nFeeRet; - double dPriority = 0; - // vouts to the payees - BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) - wtxNew.vout.push_back(CTxOut(s.second, s.first)); - - // Choose coins to use - set > setCoins; - int64 nValueIn = 0; - if (!SelectCoins(nTotalValue, setCoins, nValueIn)) - return false; - BOOST_FOREACH(PAIRTYPE(CWalletTx*, unsigned int) pcoin, setCoins) - { - int64 nCredit = pcoin.first->vout[pcoin.second].nValue; - dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(); - } - - int64 nChange = nValueIn - nValue - nFeeRet; - - // if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE - // or until nChange becomes zero - if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT) - { - int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet); - nChange -= nMoveToFee; - nFeeRet += nMoveToFee; - } - - if (nChange > 0) - { - // Note: We use a new key here to keep it from being obvious which side is the change. - // The drawback is that by not reusing a previous key, the change may be lost if a - // backup is restored, if the backup doesn't have the new private key for the change. - // If we reused the old key, it would be possible to add code to look for and - // rediscover unknown transactions that were written with keys of ours to recover - // post-backup change. - - // Reserve a new key pair from key pool - vector vchPubKey = reservekey.GetReservedKey(); - assert(mapKeys.count(vchPubKey)); - - // Fill a vout to ourself, using same address type as the payment - CScript scriptChange; - if (vecSend[0].first.GetBitcoinAddressHash160() != 0) - scriptChange.SetBitcoinAddress(vchPubKey); - else - scriptChange << vchPubKey << OP_CHECKSIG; - - // Insert change txn at random position: - vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); - wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); - } - else - reservekey.ReturnKey(); - - // Fill vin - BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) - wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); - - // Sign - int nIn = 0; - BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) - if (!SignSignature(*coin.first, wtxNew, nIn++)) - return false; - - // Limit size - unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK); - if (nBytes >= MAX_BLOCK_SIZE_GEN/5) - return false; - dPriority /= nBytes; - - // Check that enough fee is included - int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); - bool fAllowFree = CTransaction::AllowFree(dPriority); - int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree); - if (nFeeRet < max(nPayFee, nMinFee)) - { - nFeeRet = max(nPayFee, nMinFee); - continue; - } - - // Fill vtxPrev by copying from previous transactions vtxPrev - wtxNew.AddSupportingTransactions(txdb); - wtxNew.fTimeReceivedIsTxTime = true; - - break; - } - } + fGenerateBitcoins = fGenerate; + WriteSetting("fGenerateBitcoins", fGenerateBitcoins); + MainFrameRepaint(); } - return true; -} - -bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) -{ - vector< pair > vecSend; - vecSend.push_back(make_pair(scriptPubKey, nValue)); - return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); -} - -// Call after CreateTransaction unless you want to abort -bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) -{ - CRITICAL_BLOCK(cs_main) + if (fGenerateBitcoins) { - printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); - CRITICAL_BLOCK(cs_mapWallet) - { - // This is only to keep the database open to defeat the auto-flush for the - // duration of this scope. This is the only place where this optimization - // maybe makes sense; please don't do it anywhere else. - CWalletDB walletdb("r"); - - // Take key pair from key pool so it won't be used again - reservekey.KeepKey(); - - // Add tx to wallet, because if it has change it's also ours, - // otherwise just for transaction history. - AddToWallet(wtxNew); - - // Mark old coins as spent - set setCoins; - BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) - { - CWalletTx &pcoin = mapWallet[txin.prevout.hash]; - pcoin.MarkSpent(txin.prevout.n); - pcoin.WriteToDisk(); - vWalletUpdated.push_back(pcoin.GetHash()); - } - } - - // Track how many getdata requests our transaction gets - CRITICAL_BLOCK(cs_mapRequestCount) - mapRequestCount[wtxNew.GetHash()] = 0; - - // Broadcast - if (!wtxNew.AcceptToMemoryPool()) + int nProcessors = boost::thread::hardware_concurrency(); + printf("%d processors\n", nProcessors); + if (nProcessors < 1) + nProcessors = 1; + if (fLimitProcessors && nProcessors > nLimitProcessors) + nProcessors = nLimitProcessors; + int nAddThreads = nProcessors - vnThreadsRunning[3]; + printf("Starting %d BitcoinMiner threads\n", nAddThreads); + for (int i = 0; i < nAddThreads; i++) { - // This must not fail. The transaction has already been signed and recorded. - printf("CommitTransaction() : Error: Transaction not valid"); - return false; + if (!CreateThread(ThreadBitcoinMiner, pwallet)) + printf("Error: CreateThread(ThreadBitcoinMiner) failed\n"); + Sleep(10); } - wtxNew.RelayWalletTransaction(); } - MainFrameRepaint(); - return true; -} - - - - -// requires cs_main lock -string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) -{ - CReserveKey reservekey; - int64 nFeeRequired; - if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired)) - { - string strError; - if (nValue + nFeeRequired > 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("SendMoney() : %s", strError.c_str()); - return strError; - } - - if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL)) - return "ABORTED"; - - if (!CommitTransaction(wtxNew, reservekey)) - return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); - - MainFrameRepaint(); - return ""; -} - - - -// requires cs_main lock -string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee) -{ - // Check amount - if (nValue <= 0) - return _("Invalid amount"); - if (nValue + nTransactionFee > GetBalance()) - return _("Insufficient funds"); - - // Parse bitcoin address - CScript scriptPubKey; - if (!scriptPubKey.SetBitcoinAddress(strAddress)) - return _("Invalid bitcoin address"); - - return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); } diff --git a/src/main.h b/src/main.h index 73935bcee2..aa74ac5ab3 100644 --- a/src/main.h +++ b/src/main.h @@ -7,22 +7,18 @@ #include "bignum.h" #include "net.h" #include "key.h" -#include "db.h" #include "script.h" +#include "db.h" #include -class COutPoint; -class CInPoint; -class CDiskTxPos; -class CCoinBase; -class CTxIn; -class CTxOut; -class CTransaction; class CBlock; class CBlockIndex; class CWalletTx; +class CWallet; class CKeyItem; +class CReserveKey; +class CWalletDB; class CMessageHeader; class CAddress; @@ -63,13 +59,11 @@ extern CBigNum bnBestInvalidWork; extern uint256 hashBestChain; extern CBlockIndex* pindexBest; extern unsigned int nTransactionsUpdated; -extern std::map mapRequestCount; -extern CCriticalSection cs_mapRequestCount; -extern std::map mapAddressBook; -extern CCriticalSection cs_mapAddressBook; -extern std::vector vchDefaultKey; extern double dHashesPerSec; extern int64 nHPSTimerStart; +extern int64 nTimeBestReceived; +extern CCriticalSection cs_setpwalletRegistered; +extern std::set setpwalletRegistered; // Settings extern int fGenerateBitcoins; @@ -89,34 +83,20 @@ class CReserveKey; class CTxDB; class CTxIndex; +void RegisterWallet(CWallet* pwalletIn); +void UnregisterWallet(CWallet* pwalletIn); bool CheckDiskSpace(uint64 nAdditionalBytes=0); FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); FILE* AppendBlockFile(unsigned int& nFileRet); -bool AddKey(const CKey& key); -std::vector GenerateNewKey(); -bool AddToWallet(const CWalletTx& wtxIn); -void WalletUpdateSpent(const COutPoint& prevout); -int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); -void ReacceptWalletTransactions(); bool LoadBlockIndex(bool fAllowNew=true); void PrintBlockTree(); bool ProcessMessages(CNode* pfrom); -bool ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vRecv); bool SendMessages(CNode* pto, bool fSendTrickle); -int64 GetBalance(); -bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); -bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); -bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); -bool BroadcastTransaction(CWalletTx& wtxNew); -std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); -std::string SendMoneyToBitcoinAddress(std::string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); -void GenerateBitcoins(bool fGenerate); -void ThreadBitcoinMiner(void* parg); +void GenerateBitcoins(bool fGenerate, CWallet* pwallet); CBlock* CreateNewBlock(CReserveKey& reservekey); void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, int64& nPrevTime); void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); -bool CheckWork(CBlock* pblock, CReserveKey& reservekey); -void BitcoinMiner(); +bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); bool CheckProofOfWork(uint256 hash, unsigned int nBits); int GetTotalBlocksEstimate(); bool IsInitialBlockDownload(); @@ -133,6 +113,23 @@ std::string GetWarnings(std::string strFor); +bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); + +template +bool WriteSetting(const std::string& strKey, const T& value) +{ + bool fOk = false; + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + { + std::string strWalletFile; + if (!GetWalletFile(pwallet, strWalletFile)) + continue; + fOk |= CWalletDB(strWalletFile).WriteSetting(strKey, value); + } + return fOk; +} + + class CDiskTxPos { public: @@ -315,9 +312,6 @@ public: { printf("%s\n", ToString().c_str()); } - - bool IsMine() const; - int64 GetDebit() const; }; @@ -366,36 +360,6 @@ public: return SerializeHash(*this); } - bool IsMine() const - { - return ::IsMine(scriptPubKey); - } - - int64 GetCredit() const - { - if (!MoneyRange(nValue)) - throw std::runtime_error("CTxOut::GetCredit() : value out of range"); - return (IsMine() ? nValue : 0); - } - - bool IsChange() const - { - // On a debit transaction, a txout that's mine but isn't in the address book is change - std::vector vchPubKey; - if (ExtractPubKey(scriptPubKey, true, vchPubKey)) - CRITICAL_BLOCK(cs_mapAddressBook) - if (!mapAddressBook.count(PubKeyToAddress(vchPubKey))) - return true; - return false; - } - - int64 GetChange() const - { - if (!MoneyRange(nValue)) - throw std::runtime_error("CTxOut::GetChange() : value out of range"); - return (IsChange() ? nValue : 0); - } - friend bool operator==(const CTxOut& a, const CTxOut& b) { return (a.nValue == b.nValue && @@ -540,57 +504,6 @@ public: return true; } - bool IsMine() const - { - BOOST_FOREACH(const CTxOut& txout, vout) - if (txout.IsMine()) - return true; - return false; - } - - bool IsFromMe() const - { - return (GetDebit() > 0); - } - - int64 GetDebit() const - { - int64 nDebit = 0; - BOOST_FOREACH(const CTxIn& txin, vin) - { - nDebit += txin.GetDebit(); - if (!MoneyRange(nDebit)) - throw std::runtime_error("CTransaction::GetDebit() : value out of range"); - } - return nDebit; - } - - int64 GetCredit() const - { - int64 nCredit = 0; - BOOST_FOREACH(const CTxOut& txout, vout) - { - nCredit += txout.GetCredit(); - if (!MoneyRange(nCredit)) - throw std::runtime_error("CTransaction::GetCredit() : value out of range"); - } - return nCredit; - } - - int64 GetChange() const - { - if (IsCoinBase()) - return 0; - int64 nChange = 0; - BOOST_FOREACH(const CTxOut& txout, vout) - { - nChange += txout.GetChange(); - if (!MoneyRange(nChange)) - throw std::runtime_error("CTransaction::GetChange() : value out of range"); - } - return nChange; - } - int64 GetValueOut() const { int64 nValueOut = 0; @@ -722,11 +635,7 @@ public: bool ClientConnectInputs(); bool CheckTransaction() const; bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); - bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL) - { - CTxDB txdb("r"); - return AcceptToMemoryPool(txdb, fCheckInputs, pfMissingInputs); - } + bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL); protected: bool AddToMemoryPoolUnchecked(); public: @@ -785,307 +694,7 @@ public: bool IsInMainChain() const { return GetDepthInMainChain() > 0; } int GetBlocksToMaturity() const; bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true); - bool AcceptToMemoryPool() { CTxDB txdb("r"); return AcceptToMemoryPool(txdb); } -}; - - - - -// -// A transaction with a bunch of additional info that only the owner cares -// about. It includes any unrecorded transactions needed to link it back -// to the block chain. -// -class CWalletTx : public CMerkleTx -{ -public: - std::vector vtxPrev; - std::map mapValue; - std::vector > vOrderForm; - unsigned int fTimeReceivedIsTxTime; - unsigned int nTimeReceived; // time received by this node - char fFromMe; - std::string strFromAccount; - std::vector vfSpent; - - // memory only - mutable char fDebitCached; - mutable char fCreditCached; - mutable char fAvailableCreditCached; - mutable char fChangeCached; - mutable int64 nDebitCached; - mutable int64 nCreditCached; - mutable int64 nAvailableCreditCached; - mutable int64 nChangeCached; - - // memory only UI hints - mutable unsigned int nTimeDisplayed; - mutable int nLinesDisplayed; - mutable char fConfirmedDisplayed; - - - CWalletTx() - { - Init(); - } - - CWalletTx(const CMerkleTx& txIn) : CMerkleTx(txIn) - { - Init(); - } - - CWalletTx(const CTransaction& txIn) : CMerkleTx(txIn) - { - Init(); - } - - void Init() - { - vtxPrev.clear(); - mapValue.clear(); - vOrderForm.clear(); - fTimeReceivedIsTxTime = false; - nTimeReceived = 0; - fFromMe = false; - strFromAccount.clear(); - vfSpent.clear(); - fDebitCached = false; - fCreditCached = false; - fAvailableCreditCached = false; - fChangeCached = false; - nDebitCached = 0; - nCreditCached = 0; - nAvailableCreditCached = 0; - nChangeCached = 0; - nTimeDisplayed = 0; - nLinesDisplayed = 0; - fConfirmedDisplayed = false; - } - - IMPLEMENT_SERIALIZE - ( - CWalletTx* pthis = const_cast(this); - if (fRead) - pthis->Init(); - char fSpent = false; - - if (!fRead) - { - pthis->mapValue["fromaccount"] = pthis->strFromAccount; - - std::string str; - BOOST_FOREACH(char f, vfSpent) - { - str += (f ? '1' : '0'); - if (f) - fSpent = true; - } - pthis->mapValue["spent"] = str; - } - - nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action); - READWRITE(vtxPrev); - READWRITE(mapValue); - READWRITE(vOrderForm); - READWRITE(fTimeReceivedIsTxTime); - READWRITE(nTimeReceived); - READWRITE(fFromMe); - READWRITE(fSpent); - - if (fRead) - { - pthis->strFromAccount = pthis->mapValue["fromaccount"]; - - if (mapValue.count("spent")) - BOOST_FOREACH(char c, pthis->mapValue["spent"]) - pthis->vfSpent.push_back(c != '0'); - else - pthis->vfSpent.assign(vout.size(), fSpent); - } - - pthis->mapValue.erase("fromaccount"); - pthis->mapValue.erase("version"); - pthis->mapValue.erase("spent"); - ) - - // marks certain txout's as spent - // returns true if any update took place - bool UpdateSpent(const std::vector& vfNewSpent) - { - bool fReturn = false; - for (int i=0; i < vfNewSpent.size(); i++) - { - if (i == vfSpent.size()) - break; - - if (vfNewSpent[i] && !vfSpent[i]) - { - vfSpent[i] = true; - fReturn = true; - fAvailableCreditCached = false; - } - } - return fReturn; - } - - void MarkDirty() - { - fCreditCached = false; - fAvailableCreditCached = false; - fDebitCached = false; - fChangeCached = false; - } - - void MarkSpent(unsigned int nOut) - { - if (nOut >= vout.size()) - throw std::runtime_error("CWalletTx::MarkSpent() : nOut out of range"); - vfSpent.resize(vout.size()); - if (!vfSpent[nOut]) - { - vfSpent[nOut] = true; - fAvailableCreditCached = false; - } - } - - bool IsSpent(unsigned int nOut) const - { - if (nOut >= vout.size()) - throw std::runtime_error("CWalletTx::IsSpent() : nOut out of range"); - if (nOut >= vfSpent.size()) - return false; - return (!!vfSpent[nOut]); - } - - int64 GetDebit() const - { - if (vin.empty()) - return 0; - if (fDebitCached) - return nDebitCached; - nDebitCached = CTransaction::GetDebit(); - fDebitCached = true; - return nDebitCached; - } - - int64 GetCredit(bool fUseCache=true) const - { - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) - return 0; - - // GetBalance can assume transactions in mapWallet won't change - if (fUseCache && fCreditCached) - return nCreditCached; - nCreditCached = CTransaction::GetCredit(); - fCreditCached = true; - return nCreditCached; - } - - int64 GetAvailableCredit(bool fUseCache=true) const - { - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) - return 0; - - if (fUseCache && fAvailableCreditCached) - return nAvailableCreditCached; - - int64 nCredit = 0; - for (int i = 0; i < vout.size(); i++) - { - if (!IsSpent(i)) - { - const CTxOut &txout = vout[i]; - nCredit += txout.GetCredit(); - if (!MoneyRange(nCredit)) - throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); - } - } - - nAvailableCreditCached = nCredit; - fAvailableCreditCached = true; - return nCredit; - } - - - int64 GetChange() const - { - if (fChangeCached) - return nChangeCached; - nChangeCached = CTransaction::GetChange(); - fChangeCached = true; - return nChangeCached; - } - - void GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, std::list >& listReceived, - std::list >& listSent, int64& nFee, std::string& strSentAccount) const; - - void GetAccountAmounts(const std::string& strAccount, int64& nGenerated, int64& nReceived, - int64& nSent, int64& nFee) const; - - bool IsFromMe() const - { - return (GetDebit() > 0); - } - - bool IsConfirmed() const - { - // Quick answer in most cases - if (!IsFinal()) - return false; - if (GetDepthInMainChain() >= 1) - return true; - if (!IsFromMe()) // using wtx's cached debit - return false; - - // If no confirmations but it's from us, we can still - // consider it confirmed if all dependencies are confirmed - std::map mapPrev; - std::vector vWorkQueue; - vWorkQueue.reserve(vtxPrev.size()+1); - vWorkQueue.push_back(this); - for (int i = 0; i < vWorkQueue.size(); i++) - { - const CMerkleTx* ptx = vWorkQueue[i]; - - if (!ptx->IsFinal()) - return false; - if (ptx->GetDepthInMainChain() >= 1) - continue; - if (!ptx->IsFromMe()) - return false; - - if (mapPrev.empty()) - BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) - mapPrev[tx.GetHash()] = &tx; - - BOOST_FOREACH(const CTxIn& txin, ptx->vin) - { - if (!mapPrev.count(txin.prevout.hash)) - return false; - vWorkQueue.push_back(mapPrev[txin.prevout.hash]); - } - } - return true; - } - - bool WriteToDisk() - { - return CWalletDB().WriteTx(GetHash(), *this); - } - - - int64 GetTxTime() const; - int GetRequestCount() const; - - void AddSupportingTransactions(CTxDB& txdb); - - bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true); - bool AcceptWalletTransaction() { CTxDB txdb("r"); return AcceptWalletTransaction(txdb); } - - void RelayWalletTransaction(CTxDB& txdb); - void RelayWalletTransaction() { CTxDB txdb("r"); RelayWalletTransaction(txdb); } + bool AcceptToMemoryPool(); }; @@ -1745,114 +1354,6 @@ public: -// -// Private key that includes an expiration date in case it never gets used. -// -class CWalletKey -{ -public: - CPrivKey vchPrivKey; - int64 nTimeCreated; - int64 nTimeExpires; - std::string strComment; - //// todo: add something to note what created it (user, getnewaddress, change) - //// maybe should have a map property map - - CWalletKey(int64 nExpires=0) - { - nTimeCreated = (nExpires ? GetTime() : 0); - nTimeExpires = nExpires; - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vchPrivKey); - READWRITE(nTimeCreated); - READWRITE(nTimeExpires); - READWRITE(strComment); - ) -}; - - - - - - -// -// Account information. -// Stored in wallet with key "acc"+string account name -// -class CAccount -{ -public: - std::vector vchPubKey; - - CAccount() - { - SetNull(); - } - - void SetNull() - { - vchPubKey.clear(); - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vchPubKey); - ) -}; - - - -// -// Internal transfers. -// Database key is acentry -// -class CAccountingEntry -{ -public: - std::string strAccount; - int64 nCreditDebit; - int64 nTime; - std::string strOtherAccount; - std::string strComment; - - CAccountingEntry() - { - SetNull(); - } - - void SetNull() - { - nCreditDebit = 0; - nTime = 0; - strAccount.clear(); - strOtherAccount.clear(); - strComment.clear(); - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - // Note: strAccount is serialized as part of the key, not here. - READWRITE(nCreditDebit); - READWRITE(nTime); - READWRITE(strOtherAccount); - READWRITE(strComment); - ) -}; - - - - - - @@ -2064,13 +1565,9 @@ public: + extern std::map mapTransactions; -extern std::map mapWallet; -extern std::vector vWalletUpdated; -extern CCriticalSection cs_mapWallet; -extern std::map, CPrivKey> mapKeys; extern std::map > mapPubKeys; -extern CCriticalSection cs_mapKeys; -extern CKey keyUser; +extern CCriticalSection cs_mapPubKeys; #endif diff --git a/src/net.cpp b/src/net.cpp index 8b439efdca..4b13726230 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1117,7 +1117,7 @@ void MapPort(bool fMapPort) if (fUseUPnP != fMapPort) { fUseUPnP = fMapPort; - CWalletDB().WriteSetting("fUseUPnP", fUseUPnP); + WriteSetting("fUseUPnP", fUseUPnP); } if (fUseUPnP && vnThreadsRunning[5] < 1) { @@ -1698,7 +1698,7 @@ void StartNode(void* parg) printf("Error: CreateThread(ThreadMessageHandler) failed\n"); // Generate coins in the background - GenerateBitcoins(fGenerateBitcoins); + GenerateBitcoins(fGenerateBitcoins, pwalletMain); } bool StopNode() diff --git a/src/noui.h b/src/noui.h index afb19526c1..d0072df7f2 100644 --- a/src/noui.h +++ b/src/noui.h @@ -5,6 +5,8 @@ #define BITCOIN_NOUI_H #include +#include +#include "wallet.h" typedef void wxWindow; #define wxYES 0x00000002 diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 1cd82b7626..eece092f0d 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -1,6 +1,7 @@ #include "addresstablemodel.h" #include "guiutil.h" -#include "main.h" + +#include "headers.h" #include #include @@ -22,31 +23,25 @@ struct AddressTableEntry AddressTableEntry() {} AddressTableEntry(Type type, const QString &label, const QString &address): type(type), label(label), address(address) {} - - bool isDefaultAddress() const - { - std::vector vchPubKey; - if (CWalletDB("r").ReadDefaultKey(vchPubKey)) - { - return address == QString::fromStdString(PubKeyToAddress(vchPubKey)); - } - return false; - } }; // Private implementation struct AddressTablePriv { + CWallet *wallet; QList cachedAddressTable; + AddressTablePriv(CWallet *wallet): + wallet(wallet) {} + void refreshAddressTable() { cachedAddressTable.clear(); - CRITICAL_BLOCK(cs_mapKeys) - CRITICAL_BLOCK(cs_mapAddressBook) + CRITICAL_BLOCK(wallet->cs_mapKeys) + CRITICAL_BLOCK(wallet->cs_mapAddressBook) { - BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item, mapAddressBook) + BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item, wallet->mapAddressBook) { std::string strAddress = item.first; std::string strName = item.second; @@ -75,13 +70,18 @@ struct AddressTablePriv return 0; } } + + bool isDefaultAddress(const AddressTableEntry *rec) + { + return rec->address == QString::fromStdString(wallet->GetDefaultAddress()); + } }; -AddressTableModel::AddressTableModel(QObject *parent) : - QAbstractTableModel(parent),priv(0) +AddressTableModel::AddressTableModel(CWallet *wallet, QObject *parent) : + QAbstractTableModel(parent),wallet(wallet),priv(0) { columns << tr("Label") << tr("Address"); - priv = new AddressTablePriv(); + priv = new AddressTablePriv(wallet); priv->refreshAddressTable(); } @@ -118,7 +118,7 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const case Address: return rec->address; case IsDefaultAddress: - return rec->isDefaultAddress(); + return priv->isDefaultAddress(rec); } } else if (role == Qt::FontRole) @@ -128,7 +128,7 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const { font = GUIUtil::bitcoinAddressFont(); } - if(rec->isDefaultAddress()) + if(priv->isDefaultAddress(rec)) { font.setBold(true); } @@ -137,14 +137,14 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const else if (role == Qt::ForegroundRole) { // Show default address in alternative color - if(rec->isDefaultAddress()) + if(priv->isDefaultAddress(rec)) { return QColor(0,0,255); } } else if (role == Qt::ToolTipRole) { - if(rec->isDefaultAddress()) + if(priv->isDefaultAddress(rec)) { return tr("Default receiving address"); } @@ -174,7 +174,7 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu switch(index.column()) { case Label: - SetAddressBookName(rec->address.toStdString(), value.toString().toStdString()); + wallet->SetAddressBookName(rec->address.toStdString(), value.toString().toStdString()); rec->label = value.toString(); break; case Address: @@ -182,9 +182,9 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu if(rec->type == AddressTableEntry::Sending) { // Remove old entry - CWalletDB().EraseName(rec->address.toStdString()); + wallet->EraseAddressBookName(rec->address.toStdString()); // Add new entry with new address - SetAddressBookName(value.toString().toStdString(), rec->label.toStdString()); + wallet->SetAddressBookName(value.toString().toStdString(), rec->label.toStdString()); rec->address = value.toString(); } @@ -245,9 +245,9 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con if(type == Send) { // Check for duplicate - CRITICAL_BLOCK(cs_mapAddressBook) + CRITICAL_BLOCK(wallet->cs_mapAddressBook) { - if(mapAddressBook.count(strAddress)) + if(wallet->mapAddressBook.count(strAddress)) { return QString(); } @@ -257,7 +257,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con { // Generate a new address to associate with given label, optionally // set as default receiving address. - strAddress = PubKeyToAddress(GetKeyFromKeyPool()); + strAddress = PubKeyToAddress(wallet->GetKeyFromKeyPool()); if(setAsDefault) { setDefaultAddress(QString::fromStdString(strAddress)); @@ -268,7 +268,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con return QString(); } // Add entry and update list - SetAddressBookName(strAddress, strLabel); + wallet->SetAddressBookName(strAddress, strLabel); updateList(); return QString::fromStdString(strAddress); } @@ -283,33 +283,19 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex & paren // Also refuse to remove receiving addresses. return false; } - CWalletDB().EraseName(rec->address.toStdString()); + wallet->EraseAddressBookName(rec->address.toStdString()); updateList(); return true; } QString AddressTableModel::getDefaultAddress() const { - std::vector vchPubKey; - if (CWalletDB("r").ReadDefaultKey(vchPubKey)) - { - return QString::fromStdString(PubKeyToAddress(vchPubKey)); - } - else - { - return QString(); - } + return QString::fromStdString(wallet->GetDefaultAddress()); } void AddressTableModel::setDefaultAddress(const QString &defaultAddress) { - uint160 hash160; - std::string strAddress = defaultAddress.toStdString(); - if (!AddressToHash160(strAddress, hash160)) - return; - if (!mapPubKeys.count(hash160)) - return; - CWalletDB().WriteDefaultKey(mapPubKeys[hash160]); + wallet->SetDefaultAddress(defaultAddress.toStdString()); } void AddressTableModel::update() diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 32dd4d9f1d..d8465853b2 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -5,12 +5,13 @@ #include class AddressTablePriv; +class CWallet; class AddressTableModel : public QAbstractTableModel { Q_OBJECT public: - explicit AddressTableModel(QObject *parent = 0); + explicit AddressTableModel(CWallet *wallet, QObject *parent = 0); ~AddressTableModel(); enum ColumnIndex { @@ -49,6 +50,7 @@ public: void updateList(); private: + CWallet *wallet; AddressTablePriv *priv; QStringList columns; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 7d5712f4aa..c31be1b606 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -3,10 +3,9 @@ */ #include "bitcoingui.h" #include "clientmodel.h" -#include "util.h" + +#include "headers.h" #include "init.h" -#include "main.h" -#include "qtui.h" #include #include @@ -114,7 +113,7 @@ int main(int argc, char *argv[]) if(AppInit2(argc, argv)) { BitcoinGUI window; - ClientModel model; + ClientModel model(pwalletMain); guiref = &window; window.setModel(&model); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 1b4e13c420..76ae3ddb3f 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -16,7 +16,7 @@ #include "transactiondescdialog.h" #include "addresstablemodel.h" -#include "main.h" +#include "headers.h" #include #include diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index e39bb7ecb1..b70b71ee4f 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -1,14 +1,15 @@ #include "clientmodel.h" -#include "main.h" #include "guiconstants.h" #include "optionsmodel.h" #include "addresstablemodel.h" #include "transactiontablemodel.h" +#include "headers.h" + #include -ClientModel::ClientModel(QObject *parent) : - QObject(parent), optionsModel(0), addressTableModel(0), +ClientModel::ClientModel(CWallet *wallet, QObject *parent) : + QObject(parent), wallet(wallet), optionsModel(0), addressTableModel(0), transactionTableModel(0) { // Until signal notifications is built into the bitcoin core, @@ -17,14 +18,14 @@ ClientModel::ClientModel(QObject *parent) : connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(MODEL_UPDATE_DELAY); - optionsModel = new OptionsModel(this); - addressTableModel = new AddressTableModel(this); - transactionTableModel = new TransactionTableModel(this); + optionsModel = new OptionsModel(wallet, this); + addressTableModel = new AddressTableModel(wallet, this); + transactionTableModel = new TransactionTableModel(wallet, this); } qint64 ClientModel::getBalance() const { - return GetBalance(); + return wallet->GetBalance(); } int ClientModel::getNumConnections() const @@ -40,9 +41,9 @@ int ClientModel::getNumBlocks() const int ClientModel::getNumTransactions() const { int numTransactions = 0; - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(wallet->cs_mapWallet) { - numTransactions = mapWallet.size(); + numTransactions = wallet->mapWallet.size(); } return numTransactions; } @@ -92,7 +93,7 @@ ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payA CScript scriptPubKey; scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; - std::string strError = SendMoney(scriptPubKey, payAmount, wtx, true); + std::string strError = wallet->SendMoney(scriptPubKey, payAmount, wtx, true); if (strError == "") { // OK @@ -110,9 +111,11 @@ ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payA // Add addresses that we've sent to to the address book std::string strAddress = payTo.toStdString(); - CRITICAL_BLOCK(cs_mapAddressBook) - if (!mapAddressBook.count(strAddress)) - SetAddressBookName(strAddress, addToAddressBookAs.toStdString()); + CRITICAL_BLOCK(wallet->cs_mapAddressBook) + { + if (!wallet->mapAddressBook.count(strAddress)) + wallet->SetAddressBookName(strAddress, addToAddressBookAs.toStdString()); + } return OK; } diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index da3e52e20e..9c23a14a0a 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -6,12 +6,13 @@ class OptionsModel; class AddressTableModel; class TransactionTableModel; +class CWallet; class ClientModel : public QObject { Q_OBJECT public: - explicit ClientModel(QObject *parent = 0); + explicit ClientModel(CWallet *wallet, QObject *parent = 0); enum StatusCode { @@ -41,6 +42,8 @@ public: /* Send coins */ StatusCode sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs=QString()); private: + CWallet *wallet; + OptionsModel *optionsModel; AddressTableModel *addressTableModel; TransactionTableModel *transactionTableModel; diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 3788f9fd7c..8f285c6446 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -1,11 +1,12 @@ #include "optionsmodel.h" -#include "main.h" -#include "net.h" + +#include "headers.h" #include -OptionsModel::OptionsModel(QObject *parent) : - QAbstractListModel(parent) +OptionsModel::OptionsModel(CWallet *wallet, QObject *parent) : + QAbstractListModel(parent), + wallet(wallet) { } @@ -48,7 +49,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in bool successful = true; /* set to false on parse error */ if(role == Qt::EditRole) { - CWalletDB walletdb; + CWalletDB walletdb(wallet->strWalletFile); switch(index.row()) { case StartAtStartup: diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 0124e2ab47..bdb797a2de 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -3,6 +3,8 @@ #include +class CWallet; + /* Interface from QT to configuration data structure for bitcoin client. To QT, the options are presented as a list with the different options laid out vertically. @@ -13,7 +15,7 @@ class OptionsModel : public QAbstractListModel { Q_OBJECT public: - explicit OptionsModel(QObject *parent = 0); + explicit OptionsModel(CWallet *wallet, QObject *parent = 0); enum OptionID { StartAtStartup, @@ -35,6 +37,9 @@ public: qint64 getTransactionFee(); bool getMinimizeToTray(); bool getMinimizeOnClose(); +private: + // Wallet stores persistent options + CWallet *wallet; signals: public slots: diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 84b617b7c4..bb2537a479 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -70,10 +70,10 @@ static string FormatTxStatus(const CWalletTx& wtx) } } -string TransactionDesc::toHTML(CWalletTx &wtx) +string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) { string strHTML; - CRITICAL_BLOCK(cs_mapAddressBook) + CRITICAL_BLOCK(wallet->cs_mapAddressBook) { strHTML.reserve(4000); strHTML += ""; @@ -122,19 +122,19 @@ string TransactionDesc::toHTML(CWalletTx &wtx) // Credit BOOST_FOREACH(const CTxOut& txout, wtx.vout) { - if (txout.IsMine()) + if (wallet->IsMine(txout)) { vector vchPubKey; - if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) + if (ExtractPubKey(txout.scriptPubKey, wallet, vchPubKey)) { string strAddress = PubKeyToAddress(vchPubKey); - if (mapAddressBook.count(strAddress)) + if (wallet->mapAddressBook.count(strAddress)) { strHTML += string() + _("From: ") + _("unknown") + "
"; strHTML += _("To: "); strHTML += HtmlEscape(strAddress); - if (!mapAddressBook[strAddress].empty()) - strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")"; + if (!wallet->mapAddressBook[strAddress].empty()) + strHTML += _(" (yours, label: ") + wallet->mapAddressBook[strAddress] + ")"; else strHTML += _(" (yours)"); strHTML += "
"; @@ -156,8 +156,8 @@ string TransactionDesc::toHTML(CWalletTx &wtx) // Online transaction strAddress = wtx.mapValue["to"]; strHTML += _("To: "); - if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty()) - strHTML += mapAddressBook[strAddress] + " "; + if (wallet->mapAddressBook.count(strAddress) && !wallet->mapAddressBook[strAddress].empty()) + strHTML += wallet->mapAddressBook[strAddress] + " "; strHTML += HtmlEscape(strAddress) + "
"; } @@ -172,7 +172,7 @@ string TransactionDesc::toHTML(CWalletTx &wtx) // int64 nUnmatured = 0; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - nUnmatured += txout.GetCredit(); + nUnmatured += wallet->GetCredit(txout); strHTML += _("Credit: "); if (wtx.IsInMainChain()) strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity()); @@ -191,11 +191,11 @@ string TransactionDesc::toHTML(CWalletTx &wtx) { bool fAllFromMe = true; BOOST_FOREACH(const CTxIn& txin, wtx.vin) - fAllFromMe = fAllFromMe && txin.IsMine(); + fAllFromMe = fAllFromMe && wallet->IsMine(txin); bool fAllToMe = true; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - fAllToMe = fAllToMe && txout.IsMine(); + fAllToMe = fAllToMe && wallet->IsMine(txout); if (fAllFromMe) { @@ -204,7 +204,7 @@ string TransactionDesc::toHTML(CWalletTx &wtx) // BOOST_FOREACH(const CTxOut& txout, wtx.vout) { - if (txout.IsMine()) + if (wallet->IsMine(txout)) continue; if (wtx.mapValue["to"].empty()) @@ -215,8 +215,8 @@ string TransactionDesc::toHTML(CWalletTx &wtx) { string strAddress = Hash160ToAddress(hash160); strHTML += _("To: "); - if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty()) - strHTML += mapAddressBook[strAddress] + " "; + if (wallet->mapAddressBook.count(strAddress) && !wallet->mapAddressBook[strAddress].empty()) + strHTML += wallet->mapAddressBook[strAddress] + " "; strHTML += strAddress; strHTML += "
"; } @@ -244,11 +244,11 @@ string TransactionDesc::toHTML(CWalletTx &wtx) // Mixed debit transaction // BOOST_FOREACH(const CTxIn& txin, wtx.vin) - if (txin.IsMine()) - strHTML += _("Debit: ") + FormatMoney(-txin.GetDebit()) + "
"; + if (wallet->IsMine(txin)) + strHTML += _("Debit: ") + FormatMoney(-wallet->GetDebit(txin)) + "
"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.IsMine()) - strHTML += _("Credit: ") + FormatMoney(txout.GetCredit()) + "
"; + if (wallet->IsMine(txout)) + strHTML += _("Credit: ") + FormatMoney(wallet->GetCredit(txout)) + "
"; } } @@ -274,30 +274,30 @@ string TransactionDesc::toHTML(CWalletTx &wtx) { strHTML += "

debug print

"; BOOST_FOREACH(const CTxIn& txin, wtx.vin) - if (txin.IsMine()) - strHTML += "Debit: " + FormatMoney(-txin.GetDebit()) + "
"; + if(wallet->IsMine(txin)) + strHTML += "Debit: " + FormatMoney(-wallet->IsMine(txin)) + "
"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.IsMine()) - strHTML += "Credit: " + FormatMoney(txout.GetCredit()) + "
"; + if(wallet->IsMine(txout)) + strHTML += "Credit: " + FormatMoney(wallet->IsMine(txout)) + "
"; strHTML += "
Transaction:
"; strHTML += HtmlEscape(wtx.ToString(), true); strHTML += "
Inputs:
"; - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(wallet->cs_mapWallet) { BOOST_FOREACH(const CTxIn& txin, wtx.vin) { COutPoint prevout = txin.prevout; - map::iterator mi = mapWallet.find(prevout.hash); - if (mi != mapWallet.end()) + map::iterator mi = wallet->mapWallet.find(prevout.hash); + if (mi != wallet->mapWallet.end()) { const CWalletTx& prev = (*mi).second; if (prevout.n < prev.vout.size()) { strHTML += HtmlEscape(prev.ToString(), true); strHTML += "    " + FormatTxStatus(prev) + ", "; - strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "
"; + strHTML = strHTML + "IsMine=" + (wallet->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "
"; } } } diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h index 5a85949341..fde861b6fb 100644 --- a/src/qt/transactiondesc.h +++ b/src/qt/transactiondesc.h @@ -3,13 +3,14 @@ #include +class CWallet; class CWalletTx; class TransactionDesc { public: /* Provide human-readable extended HTML description of a transaction */ - static std::string toHTML(CWalletTx &wtx); + static std::string toHTML(CWallet *wallet, CWalletTx &wtx); }; #endif // TRANSACTIONDESC_H diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 2f00fa8752..864dffa99e 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -1,5 +1,6 @@ #include "transactionrecord.h" +#include "headers.h" /* Return positive answer if transaction should be shown in list. */ @@ -29,7 +30,7 @@ bool TransactionRecord::showTransaction(const CWalletTx &wtx) /* * Decompose CWallet transaction to model transaction records. */ -QList TransactionRecord::decomposeTransaction(const CWalletTx &wtx) +QList TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx) { QList parts; int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime(); @@ -59,7 +60,7 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx { int64 nUnmatured = 0; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - nUnmatured += txout.GetCredit(); + nUnmatured += wallet->GetCredit(txout); sub.credit = nUnmatured; } } @@ -76,10 +77,10 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx sub.type = TransactionRecord::RecvWithAddress; BOOST_FOREACH(const CTxOut& txout, wtx.vout) { - if (txout.IsMine()) + if(wallet->IsMine(txout)) { std::vector vchPubKey; - if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) + if (ExtractPubKey(txout.scriptPubKey, wallet, vchPubKey)) { sub.address = PubKeyToAddress(vchPubKey); } @@ -93,11 +94,11 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx { bool fAllFromMe = true; BOOST_FOREACH(const CTxIn& txin, wtx.vin) - fAllFromMe = fAllFromMe && txin.IsMine(); + fAllFromMe = fAllFromMe && wallet->IsMine(txin); bool fAllToMe = true; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - fAllToMe = fAllToMe && txout.IsMine(); + fAllToMe = fAllToMe && wallet->IsMine(txout); if (fAllFromMe && fAllToMe) { @@ -120,13 +121,13 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx TransactionRecord sub(hash, nTime); sub.idx = parts.size(); - if (txout.IsMine()) + if(wallet->IsMine(txout)) { // Ignore parts sent to self, as this is usually the change // from a transaction sent back to our own address. continue; } - else if (!mapValue["to"].empty()) + else if(!mapValue["to"].empty()) { // Sent to IP sub.type = TransactionRecord::SendToIP; @@ -160,9 +161,9 @@ QList TransactionRecord::decomposeTransaction(const CWalletTx // bool fAllMine = true; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - fAllMine = fAllMine && txout.IsMine(); + fAllMine = fAllMine && wallet->IsMine(txout); BOOST_FOREACH(const CTxIn& txin, wtx.vin) - fAllMine = fAllMine && txin.IsMine(); + fAllMine = fAllMine && wallet->IsMine(txin); parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0)); } diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index a7f6537b3f..ba071dfdc9 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -5,6 +5,8 @@ #include +class CWallet; + class TransactionStatus { public: @@ -84,7 +86,7 @@ public: /* Decompose CWallet transaction to model transaction records. */ static bool showTransaction(const CWalletTx &wtx); - static QList decomposeTransaction(const CWalletTx &wtx); + static QList decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx); /* Fixed */ uint256 hash; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index dc3ef245e0..18ab421e3c 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -2,9 +2,10 @@ #include "guiutil.h" #include "transactionrecord.h" #include "guiconstants.h" -#include "main.h" #include "transactiondesc.h" +#include "headers.h" + #include #include #include @@ -37,11 +38,12 @@ struct TxLessThan // Private implementation struct TransactionTablePriv { - TransactionTablePriv(TransactionTableModel *parent): + TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent): + wallet(wallet), parent(parent) { } - + CWallet *wallet; TransactionTableModel *parent; /* Local cache of wallet. @@ -58,11 +60,11 @@ struct TransactionTablePriv qDebug() << "refreshWallet"; #endif cachedWallet.clear(); - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(wallet->cs_mapWallet) { - for(std::map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for(std::map::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it) { - cachedWallet.append(TransactionRecord::decomposeTransaction(it->second)); + cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second)); } } } @@ -84,14 +86,14 @@ struct TransactionTablePriv QList updated_sorted = updated; qSort(updated_sorted); - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(wallet->cs_mapWallet) { for(int update_idx = updated_sorted.size()-1; update_idx >= 0; --update_idx) { const uint256 &hash = updated_sorted.at(update_idx); /* Find transaction in wallet */ - std::map::iterator mi = mapWallet.find(hash); - bool inWallet = mi != mapWallet.end(); + std::map::iterator mi = wallet->mapWallet.find(hash); + bool inWallet = mi != wallet->mapWallet.end(); /* Find bounds of this transaction in model */ QList::iterator lower = qLowerBound( cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); @@ -100,6 +102,7 @@ struct TransactionTablePriv int lowerIndex = (lower - cachedWallet.begin()); int upperIndex = (upper - cachedWallet.begin()); + // Determine if transaction is in model already bool inModel = false; if(lower != upper) { @@ -115,7 +118,7 @@ struct TransactionTablePriv { // Added -- insert at the right position QList toInsert = - TransactionRecord::decomposeTransaction(mi->second); + TransactionRecord::decomposeTransaction(wallet, mi->second); if(!toInsert.isEmpty()) /* only if something to insert */ { parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); @@ -159,11 +162,11 @@ struct TransactionTablePriv // simply re-use the cached status. if(rec->statusUpdateNeeded()) { - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(wallet->cs_mapWallet) { - std::map::iterator mi = mapWallet.find(rec->hash); + std::map::iterator mi = wallet->mapWallet.find(rec->hash); - if(mi != mapWallet.end()) + if(mi != wallet->mapWallet.end()) { rec->updateStatus(mi->second); } @@ -179,12 +182,12 @@ struct TransactionTablePriv QString describe(TransactionRecord *rec) { - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(wallet->cs_mapWallet) { - std::map::iterator mi = mapWallet.find(rec->hash); - if(mi != mapWallet.end()) + std::map::iterator mi = wallet->mapWallet.find(rec->hash); + if(mi != wallet->mapWallet.end()) { - return QString::fromStdString(TransactionDesc::toHTML(mi->second)); + return QString::fromStdString(TransactionDesc::toHTML(wallet, mi->second)); } } return QString(""); @@ -202,9 +205,10 @@ static int column_alignments[] = { Qt::AlignLeft|Qt::AlignVCenter }; -TransactionTableModel::TransactionTableModel(QObject *parent): +TransactionTableModel::TransactionTableModel(CWallet* wallet, QObject *parent): QAbstractTableModel(parent), - priv(new TransactionTablePriv(this)) + wallet(wallet), + priv(new TransactionTablePriv(wallet, this)) { columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); @@ -225,15 +229,15 @@ void TransactionTableModel::update() QList updated; // Check if there are changes to wallet map - TRY_CRITICAL_BLOCK(cs_mapWallet) + TRY_CRITICAL_BLOCK(wallet->cs_mapWallet) { - if(!vWalletUpdated.empty()) + if(!wallet->vWalletUpdated.empty()) { - BOOST_FOREACH(uint256 hash, vWalletUpdated) + BOOST_FOREACH(uint256 hash, wallet->vWalletUpdated) { updated.append(hash); } - vWalletUpdated.clear(); + wallet->vWalletUpdated.clear(); } } @@ -302,13 +306,13 @@ QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const address[0:12]... (label) otherwise just return address */ -std::string lookupAddress(const std::string &address) +std::string TransactionTableModel::lookupAddress(const std::string &address) const { std::string description; - CRITICAL_BLOCK(cs_mapAddressBook) + CRITICAL_BLOCK(wallet->cs_mapAddressBook) { - std::map::iterator mi = mapAddressBook.find(address); - if (mi != mapAddressBook.end() && !(*mi).second.empty()) + std::map::iterator mi = wallet->mapAddressBook.find(address); + if (mi != wallet->mapAddressBook.end() && !(*mi).second.empty()) { std::string label = (*mi).second; description += address.substr(0,12) + "... "; diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 804f004e38..72a645b3db 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -4,6 +4,7 @@ #include #include +class CWallet; class TransactionTablePriv; class TransactionRecord; @@ -11,7 +12,7 @@ class TransactionTableModel : public QAbstractTableModel { Q_OBJECT public: - explicit TransactionTableModel(QObject *parent = 0); + explicit TransactionTableModel(CWallet* wallet, QObject *parent = 0); ~TransactionTableModel(); enum { @@ -39,9 +40,11 @@ public: Qt::ItemFlags flags(const QModelIndex &index) const; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; private: + CWallet* wallet; QStringList columns; TransactionTablePriv *priv; + std::string lookupAddress(const std::string &address) const; QVariant formatTxStatus(const TransactionRecord *wtx) const; QVariant formatTxDate(const TransactionRecord *wtx) const; QVariant formatTxDescription(const TransactionRecord *wtx) const; diff --git a/src/qtui.h b/src/qtui.h index 42f44e0d04..a3b9eb0148 100644 --- a/src/qtui.h +++ b/src/qtui.h @@ -6,6 +6,7 @@ #include #include +#include "wallet.h" typedef void wxWindow; #define wxYES 0x00000002 diff --git a/src/rpc.cpp b/src/rpc.cpp index bc9b224518..644ad92297 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -14,6 +14,7 @@ #include #ifdef USE_SSL #include +#include #include typedef boost::asio::ssl::stream SSLStream; #endif @@ -264,14 +265,14 @@ Value setgenerate(const Array& params, bool fHelp) { int nGenProcLimit = params[1].get_int(); fLimitProcessors = (nGenProcLimit != -1); - CWalletDB().WriteSetting("fLimitProcessors", fLimitProcessors); + WriteSetting("fLimitProcessors", fLimitProcessors); if (nGenProcLimit != -1) - CWalletDB().WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit); + WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit); if (nGenProcLimit == 0) fGenerate = false; } - GenerateBitcoins(fGenerate); + GenerateBitcoins(fGenerate, pwalletMain); return Value::null; } @@ -298,7 +299,7 @@ Value getinfo(const Array& params, bool fHelp) Object obj; obj.push_back(Pair("version", (int)VERSION)); - obj.push_back(Pair("balance", ValueFromAmount(GetBalance()))); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); obj.push_back(Pair("blocks", (int)nBestHeight)); obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string()))); @@ -307,7 +308,7 @@ Value getinfo(const Array& params, bool fHelp) obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("hashespersec", gethashespersec(params, false))); obj.push_back(Pair("testnet", fTestNet)); - obj.push_back(Pair("keypoololdest", (boost::int64_t)GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); obj.push_back(Pair("errors", GetWarnings("statusbar"))); return obj; @@ -329,9 +330,9 @@ Value getnewaddress(const Array& params, bool fHelp) strAccount = AccountFromValue(params[0]); // Generate a new key that is added to wallet - string strAddress = PubKeyToAddress(GetKeyFromKeyPool()); + string strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool()); - SetAddressBookName(strAddress, strAccount); + pwalletMain->SetAddressBookName(strAddress, strAccount); return strAddress; } @@ -341,7 +342,7 @@ string GetAccountAddress(string strAccount, bool bForceNew=false) { string strAddress; - CWalletDB walletdb; + CWalletDB walletdb(pwalletMain->strWalletFile); walletdb.TxnBegin(); CAccount account; @@ -352,8 +353,8 @@ string GetAccountAddress(string strAccount, bool bForceNew=false) { CScript scriptPubKey; scriptPubKey.SetBitcoinAddress(account.vchPubKey); - for (map::iterator it = mapWallet.begin(); - it != mapWallet.end() && !account.vchPubKey.empty(); + for (map::iterator it = pwalletMain->mapWallet.begin(); + it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty(); ++it) { const CWalletTx& wtx = (*it).second; @@ -366,9 +367,9 @@ string GetAccountAddress(string strAccount, bool bForceNew=false) // Generate a new key if (account.vchPubKey.empty() || bForceNew) { - account.vchPubKey = GetKeyFromKeyPool(); + account.vchPubKey = pwalletMain->GetKeyFromKeyPool(); string strAddress = PubKeyToAddress(account.vchPubKey); - SetAddressBookName(strAddress, strAccount); + pwalletMain->SetAddressBookName(strAddress, strAccount); walletdb.WriteAccount(strAccount, account); } @@ -391,7 +392,7 @@ Value getaccountaddress(const Array& params, bool fHelp) Value ret; CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) { ret = GetAccountAddress(strAccount); } @@ -421,18 +422,18 @@ Value setaccount(const Array& params, bool fHelp) // Detect when changing the account of an address that is the 'unused current key' of another account: CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - CRITICAL_BLOCK(cs_mapAddressBook) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) { - if (mapAddressBook.count(strAddress)) + if (pwalletMain->mapAddressBook.count(strAddress)) { - string strOldAccount = mapAddressBook[strAddress]; + string strOldAccount = pwalletMain->mapAddressBook[strAddress]; if (strAddress == GetAccountAddress(strOldAccount)) GetAccountAddress(strOldAccount, true); } } - SetAddressBookName(strAddress, strAccount); + pwalletMain->SetAddressBookName(strAddress, strAccount); return Value::null; } @@ -447,10 +448,10 @@ Value getaccount(const Array& params, bool fHelp) string strAddress = params[0].get_str(); string strAccount; - CRITICAL_BLOCK(cs_mapAddressBook) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) { - map::iterator mi = mapAddressBook.find(strAddress); - if (mi != mapAddressBook.end() && !(*mi).second.empty()) + map::iterator mi = pwalletMain->mapAddressBook.find(strAddress); + if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty()) strAccount = (*mi).second; } return strAccount; @@ -468,9 +469,9 @@ Value getaddressesbyaccount(const Array& params, bool fHelp) // Find all addresses that have the given account Array ret; - CRITICAL_BLOCK(cs_mapAddressBook) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook) + BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) { const string& strAddress = item.first; const string& strName = item.second; @@ -523,7 +524,7 @@ Value sendtoaddress(const Array& params, bool fHelp) CRITICAL_BLOCK(cs_main) { - string strError = SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); + string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); if (strError != "") throw JSONRPCError(-4, strError); } @@ -544,7 +545,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) CScript scriptPubKey; if (!scriptPubKey.SetBitcoinAddress(strAddress)) throw JSONRPCError(-5, "Invalid bitcoin address"); - if (!IsMine(scriptPubKey)) + if (!IsMine(*pwalletMain,scriptPubKey)) return (double)0.0; // Minimum confirmations @@ -554,9 +555,9 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) // Tally int64 nAmount = 0; - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) { - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; if (wtx.IsCoinBase() || !wtx.IsFinal()) @@ -575,9 +576,9 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) void GetAccountPubKeys(string strAccount, set& setPubKey) { - CRITICAL_BLOCK(cs_mapAddressBook) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook) + BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) { const string& strAddress = item.first; const string& strName = item.second; @@ -586,7 +587,7 @@ void GetAccountPubKeys(string strAccount, set& setPubKey) // We're only counting our own valid bitcoin addresses and not ip addresses CScript scriptPubKey; if (scriptPubKey.SetBitcoinAddress(strAddress)) - if (IsMine(scriptPubKey)) + if (IsMine(*pwalletMain,scriptPubKey)) setPubKey.insert(scriptPubKey); } } @@ -613,9 +614,9 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) // Tally int64 nAmount = 0; - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) { - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; if (wtx.IsCoinBase() || !wtx.IsFinal()) @@ -635,10 +636,10 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) { int64 nBalance = 0; - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) { // Tally wallet transactions - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; if (!wtx.IsFinal()) @@ -661,7 +662,7 @@ int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinD int64 GetAccountBalance(const string& strAccount, int nMinDepth) { - CWalletDB walletdb; + CWalletDB walletdb(pwalletMain->strWalletFile); return GetAccountBalance(walletdb, strAccount, nMinDepth); } @@ -675,7 +676,7 @@ Value getbalance(const Array& params, bool fHelp) "If [account] is specified, returns the balance in the account."); if (params.size() == 0) - return ValueFromAmount(GetBalance()); + return ValueFromAmount(pwalletMain->GetBalance()); int nMinDepth = 1; if (params.size() > 1) @@ -686,7 +687,7 @@ Value getbalance(const Array& params, bool fHelp) // (GetBalance() sums up all unspent TxOuts) // getbalance and getbalance '*' should always return the same number. int64 nBalance = 0; - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; if (!wtx.IsFinal()) @@ -734,9 +735,9 @@ Value movecmd(const Array& params, bool fHelp) if (params.size() > 4) strComment = params[4].get_str(); - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) { - CWalletDB walletdb; + CWalletDB walletdb(pwalletMain->strWalletFile); walletdb.TxnBegin(); int64 nNow = GetAdjustedTime(); @@ -787,7 +788,7 @@ Value sendfrom(const Array& params, bool fHelp) wtx.mapValue["to"] = params[5].get_str(); CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) { // Check funds int64 nBalance = GetAccountBalance(strAccount, nMinDepth); @@ -795,7 +796,7 @@ Value sendfrom(const Array& params, bool fHelp) throw JSONRPCError(-6, "Account has insufficient funds"); // Send - string strError = SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); + string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); if (strError != "") throw JSONRPCError(-4, strError); } @@ -844,7 +845,7 @@ Value sendmany(const Array& params, bool fHelp) } CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) { // Check funds int64 nBalance = GetAccountBalance(strAccount, nMinDepth); @@ -852,16 +853,16 @@ Value sendmany(const Array& params, bool fHelp) throw JSONRPCError(-6, "Account has insufficient funds"); // Send - CReserveKey keyChange; + CReserveKey keyChange(pwalletMain); int64 nFeeRequired = 0; - bool fCreated = CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); + bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); if (!fCreated) { - if (totalAmount + nFeeRequired > GetBalance()) + if (totalAmount + nFeeRequired > pwalletMain->GetBalance()) throw JSONRPCError(-6, "Insufficient funds"); throw JSONRPCError(-4, "Transaction creation failed"); } - if (!CommitTransaction(wtx, keyChange)) + if (!pwalletMain->CommitTransaction(wtx, keyChange)) throw JSONRPCError(-4, "Transaction commit failed"); } @@ -894,9 +895,9 @@ Value ListReceived(const Array& params, bool fByAccounts) // Tally map mapTally; - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) { - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; if (wtx.IsCoinBase() || !wtx.IsFinal()) @@ -923,9 +924,9 @@ Value ListReceived(const Array& params, bool fByAccounts) // Reply Array ret; map mapAccountTally; - CRITICAL_BLOCK(cs_mapAddressBook) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook) + BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) { const string& strAddress = item.first; const string& strAccount = item.second; @@ -1061,13 +1062,13 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe // Received if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) - CRITICAL_BLOCK(cs_mapAddressBook) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) { BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) { string account; - if (mapAddressBook.count(r.first)) - account = mapAddressBook[r.first]; + if (pwalletMain->mapAddressBook.count(r.first)) + account = pwalletMain->mapAddressBook[r.first]; if (fAllAccounts || (account == strAccount)) { Object entry; @@ -1119,16 +1120,16 @@ Value listtransactions(const Array& params, bool fHelp) nFrom = params[2].get_int(); Array ret; - CWalletDB walletdb; + CWalletDB walletdb(pwalletMain->strWalletFile); - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) { // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap: typedef pair TxPair; typedef multimap TxItems; TxItems txByTime; - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { CWalletTx* wtx = &((*it).second); txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0))); @@ -1180,16 +1181,16 @@ Value listaccounts(const Array& params, bool fHelp) nMinDepth = params[0].get_int(); map mapAccountBalances; - CRITICAL_BLOCK(cs_mapWallet) - CRITICAL_BLOCK(cs_mapAddressBook) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) { - BOOST_FOREACH(const PAIRTYPE(string, string)& entry, mapAddressBook) { + BOOST_FOREACH(const PAIRTYPE(string, string)& entry, pwalletMain->mapAddressBook) { uint160 hash160; if(AddressToHash160(entry.first, hash160) && mapPubKeys.count(hash160)) // This address belongs to me mapAccountBalances[entry.second] = 0; } - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; int64 nGeneratedImmature, nGeneratedMature, nFee; @@ -1204,8 +1205,8 @@ Value listaccounts(const Array& params, bool fHelp) { mapAccountBalances[""] += nGeneratedMature; BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) - if (mapAddressBook.count(r.first)) - mapAccountBalances[mapAddressBook[r.first]] += r.second; + if (pwalletMain->mapAddressBook.count(r.first)) + mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second; else mapAccountBalances[""] += r.second; } @@ -1213,7 +1214,7 @@ Value listaccounts(const Array& params, bool fHelp) } list acentries; - CWalletDB().ListAccountCreditDebit("*", acentries); + CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); BOOST_FOREACH(const CAccountingEntry& entry, acentries) mapAccountBalances[entry.strAccount] += entry.nCreditDebit; @@ -1235,11 +1236,11 @@ Value gettransaction(const Array& params, bool fHelp) hash.SetHex(params[0].get_str()); Object entry; - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) { - if (!mapWallet.count(hash)) + if (!pwalletMain->mapWallet.count(hash)) throw JSONRPCError(-5, "Invalid or non-wallet transaction id"); - const CWalletTx& wtx = mapWallet[hash]; + const CWalletTx& wtx = pwalletMain->mapWallet[hash]; int64 nCredit = wtx.GetCredit(); int64 nDebit = wtx.GetDebit(); @@ -1250,10 +1251,10 @@ Value gettransaction(const Array& params, bool fHelp) if (wtx.IsFromMe()) entry.push_back(Pair("fee", ValueFromAmount(nFee))); - WalletTxToJSON(mapWallet[hash], entry); + WalletTxToJSON(pwalletMain->mapWallet[hash], entry); Array details; - ListTransactions(mapWallet[hash], "*", 0, false, details); + ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details); entry.push_back(Pair("details", details)); } @@ -1269,7 +1270,7 @@ Value backupwallet(const Array& params, bool fHelp) "Safely copies wallet.dat to destination, which can be a directory or a path with filename."); string strDest = params[0].get_str(); - BackupWallet(strDest); + BackupWallet(*pwalletMain, strDest); return Value::null; } @@ -1295,10 +1296,10 @@ Value validateaddress(const Array& params, bool fHelp) string currentAddress = Hash160ToAddress(hash160); ret.push_back(Pair("address", currentAddress)); ret.push_back(Pair("ismine", (mapPubKeys.count(hash160) > 0))); - CRITICAL_BLOCK(cs_mapAddressBook) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) { - if (mapAddressBook.count(currentAddress)) - ret.push_back(Pair("account", mapAddressBook[currentAddress])); + if (pwalletMain->mapAddressBook.count(currentAddress)) + ret.push_back(Pair("account", pwalletMain->mapAddressBook[currentAddress])); } } return ret; @@ -1325,7 +1326,7 @@ Value getwork(const Array& params, bool fHelp) static map > mapNewBlock; static vector vNewBlock; - static CReserveKey reservekey; + static CReserveKey reservekey(pwalletMain); if (params.size() == 0) { @@ -1406,7 +1407,7 @@ Value getwork(const Array& params, bool fHelp) pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - return CheckWork(pblock, reservekey); + return CheckWork(pblock, *pwalletMain, reservekey); } } diff --git a/src/script.cpp b/src/script.cpp index 97334ca0a0..bd1b5b3c5f 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1021,7 +1021,7 @@ bool Solver(const CScript& scriptPubKey, vector >& vSo } -bool Solver(const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet) +bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet) { scriptSigRet.clear(); @@ -1030,7 +1030,7 @@ bool Solver(const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& s return false; // Compile solution - CRITICAL_BLOCK(cs_mapKeys) + CRITICAL_BLOCK(keystore.cs_mapKeys) { BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) { @@ -1038,12 +1038,13 @@ bool Solver(const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& s { // Sign const valtype& vchPubKey = item.second; - if (!mapKeys.count(vchPubKey)) + CPrivKey privkey; + if (!keystore.GetPrivKey(vchPubKey, privkey)) return false; if (hash != 0) { vector vchSig; - if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig)) + if (!CKey::Sign(privkey, hash, vchSig)) return false; vchSig.push_back((unsigned char)nHashType); scriptSigRet << vchSig; @@ -1056,12 +1057,13 @@ bool Solver(const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& s if (mi == mapPubKeys.end()) return false; const vector& vchPubKey = (*mi).second; - if (!mapKeys.count(vchPubKey)) + CPrivKey privkey; + if (!keystore.GetPrivKey(vchPubKey, privkey)) return false; if (hash != 0) { vector vchSig; - if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig)) + if (!CKey::Sign(privkey, hash, vchSig)) return false; vchSig.push_back((unsigned char)nHashType); scriptSigRet << vchSig << vchPubKey; @@ -1085,14 +1087,14 @@ bool IsStandard(const CScript& scriptPubKey) } -bool IsMine(const CScript& scriptPubKey) +bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) { CScript scriptSig; - return Solver(scriptPubKey, 0, 0, scriptSig); + return Solver(keystore, scriptPubKey, 0, 0, scriptSig); } -bool ExtractPubKey(const CScript& scriptPubKey, bool fMineOnly, vector& vchPubKeyRet) +bool ExtractPubKey(const CScript& scriptPubKey, const CKeyStore* keystore, vector& vchPubKeyRet) { vchPubKeyRet.clear(); @@ -1100,7 +1102,7 @@ bool ExtractPubKey(const CScript& scriptPubKey, bool fMineOnly, vectorHaveKey(vchPubKey)) { vchPubKeyRet = vchPubKey; return true; @@ -1160,7 +1162,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C } -bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType, CScript scriptPrereq) +bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType, CScript scriptPrereq) { assert(nIn < txTo.vin.size()); CTxIn& txin = txTo.vin[nIn]; @@ -1171,7 +1173,7 @@ bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int // The checksig op will also drop the signatures from its hash. uint256 hash = SignatureHash(scriptPrereq + txout.scriptPubKey, txTo, nIn, nHashType); - if (!Solver(txout.scriptPubKey, hash, nHashType, txin.scriptSig)) + if (!Solver(keystore, txout.scriptPubKey, hash, nHashType, txin.scriptSig)) return false; txin.scriptSig = scriptPrereq + txin.scriptSig; @@ -1199,10 +1201,5 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nHashType)) return false; - // Anytime a signature is successfully verified, it's proof the outpoint is spent, - // so lets update the wallet spent flag if it doesn't know due to wallet.dat being - // restored from backup or the user making copies of wallet.dat. - WalletUpdateSpent(txin.prevout); - return true; } diff --git a/src/script.h b/src/script.h index 22a6020dce..ae9fdfffa2 100644 --- a/src/script.h +++ b/src/script.h @@ -5,6 +5,7 @@ #define H_BITCOIN_SCRIPT #include "base58.h" +#include "keystore.h" #include #include @@ -707,12 +708,11 @@ public: -uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); bool IsStandard(const CScript& scriptPubKey); -bool IsMine(const CScript& scriptPubKey); -bool ExtractPubKey(const CScript& scriptPubKey, bool fMineOnly, std::vector& vchPubKeyRet); +bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); +bool ExtractPubKey(const CScript& scriptPubKey, const CKeyStore* pkeystore, std::vector& vchPubKeyRet); bool ExtractHash160(const CScript& scriptPubKey, uint160& hash160Ret); -bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript()); +bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript()); bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType=0); #endif diff --git a/src/util.cpp b/src/util.cpp index 688605ef5c..2bc2cdd599 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -5,7 +5,7 @@ #include "strlcpy.h" #include #include -#include +#include #include #include #include diff --git a/src/util.h b/src/util.h index a1f1694929..4c1e74b7da 100644 --- a/src/util.h +++ b/src/util.h @@ -139,14 +139,12 @@ inline int myclosesocket(SOCKET& hSocket) return ret; } #define closesocket(s) myclosesocket(s) -#if 0 -#ifndef GUI +#if !defined(QT_GUI) && !defined(GUI) inline const char* _(const char* psz) { return psz; } #endif -#endif diff --git a/src/wallet.cpp b/src/wallet.cpp new file mode 100644 index 0000000000..eab58aaafb --- /dev/null +++ b/src/wallet.cpp @@ -0,0 +1,1176 @@ +// Copyright (c) 2009-2011 Satoshi Nakamoto & Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" +#include "db.h" +#include "cryptopp/sha.h" + +using namespace std; + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapWallet +// + +bool CWallet::AddKey(const CKey& key) +{ + this->CKeyStore::AddKey(key); + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey()); +} + +void CWallet::WalletUpdateSpent(const CTransaction &tx) +{ + // Anytime a signature is successfully verified, it's proof the outpoint is spent. + // Update the wallet spent flag if it doesn't know due to wallet.dat being + // restored from backup or the user making copies of wallet.dat. + CRITICAL_BLOCK(cs_mapWallet) + { + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + map::iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + CWalletTx& wtx = (*mi).second; + if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) + { + printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + wtx.MarkSpent(txin.prevout.n); + wtx.WriteToDisk(); + vWalletUpdated.push_back(txin.prevout.hash); + } + } + } + } +} + +bool CWallet::AddToWallet(const CWalletTx& wtxIn) +{ + uint256 hash = wtxIn.GetHash(); + CRITICAL_BLOCK(cs_mapWallet) + { + // Inserts only if not already there, returns tx inserted or tx found + pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); + CWalletTx& wtx = (*ret.first).second; + wtx.pwallet = this; + bool fInsertedNew = ret.second; + if (fInsertedNew) + wtx.nTimeReceived = GetAdjustedTime(); + + bool fUpdated = false; + if (!fInsertedNew) + { + // Merge + if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) + { + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) + { + wtx.vMerkleBranch = wtxIn.vMerkleBranch; + wtx.nIndex = wtxIn.nIndex; + fUpdated = true; + } + if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) + { + wtx.fFromMe = wtxIn.fFromMe; + fUpdated = true; + } + fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent); + } + + //// debug print + printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + + // Write to disk + if (fInsertedNew || fUpdated) + if (!wtx.WriteToDisk()) + return false; + + // If default receiving address gets used, replace it with a new one + CScript scriptDefaultKey; + scriptDefaultKey.SetBitcoinAddress(vchDefaultKey); + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (txout.scriptPubKey == scriptDefaultKey) + { + if (!fFileBacked) + continue; + CWalletDB walletdb(strWalletFile); + vchDefaultKey = GetKeyFromKeyPool(); + walletdb.WriteDefaultKey(vchDefaultKey); + walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); + } + } + + // Notify UI + vWalletUpdated.push_back(hash); + + // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins + WalletUpdateSpent(wtx); + } + + // Refresh UI + MainFrameRepaint(); + return true; +} + +bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) +{ + uint256 hash = tx.GetHash(); + bool fExisted = mapWallet.count(hash); + if (fExisted && !fUpdate) return false; + if (fExisted || IsMine(tx) || IsFromMe(tx)) + { + CWalletTx wtx(this,tx); + // Get merkle branch if transaction was found in a block + if (pblock) + wtx.SetMerkleBranch(pblock); + return AddToWallet(wtx); + } + else + WalletUpdateSpent(tx); + return false; +} + +bool CWallet::EraseFromWallet(uint256 hash) +{ + if (!fFileBacked) + return false; + CRITICAL_BLOCK(cs_mapWallet) + { + if (mapWallet.erase(hash)) + CWalletDB(strWalletFile).EraseTx(hash); + } + return true; +} + + +bool CWallet::IsMine(const CTxIn &txin) const +{ + CRITICAL_BLOCK(cs_mapWallet) + { + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.vout.size()) + if (IsMine(prev.vout[txin.prevout.n])) + return true; + } + } + return false; +} + +int64 CWallet::GetDebit(const CTxIn &txin) const +{ + CRITICAL_BLOCK(cs_mapWallet) + { + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.vout.size()) + if (IsMine(prev.vout[txin.prevout.n])) + return prev.vout[txin.prevout.n].nValue; + } + } + return 0; +} + +int64 CWalletTx::GetTxTime() const +{ + if (!fTimeReceivedIsTxTime && hashBlock != 0) + { + // If we did not receive the transaction directly, we rely on the block's + // time to figure out when it happened. We use the median over a range + // of blocks to try to filter out inaccurate block times. + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex) + return pindex->GetMedianTime(); + } + } + return nTimeReceived; +} + +int CWalletTx::GetRequestCount() const +{ + // Returns -1 if it wasn't being tracked + int nRequests = -1; + CRITICAL_BLOCK(pwallet->cs_mapRequestCount) + { + if (IsCoinBase()) + { + // Generated block + if (hashBlock != 0) + { + map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); + if (mi != pwallet->mapRequestCount.end()) + nRequests = (*mi).second; + } + } + else + { + // Did anyone request this transaction? + map::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); + if (mi != pwallet->mapRequestCount.end()) + { + nRequests = (*mi).second; + + // How about the block it's in? + if (nRequests == 0 && hashBlock != 0) + { + map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); + if (mi != pwallet->mapRequestCount.end()) + nRequests = (*mi).second; + else + nRequests = 1; // If it's in someone else's block it must have got out + } + } + } + } + return nRequests; +} + +void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list >& listReceived, + list >& listSent, int64& nFee, string& strSentAccount) const +{ + nGeneratedImmature = nGeneratedMature = nFee = 0; + listReceived.clear(); + listSent.clear(); + strSentAccount = strFromAccount; + + if (IsCoinBase()) + { + if (GetBlocksToMaturity() > 0) + nGeneratedImmature = pwallet->GetCredit(*this); + else + nGeneratedMature = GetCredit(); + return; + } + + // Compute fee: + int64 nDebit = GetDebit(); + if (nDebit > 0) // debit>0 means we signed/sent this transaction + { + int64 nValueOut = GetValueOut(); + nFee = nDebit - nValueOut; + } + + // Sent/received. Standard client will never generate a send-to-multiple-recipients, + // but non-standard clients might (so return a list of address/amount pairs) + BOOST_FOREACH(const CTxOut& txout, vout) + { + string address; + uint160 hash160; + vector vchPubKey; + if (ExtractHash160(txout.scriptPubKey, hash160)) + address = Hash160ToAddress(hash160); + else if (ExtractPubKey(txout.scriptPubKey, NULL, vchPubKey)) + address = PubKeyToAddress(vchPubKey); + else + { + printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", + this->GetHash().ToString().c_str()); + address = " unknown "; + } + + // Don't report 'change' txouts + if (nDebit > 0 && pwallet->IsChange(txout)) + continue; + + if (nDebit > 0) + listSent.push_back(make_pair(address, txout.nValue)); + + if (pwallet->IsMine(txout)) + listReceived.push_back(make_pair(address, txout.nValue)); + } + +} + +void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, + int64& nSent, int64& nFee) const +{ + nGenerated = nReceived = nSent = nFee = 0; + + int64 allGeneratedImmature, allGeneratedMature, allFee; + allGeneratedImmature = allGeneratedMature = allFee = 0; + string strSentAccount; + list > listReceived; + list > listSent; + GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); + + if (strAccount == "") + nGenerated = allGeneratedMature; + if (strAccount == strSentAccount) + { + BOOST_FOREACH(const PAIRTYPE(string,int64)& s, listSent) + nSent += s.second; + nFee = allFee; + } + CRITICAL_BLOCK(pwallet->cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) + { + if (pwallet->mapAddressBook.count(r.first)) + { + map::const_iterator mi = pwallet->mapAddressBook.find(r.first); + if (mi != pwallet->mapAddressBook.end() && (*mi).second == strAccount) + nReceived += r.second; + } + else if (strAccount.empty()) + { + nReceived += r.second; + } + } + } +} + +void CWalletTx::AddSupportingTransactions(CTxDB& txdb) +{ + vtxPrev.clear(); + + const int COPY_DEPTH = 3; + if (SetMerkleBranch() < COPY_DEPTH) + { + vector vWorkQueue; + BOOST_FOREACH(const CTxIn& txin, vin) + vWorkQueue.push_back(txin.prevout.hash); + + // This critsect is OK because txdb is already open + CRITICAL_BLOCK(pwallet->cs_mapWallet) + { + map mapWalletPrev; + set setAlreadyDone; + for (int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hash = vWorkQueue[i]; + if (setAlreadyDone.count(hash)) + continue; + setAlreadyDone.insert(hash); + + CMerkleTx tx; + map::const_iterator mi = pwallet->mapWallet.find(hash); + if (mi != pwallet->mapWallet.end()) + { + tx = (*mi).second; + BOOST_FOREACH(const CMerkleTx& txWalletPrev, (*mi).second.vtxPrev) + mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; + } + else if (mapWalletPrev.count(hash)) + { + tx = *mapWalletPrev[hash]; + } + else if (!fClient && txdb.ReadDiskTx(hash, tx)) + { + ; + } + else + { + printf("ERROR: AddSupportingTransactions() : unsupported transaction\n"); + continue; + } + + int nDepth = tx.SetMerkleBranch(); + vtxPrev.push_back(tx); + + if (nDepth < COPY_DEPTH) + BOOST_FOREACH(const CTxIn& txin, tx.vin) + vWorkQueue.push_back(txin.prevout.hash); + } + } + } + + reverse(vtxPrev.begin(), vtxPrev.end()); +} + +bool CWalletTx::WriteToDisk() +{ + return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this); +} + +int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) +{ + int ret = 0; + + CBlockIndex* pindex = pindexStart; + CRITICAL_BLOCK(cs_mapWallet) + { + while (pindex) + { + CBlock block; + block.ReadFromDisk(pindex, true); + BOOST_FOREACH(CTransaction& tx, block.vtx) + { + if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) + ret++; + } + pindex = pindex->pnext; + } + } + return ret; +} + +void CWallet::ReacceptWalletTransactions() +{ + CTxDB txdb("r"); + bool fRepeat = true; + while (fRepeat) CRITICAL_BLOCK(cs_mapWallet) + { + fRepeat = false; + vector vMissingTx; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + if (wtx.IsCoinBase() && wtx.IsSpent(0)) + continue; + + CTxIndex txindex; + bool fUpdated = false; + if (txdb.ReadTxIndex(wtx.GetHash(), txindex)) + { + // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat + if (txindex.vSpent.size() != wtx.vout.size()) + { + printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size()); + continue; + } + for (int i = 0; i < txindex.vSpent.size(); i++) + { + if (wtx.IsSpent(i)) + continue; + if (!txindex.vSpent[i].IsNull() && IsMine(wtx.vout[i])) + { + wtx.MarkSpent(i); + fUpdated = true; + vMissingTx.push_back(txindex.vSpent[i]); + } + } + if (fUpdated) + { + printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + wtx.MarkDirty(); + wtx.WriteToDisk(); + } + } + else + { + // Reaccept any txes of ours that aren't already in a block + if (!wtx.IsCoinBase()) + wtx.AcceptWalletTransaction(txdb, false); + } + } + if (!vMissingTx.empty()) + { + // TODO: optimize this to scan just part of the block chain? + if (ScanForWalletTransactions(pindexGenesisBlock)) + fRepeat = true; // Found missing transactions: re-do Reaccept. + } + } +} + +void CWalletTx::RelayWalletTransaction(CTxDB& txdb) +{ + BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) + { + if (!tx.IsCoinBase()) + { + uint256 hash = tx.GetHash(); + if (!txdb.ContainsTx(hash)) + RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); + } + } + if (!IsCoinBase()) + { + uint256 hash = GetHash(); + if (!txdb.ContainsTx(hash)) + { + printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); + RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); + } + } +} + +void CWalletTx::RelayWalletTransaction() +{ + CTxDB txdb("r"); + RelayWalletTransaction(txdb); +} + +void CWallet::ResendWalletTransactions() +{ + // Do this infrequently and randomly to avoid giving away + // that these are our transactions. + static int64 nNextTime; + if (GetTime() < nNextTime) + return; + bool fFirst = (nNextTime == 0); + nNextTime = GetTime() + GetRand(30 * 60); + if (fFirst) + return; + + // Only do it if there's been a new block since last time + static int64 nLastTime; + if (nTimeBestReceived < nLastTime) + return; + nLastTime = GetTime(); + + // Rebroadcast any of our txes that aren't in a block yet + printf("ResendWalletTransactions()\n"); + CTxDB txdb("r"); + CRITICAL_BLOCK(cs_mapWallet) + { + // Sort them in chronological order + multimap mapSorted; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + // Don't rebroadcast until it's had plenty of time that + // it should have gotten in already by now. + if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) + mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); + } + BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) + { + CWalletTx& wtx = *item.second; + wtx.RelayWalletTransaction(txdb); + } + } +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Actions +// + + +int64 CWallet::GetBalance() const +{ + int64 nStart = GetTimeMillis(); + + int64 nTotal = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + continue; + nTotal += pcoin->GetAvailableCredit(); + } + } + + //printf("GetBalance() %"PRI64d"ms\n", GetTimeMillis() - nStart); + return nTotal; +} + + +bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) const +{ + setCoinsRet.clear(); + nValueRet = 0; + + // List of values less than target + pair > coinLowestLarger; + coinLowestLarger.first = INT64_MAX; + coinLowestLarger.second.first = NULL; + vector > > vValue; + int64 nTotalLower = 0; + + CRITICAL_BLOCK(cs_mapWallet) + { + vector vCoins; + vCoins.reserve(mapWallet.size()); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + vCoins.push_back(&(*it).second); + random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + + BOOST_FOREACH(const CWalletTx* pcoin, vCoins) + { + if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) + continue; + + for (int i = 0; i < pcoin->vout.size(); i++) + { + if (pcoin->IsSpent(i) || !IsMine(pcoin->vout[i])) + continue; + + int64 n = pcoin->vout[i].nValue; + + if (n <= 0) + continue; + + pair > coin = make_pair(n,make_pair(pcoin,i)); + + if (n == nTargetValue) + { + setCoinsRet.insert(coin.second); + nValueRet += coin.first; + return true; + } + else if (n < nTargetValue + CENT) + { + vValue.push_back(coin); + nTotalLower += n; + } + else if (n < coinLowestLarger.first) + { + coinLowestLarger = coin; + } + } + } + } + + if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT) + { + for (int i = 0; i < vValue.size(); ++i) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + return true; + } + + if (nTotalLower < nTargetValue + (coinLowestLarger.second.first ? CENT : 0)) + { + if (coinLowestLarger.second.first == NULL) + return false; + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + return true; + } + + if (nTotalLower >= nTargetValue + CENT) + nTargetValue += CENT; + + // Solve subset sum by stochastic approximation + sort(vValue.rbegin(), vValue.rend()); + vector vfIncluded; + vector vfBest(vValue.size(), true); + int64 nBest = nTotalLower; + + for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++) + { + vfIncluded.assign(vValue.size(), false); + int64 nTotal = 0; + bool fReachedTarget = false; + for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) + { + for (int i = 0; i < vValue.size(); i++) + { + if (nPass == 0 ? rand() % 2 : !vfIncluded[i]) + { + nTotal += vValue[i].first; + vfIncluded[i] = true; + if (nTotal >= nTargetValue) + { + fReachedTarget = true; + if (nTotal < nBest) + { + nBest = nTotal; + vfBest = vfIncluded; + } + nTotal -= vValue[i].first; + vfIncluded[i] = false; + } + } + } + } + } + + // If the next larger is still closer, return it + if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue) + { + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + } + else { + for (int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + + //// debug print + printf("SelectCoins() best subset: "); + for (int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + printf("%s ", FormatMoney(vValue[i].first).c_str()); + printf("total %s\n", FormatMoney(nBest).c_str()); + } + + return true; +} + +bool CWallet::SelectCoins(int64 nTargetValue, set >& setCoinsRet, int64& nValueRet) const +{ + return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet)); +} + + + + +bool CWallet::CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +{ + int64 nValue = 0; + BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + { + if (nValue < 0) + return false; + nValue += s.second; + } + if (vecSend.empty() || nValue < 0) + return false; + + wtxNew.pwallet = this; + + CRITICAL_BLOCK(cs_main) + { + // txdb must be opened before the mapWallet lock + CTxDB txdb("r"); + CRITICAL_BLOCK(cs_mapWallet) + { + nFeeRet = nTransactionFee; + loop + { + wtxNew.vin.clear(); + wtxNew.vout.clear(); + wtxNew.fFromMe = true; + + int64 nTotalValue = nValue + nFeeRet; + double dPriority = 0; + // vouts to the payees + BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + wtxNew.vout.push_back(CTxOut(s.second, s.first)); + + // Choose coins to use + set > setCoins; + int64 nValueIn = 0; + if (!SelectCoins(nTotalValue, setCoins, nValueIn)) + return false; + BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) + { + int64 nCredit = pcoin.first->vout[pcoin.second].nValue; + dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(); + } + + // Fill a vout back to self with any change + int64 nChange = nValueIn - nTotalValue; + if (nChange >= CENT) + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + vector vchPubKey = reservekey.GetReservedKey(); + assert(mapKeys.count(vchPubKey)); + + // Fill a vout to ourself, using same address type as the payment + CScript scriptChange; + if (vecSend[0].first.GetBitcoinAddressHash160() != 0) + scriptChange.SetBitcoinAddress(vchPubKey); + else + scriptChange << vchPubKey << OP_CHECKSIG; + + // Insert change txn at random position: + vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); + wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); + } + else + reservekey.ReturnKey(); + + // Fill vin + BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) + wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); + + // Sign + int nIn = 0; + BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) + if (!SignSignature(*this, *coin.first, wtxNew, nIn++)) + return false; + + // Limit size + unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK); + if (nBytes >= MAX_BLOCK_SIZE_GEN/5) + return false; + dPriority /= nBytes; + + // Check that enough fee is included + int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); + bool fAllowFree = CTransaction::AllowFree(dPriority); + int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree); + if (nFeeRet < max(nPayFee, nMinFee)) + { + nFeeRet = max(nPayFee, nMinFee); + continue; + } + + // Fill vtxPrev by copying from previous transactions vtxPrev + wtxNew.AddSupportingTransactions(txdb); + wtxNew.fTimeReceivedIsTxTime = true; + + break; + } + } + } + return true; +} + +bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +{ + vector< pair > vecSend; + vecSend.push_back(make_pair(scriptPubKey, nValue)); + return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); +} + +// Call after CreateTransaction unless you want to abort +bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) +{ + CRITICAL_BLOCK(cs_main) + { + printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); + CRITICAL_BLOCK(cs_mapWallet) + { + // This is only to keep the database open to defeat the auto-flush for the + // duration of this scope. This is the only place where this optimization + // maybe makes sense; please don't do it anywhere else. + CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r") : NULL; + + // Take key pair from key pool so it won't be used again + reservekey.KeepKey(); + + // Add tx to wallet, because if it has change it's also ours, + // otherwise just for transaction history. + AddToWallet(wtxNew); + + // Mark old coins as spent + set setCoins; + BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) + { + CWalletTx &coin = mapWallet[txin.prevout.hash]; + coin.pwallet = this; + coin.MarkSpent(txin.prevout.n); + coin.WriteToDisk(); + vWalletUpdated.push_back(coin.GetHash()); + } + + if (fFileBacked) + delete pwalletdb; + } + + // Track how many getdata requests our transaction gets + CRITICAL_BLOCK(cs_mapRequestCount) + mapRequestCount[wtxNew.GetHash()] = 0; + + // Broadcast + if (!wtxNew.AcceptToMemoryPool()) + { + // This must not fail. The transaction has already been signed and recorded. + printf("CommitTransaction() : Error: Transaction not valid"); + return false; + } + wtxNew.RelayWalletTransaction(); + } + MainFrameRepaint(); + return true; +} + + + + +// requires cs_main lock +string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +{ + CReserveKey reservekey(this); + int64 nFeeRequired; + if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired)) + { + string strError; + if (nValue + nFeeRequired > 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("SendMoney() : %s", strError.c_str()); + return strError; + } + + if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL)) + return "ABORTED"; + + if (!CommitTransaction(wtxNew, reservekey)) + return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); + + MainFrameRepaint(); + return ""; +} + + + +// requires cs_main lock +string CWallet::SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +{ + // Check amount + if (nValue <= 0) + return _("Invalid amount"); + if (nValue + nTransactionFee > GetBalance()) + return _("Insufficient funds"); + + // Parse bitcoin address + CScript scriptPubKey; + if (!scriptPubKey.SetBitcoinAddress(strAddress)) + return _("Invalid bitcoin address"); + + return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); +} + + + + +bool CWallet::LoadWallet(bool& fFirstRunRet) +{ + if (!fFileBacked) + return false; + fFirstRunRet = false; + if (!CWalletDB(strWalletFile,"cr+").LoadWallet(this)) + return false; + fFirstRunRet = vchDefaultKey.empty(); + + if (!mapKeys.count(vchDefaultKey)) + { + // Create new default key + RandAddSeedPerfmon(); + + vchDefaultKey = GetKeyFromKeyPool(); + if (!SetAddressBookName(PubKeyToAddress(vchDefaultKey), "")) + return false; + CWalletDB(strWalletFile).WriteDefaultKey(vchDefaultKey); + } + + CreateThread(ThreadFlushWalletDB, &strWalletFile); + return true; +} + +void CWallet::PrintWallet(const CBlock& block) +{ + CRITICAL_BLOCK(cs_mapWallet) + { + if (mapWallet.count(block.vtx[0].GetHash())) + { + CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; + printf(" mine: %d %d %d", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); + } + } + printf("\n"); +} + +bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) +{ + CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(hashTx); + if (mi != mapWallet.end()) + { + wtx = (*mi).second; + return true; + } + } + return false; +} + +bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) +{ + if (!pwallet->fFileBacked) + return false; + strWalletFileOut = pwallet->strWalletFile; + return true; +} + +void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) +{ + nIndex = -1; + keypool.vchPubKey.clear(); + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_setKeyPool) + { + CWalletDB walletdb(strWalletFile); + + // Top up key pool + int64 nTargetSize = max(GetArg("-keypool", 100), (int64)0); + while (setKeyPool.size() < nTargetSize+1) + { + int64 nEnd = 1; + if (!setKeyPool.empty()) + nEnd = *(--setKeyPool.end()) + 1; + if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) + throw runtime_error("ReserveKeyFromKeyPool() : writing generated key failed"); + setKeyPool.insert(nEnd); + printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size()); + } + + // Get the oldest key + assert(!setKeyPool.empty()); + nIndex = *(setKeyPool.begin()); + setKeyPool.erase(setKeyPool.begin()); + if (!walletdb.ReadPool(nIndex, keypool)) + throw runtime_error("ReserveKeyFromKeyPool() : read failed"); + if (!mapKeys.count(keypool.vchPubKey)) + throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); + assert(!keypool.vchPubKey.empty()); + printf("keypool reserve %"PRI64d"\n", nIndex); + } +} + +void CWallet::KeepKey(int64 nIndex) +{ + // Remove from key pool + if (fFileBacked) + { + CWalletDB walletdb(strWalletFile); + CRITICAL_BLOCK(cs_main) + { + walletdb.ErasePool(nIndex); + } + } + printf("keypool keep %"PRI64d"\n", nIndex); +} + +void CWallet::ReturnKey(int64 nIndex) +{ + // Return to key pool + CRITICAL_BLOCK(cs_setKeyPool) + setKeyPool.insert(nIndex); + printf("keypool return %"PRI64d"\n", nIndex); +} + +bool CWallet::SetAddressBookName(const std::string& strAddress, const std::string& strName) +{ + if (!fFileBacked) + return false; + if(CWalletDB(strWalletFile).WriteName(strAddress, strName)) + { + CRITICAL_BLOCK(cs_mapAddressBook) + mapAddressBook[strAddress] = strName; + return true; + } + else + { + return false; + } +} + +bool CWallet::EraseAddressBookName(const std::string& strAddress) +{ + if (!fFileBacked) + return false; + if(CWalletDB(strWalletFile).EraseName(strAddress)) + { + CRITICAL_BLOCK(cs_mapAddressBook) + mapAddressBook.erase(strAddress); + return true; + } + else + { + return false; + } +} + + +std::string CWallet::GetDefaultAddress() +{ + if (!fFileBacked) + return false; + std::vector vchPubKey; + if (CWalletDB(strWalletFile, "r").ReadDefaultKey(vchPubKey)) + { + return PubKeyToAddress(vchPubKey); + } + else + { + return ""; + } +} + +bool CWallet::SetDefaultAddress(const std::string& strAddress) +{ + uint160 hash160; + if (!AddressToHash160(strAddress, hash160)) + return false; + if (!mapPubKeys.count(hash160)) + return false; + return CWalletDB(strWalletFile).WriteDefaultKey(mapPubKeys[hash160]); +} + + +vector CWallet::GetKeyFromKeyPool() +{ + int64 nIndex = 0; + CKeyPool keypool; + ReserveKeyFromKeyPool(nIndex, keypool); + KeepKey(nIndex); + return keypool.vchPubKey; +} + +int64 CWallet::GetOldestKeyPoolTime() +{ + int64 nIndex = 0; + CKeyPool keypool; + ReserveKeyFromKeyPool(nIndex, keypool); + ReturnKey(nIndex); + return keypool.nTime; +} + +vector CReserveKey::GetReservedKey() +{ + if (nIndex == -1) + { + CKeyPool keypool; + pwallet->ReserveKeyFromKeyPool(nIndex, keypool); + vchPubKey = keypool.vchPubKey; + } + assert(!vchPubKey.empty()); + return vchPubKey; +} + +void CReserveKey::KeepKey() +{ + if (nIndex != -1) + pwallet->KeepKey(nIndex); + nIndex = -1; + vchPubKey.clear(); +} + +void CReserveKey::ReturnKey() +{ + if (nIndex != -1) + pwallet->ReturnKey(nIndex); + nIndex = -1; + vchPubKey.clear(); +} diff --git a/src/wallet.h b/src/wallet.h new file mode 100644 index 0000000000..69110a4afa --- /dev/null +++ b/src/wallet.h @@ -0,0 +1,611 @@ +// Copyright (c) 2009-2011 Satoshi Nakamoto & Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_WALLET_H +#define BITCOIN_WALLET_H + +#include "bignum.h" +#include "key.h" +#include "script.h" + +class CWalletTx; +class CReserveKey; +class CWalletDB; + +class CWallet : public CKeyStore +{ +private: + bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, std::set >& setCoinsRet, int64& nValueRet) const; + bool SelectCoins(int64 nTargetValue, std::set >& setCoinsRet, int64& nValueRet) const; + + +public: + bool fFileBacked; + std::string strWalletFile; + + std::set setKeyPool; + CCriticalSection cs_setKeyPool; + + CWallet() + { + fFileBacked = false; + } + CWallet(std::string strWalletFileIn) + { + strWalletFile = strWalletFileIn; + fFileBacked = true; + } + + mutable CCriticalSection cs_mapWallet; + std::map mapWallet; + std::vector vWalletUpdated; + + std::map mapRequestCount; + mutable CCriticalSection cs_mapRequestCount; + + std::map mapAddressBook; + mutable CCriticalSection cs_mapAddressBook; + + std::vector vchDefaultKey; + + bool AddKey(const CKey& key); + bool AddToWallet(const CWalletTx& wtxIn); + bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false); + bool EraseFromWallet(uint256 hash); + void WalletUpdateSpent(const CTransaction& prevout); + int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); + void ReacceptWalletTransactions(); + void ResendWalletTransactions(); + int64 GetBalance() const; + bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); + bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); + bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); + bool BroadcastTransaction(CWalletTx& wtxNew); + std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); + std::string SendMoneyToBitcoinAddress(std::string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); + + void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool); + void KeepKey(int64 nIndex); + void ReturnKey(int64 nIndex); + std::vector GetKeyFromKeyPool(); + int64 GetOldestKeyPoolTime(); + + bool IsMine(const CTxIn& txin) const; + int64 GetDebit(const CTxIn& txin) const; + bool IsMine(const CTxOut& txout) const + { + return ::IsMine(*this, txout.scriptPubKey); + } + int64 GetCredit(const CTxOut& txout) const + { + if (!MoneyRange(txout.nValue)) + throw std::runtime_error("CWallet::GetCredit() : value out of range"); + return (IsMine(txout) ? txout.nValue : 0); + } + bool IsChange(const CTxOut& txout) const + { + std::vector vchPubKey; + if (ExtractPubKey(txout.scriptPubKey, this, vchPubKey)) + CRITICAL_BLOCK(cs_mapAddressBook) + if (!mapAddressBook.count(PubKeyToAddress(vchPubKey))) + return true; + return false; + } + int64 GetChange(const CTxOut& txout) const + { + if (!MoneyRange(txout.nValue)) + throw std::runtime_error("CWallet::GetChange() : value out of range"); + return (IsChange(txout) ? txout.nValue : 0); + } + bool IsMine(const CTransaction& tx) const + { + BOOST_FOREACH(const CTxOut& txout, tx.vout) + if (IsMine(txout)) + return true; + return false; + } + bool IsFromMe(const CTransaction& tx) const + { + return (GetDebit(tx) > 0); + } + int64 GetDebit(const CTransaction& tx) const + { + int64 nDebit = 0; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + nDebit += GetDebit(txin); + if (!MoneyRange(nDebit)) + throw std::runtime_error("CWallet::GetDebit() : value out of range"); + } + return nDebit; + } + int64 GetCredit(const CTransaction& tx) const + { + int64 nCredit = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nCredit += GetCredit(txout); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWallet::GetCredit() : value out of range"); + } + return nCredit; + } + int64 GetChange(const CTransaction& tx) const + { + int64 nChange = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nChange += GetChange(txout); + if (!MoneyRange(nChange)) + throw std::runtime_error("CWallet::GetChange() : value out of range"); + } + return nChange; + } + void SetBestChain(const CBlockLocator& loc) + { + CWalletDB walletdb(strWalletFile); + walletdb.WriteBestBlock(loc); + } + + bool LoadWallet(bool& fFirstRunRet); +// bool BackupWallet(const std::string& strDest); + bool SetAddressBookName(const std::string& strAddress, const std::string& strName); + bool EraseAddressBookName(const std::string& strAddress); + std::string GetDefaultAddress(); + bool SetDefaultAddress(const std::string& strAddress); + + void UpdatedTransaction(const uint256 &hashTx) + { + CRITICAL_BLOCK(cs_mapWallet) + vWalletUpdated.push_back(hashTx); + } + + void PrintWallet(const CBlock& block); + + void Inventory(const uint256 &hash) + { + CRITICAL_BLOCK(cs_mapRequestCount) + { + std::map::iterator mi = mapRequestCount.find(hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } + } + + bool GetTransaction(const uint256 &hashTx, CWalletTx& wtx); + +}; + + +class CReserveKey +{ +protected: + CWallet* pwallet; + int64 nIndex; + std::vector vchPubKey; +public: + CReserveKey(CWallet* pwalletIn) + { + nIndex = -1; + pwallet = pwalletIn; + } + + ~CReserveKey() + { + if (!fShutdown) + ReturnKey(); + } + + void ReturnKey(); + std::vector GetReservedKey(); + void KeepKey(); +}; + + +// +// A transaction with a bunch of additional info that only the owner cares +// about. It includes any unrecorded transactions needed to link it back +// to the block chain. +// +class CWalletTx : public CMerkleTx +{ +public: + const CWallet* pwallet; + + std::vector vtxPrev; + std::map mapValue; + std::vector > vOrderForm; + unsigned int fTimeReceivedIsTxTime; + unsigned int nTimeReceived; // time received by this node + char fFromMe; + std::string strFromAccount; + std::vector vfSpent; + + // memory only + mutable char fDebitCached; + mutable char fCreditCached; + mutable char fAvailableCreditCached; + mutable char fChangeCached; + mutable int64 nDebitCached; + mutable int64 nCreditCached; + mutable int64 nAvailableCreditCached; + mutable int64 nChangeCached; + + // memory only UI hints + mutable unsigned int nTimeDisplayed; + mutable int nLinesDisplayed; + mutable char fConfirmedDisplayed; + + CWalletTx() + { + Init(NULL); + } + + CWalletTx(const CWallet* pwalletIn) + { + Init(pwalletIn); + } + + CWalletTx(const CWallet* pwalletIn, const CMerkleTx& txIn) : CMerkleTx(txIn) + { + Init(pwalletIn); + } + + CWalletTx(const CWallet* pwalletIn, const CTransaction& txIn) : CMerkleTx(txIn) + { + Init(pwalletIn); + } + + void Init(const CWallet* pwalletIn) + { + pwallet = pwalletIn; + vtxPrev.clear(); + mapValue.clear(); + vOrderForm.clear(); + fTimeReceivedIsTxTime = false; + nTimeReceived = 0; + fFromMe = false; + strFromAccount.clear(); + vfSpent.clear(); + fDebitCached = false; + fCreditCached = false; + fAvailableCreditCached = false; + fChangeCached = false; + nDebitCached = 0; + nCreditCached = 0; + nAvailableCreditCached = 0; + nChangeCached = 0; + nTimeDisplayed = 0; + nLinesDisplayed = 0; + fConfirmedDisplayed = false; + } + + IMPLEMENT_SERIALIZE + ( + CWalletTx* pthis = const_cast(this); + if (fRead) + pthis->Init(NULL); + char fSpent = false; + + if (!fRead) + { + pthis->mapValue["fromaccount"] = pthis->strFromAccount; + + std::string str; + BOOST_FOREACH(char f, vfSpent) + { + str += (f ? '1' : '0'); + if (f) + fSpent = true; + } + pthis->mapValue["spent"] = str; + } + + nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action); + READWRITE(vtxPrev); + READWRITE(mapValue); + READWRITE(vOrderForm); + READWRITE(fTimeReceivedIsTxTime); + READWRITE(nTimeReceived); + READWRITE(fFromMe); + READWRITE(fSpent); + + if (fRead) + { + pthis->strFromAccount = pthis->mapValue["fromaccount"]; + + if (mapValue.count("spent")) + BOOST_FOREACH(char c, pthis->mapValue["spent"]) + pthis->vfSpent.push_back(c != '0'); + else + pthis->vfSpent.assign(vout.size(), fSpent); + } + + pthis->mapValue.erase("fromaccount"); + pthis->mapValue.erase("version"); + pthis->mapValue.erase("spent"); + ) + + // marks certain txout's as spent + // returns true if any update took place + bool UpdateSpent(const std::vector& vfNewSpent) + { + bool fReturn = false; + for (int i=0; i < vfNewSpent.size(); i++) + { + if (i == vfSpent.size()) + break; + + if (vfNewSpent[i] && !vfSpent[i]) + { + vfSpent[i] = true; + fReturn = true; + fAvailableCreditCached = false; + } + } + return fReturn; + } + + void MarkDirty() + { + fCreditCached = false; + fAvailableCreditCached = false; + fDebitCached = false; + fChangeCached = false; + } + + void MarkSpent(unsigned int nOut) + { + if (nOut >= vout.size()) + throw std::runtime_error("CWalletTx::MarkSpent() : nOut out of range"); + vfSpent.resize(vout.size()); + if (!vfSpent[nOut]) + { + vfSpent[nOut] = true; + fAvailableCreditCached = false; + } + } + + bool IsSpent(unsigned int nOut) const + { + if (nOut >= vout.size()) + throw std::runtime_error("CWalletTx::IsSpent() : nOut out of range"); + if (nOut >= vfSpent.size()) + return false; + return (!!vfSpent[nOut]); + } + + int64 GetDebit() const + { + if (vin.empty()) + return 0; + if (fDebitCached) + return nDebitCached; + nDebitCached = pwallet->GetDebit(*this); + fDebitCached = true; + return nDebitCached; + } + + int64 GetCredit(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + // GetBalance can assume transactions in mapWallet won't change + if (fUseCache && fCreditCached) + return nCreditCached; + nCreditCached = pwallet->GetCredit(*this); + fCreditCached = true; + return nCreditCached; + } + + int64 GetAvailableCredit(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableCreditCached) + return nAvailableCreditCached; + + int64 nCredit = 0; + for (int i = 0; i < vout.size(); i++) + { + if (!IsSpent(i)) + { + const CTxOut &txout = vout[i]; + nCredit += pwallet->GetCredit(txout); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableCreditCached = nCredit; + fAvailableCreditCached = true; + return nCredit; + } + + + int64 GetChange() const + { + if (fChangeCached) + return nChangeCached; + nChangeCached = pwallet->GetChange(*this); + fChangeCached = true; + return nChangeCached; + } + + void GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, std::list >& listReceived, + std::list >& listSent, int64& nFee, std::string& strSentAccount) const; + + void GetAccountAmounts(const std::string& strAccount, int64& nGenerated, int64& nReceived, + int64& nSent, int64& nFee) const; + + bool IsFromMe() const + { + return (GetDebit() > 0); + } + + bool IsConfirmed() const + { + // Quick answer in most cases + if (!IsFinal()) + return false; + if (GetDepthInMainChain() >= 1) + return true; + if (!IsFromMe()) // using wtx's cached debit + return false; + + // If no confirmations but it's from us, we can still + // consider it confirmed if all dependencies are confirmed + std::map mapPrev; + std::vector vWorkQueue; + vWorkQueue.reserve(vtxPrev.size()+1); + vWorkQueue.push_back(this); + for (int i = 0; i < vWorkQueue.size(); i++) + { + const CMerkleTx* ptx = vWorkQueue[i]; + + if (!ptx->IsFinal()) + return false; + if (ptx->GetDepthInMainChain() >= 1) + continue; + if (!pwallet->IsFromMe(*ptx)) + return false; + + if (mapPrev.empty()) + BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) + mapPrev[tx.GetHash()] = &tx; + + BOOST_FOREACH(const CTxIn& txin, ptx->vin) + { + if (!mapPrev.count(txin.prevout.hash)) + return false; + vWorkQueue.push_back(mapPrev[txin.prevout.hash]); + } + } + return true; + } + + bool WriteToDisk(); + + int64 GetTxTime() const; + int GetRequestCount() const; + + void AddSupportingTransactions(CTxDB& txdb); + + bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true); + bool AcceptWalletTransaction(); + + void RelayWalletTransaction(CTxDB& txdb); + void RelayWalletTransaction(); +}; + + +// +// Private key that includes an expiration date in case it never gets used. +// +class CWalletKey +{ +public: + CPrivKey vchPrivKey; + int64 nTimeCreated; + int64 nTimeExpires; + std::string strComment; + //// todo: add something to note what created it (user, getnewaddress, change) + //// maybe should have a map property map + + CWalletKey(int64 nExpires=0) + { + nTimeCreated = (nExpires ? GetTime() : 0); + nTimeExpires = nExpires; + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPrivKey); + READWRITE(nTimeCreated); + READWRITE(nTimeExpires); + READWRITE(strComment); + ) +}; + + + + + + +// +// Account information. +// Stored in wallet with key "acc"+string account name +// +class CAccount +{ +public: + std::vector vchPubKey; + + CAccount() + { + SetNull(); + } + + void SetNull() + { + vchPubKey.clear(); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPubKey); + ) +}; + + + +// +// Internal transfers. +// Database key is acentry +// +class CAccountingEntry +{ +public: + std::string strAccount; + int64 nCreditDebit; + int64 nTime; + std::string strOtherAccount; + std::string strComment; + + CAccountingEntry() + { + SetNull(); + } + + void SetNull() + { + nCreditDebit = 0; + nTime = 0; + strAccount.clear(); + strOtherAccount.clear(); + strComment.clear(); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + // Note: strAccount is serialized as part of the key, not here. + READWRITE(nCreditDebit); + READWRITE(nTime); + READWRITE(strOtherAccount); + READWRITE(strComment); + ) +}; + +bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); + +#endif -- cgit v1.2.3 From 34fa178243d462fa8ed8978227626bcd0c622d82 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 26 Jun 2011 22:47:02 +0200 Subject: Change transaction table: - Split "Description" column into "Type" and "Address", to make sorting easier (and facilitate filtering in the future) - Merged "credit" and "debit" columns into one "amount" column that can be black (positive) or red (negative) --- src/qt/bitcoingui.cpp | 26 +++---- src/qt/forms/addressbookdialog.ui | 8 ++- src/qt/transactiontablemodel.cpp | 143 ++++++++++++++++++++------------------ src/qt/transactiontablemodel.h | 12 ++-- 4 files changed, 102 insertions(+), 87 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 76ae3ddb3f..24311bfd29 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -265,6 +265,7 @@ void BitcoinGUI::setTabsModel(QAbstractItemModel *transaction_model) QTableView *transaction_table = transactionViews.at(i); transaction_table->setModel(proxy_model); + transaction_table->setAlternatingRowColors(true); transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); transaction_table->setSortingEnabled(true); @@ -275,12 +276,12 @@ void BitcoinGUI::setTabsModel(QAbstractItemModel *transaction_model) TransactionTableModel::Status, 23); transaction_table->horizontalHeader()->resizeSection( TransactionTableModel::Date, 120); - transaction_table->horizontalHeader()->setResizeMode( - TransactionTableModel::Description, QHeaderView::Stretch); transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Debit, 79); + TransactionTableModel::Type, 120); + transaction_table->horizontalHeader()->setResizeMode( + TransactionTableModel::ToAddress, QHeaderView::Stretch); transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Credit, 79); + TransactionTableModel::Amount, 79); } connect(transaction_model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), @@ -457,23 +458,24 @@ void BitcoinGUI::transactionDetails(const QModelIndex& idx) void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) { TransactionTableModel *ttm = model->getTransactionTableModel(); - qint64 credit = ttm->index(start, TransactionTableModel::Credit, parent) + qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent) .data(Qt::EditRole).toULongLong(); - qint64 debit = ttm->index(start, TransactionTableModel::Debit, parent) - .data(Qt::EditRole).toULongLong(); - if((credit+debit)>0 && !model->inInitialBlockDownload()) + if(amount>0 && !model->inInitialBlockDownload()) { // On incoming transaction, make an info balloon // Unless the initial block download is in progress, to prevent balloon-spam QString date = ttm->index(start, TransactionTableModel::Date, parent) .data().toString(); - QString description = ttm->index(start, TransactionTableModel::Description, parent) + QString type = ttm->index(start, TransactionTableModel::Type, parent) + .data().toString(); + QString address = ttm->index(start, TransactionTableModel::ToAddress, parent) .data().toString(); trayIcon->showMessage(tr("Incoming transaction"), - "Date: " + date + "\n" + - "Amount: " + QString::fromStdString(FormatMoney(credit+debit, true)) + "\n" + - description, + tr("Date: ") + date + "\n" + + tr("Amount: ") + QString::fromStdString(FormatMoney(amount, true)) + "\n" + + tr("Type: ") + type + "\n" + + tr("Address: ") + address + "\n", QSystemTrayIcon::Information); } } diff --git a/src/qt/forms/addressbookdialog.ui b/src/qt/forms/addressbookdialog.ui index f9b95c40c5..0427789687 100644 --- a/src/qt/forms/addressbookdialog.ui +++ b/src/qt/forms/addressbookdialog.ui @@ -17,7 +17,7 @@ - 1 + 0 @@ -29,6 +29,9 @@ + + true + QAbstractItemView::SingleSelection @@ -68,6 +71,9 @@ + + true + QAbstractItemView::SingleSelection diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 18ab421e3c..e49629c9a3 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -18,6 +18,15 @@ const QString TransactionTableModel::Sent = "s"; const QString TransactionTableModel::Received = "r"; const QString TransactionTableModel::Other = "o"; +// Credit and Debit columns are right-aligned as they contain numbers +static int column_alignments[] = { + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignRight|Qt::AlignVCenter + }; + // Comparison operator for sort/binary search of model tx list struct TxLessThan { @@ -195,22 +204,12 @@ struct TransactionTablePriv }; -// Credit and Debit columns are right-aligned as they contain numbers -static int column_alignments[] = { - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignRight|Qt::AlignVCenter, - Qt::AlignRight|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter - }; - TransactionTableModel::TransactionTableModel(CWallet* wallet, QObject *parent): QAbstractTableModel(parent), wallet(wallet), priv(new TransactionTablePriv(wallet, this)) { - columns << tr("Status") << tr("Date") << tr("Description") << tr("Debit") << tr("Credit"); + columns << tr("Status") << tr("Date") << tr("Type") << tr("Address") << tr("Amount"); priv->refreshWallet(); @@ -248,7 +247,7 @@ void TransactionTableModel::update() // Status (number of confirmations) and (possibly) description // columns changed for all rows. emit dataChanged(index(0, Status), index(priv->size()-1, Status)); - emit dataChanged(index(0, Description), index(priv->size()-1, Description)); + emit dataChanged(index(0, ToAddress), index(priv->size()-1, ToAddress)); } } @@ -326,42 +325,70 @@ std::string TransactionTableModel::lookupAddress(const std::string &address) con return description; } -QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx) const +QVariant TransactionTableModel::formatTxType(const TransactionRecord *wtx) const { QString description; switch(wtx->type) { case TransactionRecord::RecvWithAddress: - description = tr("Received with: ") + QString::fromStdString(lookupAddress(wtx->address)); + description = tr("Received with"); break; case TransactionRecord::RecvFromIP: - description = tr("Received from IP: ") + QString::fromStdString(wtx->address); + description = tr("Received from IP"); break; case TransactionRecord::SendToAddress: - description = tr("Sent to: ") + QString::fromStdString(lookupAddress(wtx->address)); + description = tr("Sent to"); break; case TransactionRecord::SendToIP: - description = tr("Sent to IP: ") + QString::fromStdString(wtx->address); + description = tr("Sent to IP"); break; case TransactionRecord::SendToSelf: description = tr("Payment to yourself"); break; + case TransactionRecord::Generated: + description = tr("Generated"); + break; + } + return QVariant(description); +} + +QVariant TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx) const +{ + QString description; + + switch(wtx->type) + { + case TransactionRecord::RecvWithAddress: + description = QString::fromStdString(lookupAddress(wtx->address)); + break; + case TransactionRecord::RecvFromIP: + description = QString::fromStdString(wtx->address); + break; + case TransactionRecord::SendToAddress: + description = QString::fromStdString(lookupAddress(wtx->address)); + break; + case TransactionRecord::SendToIP: + description = QString::fromStdString(wtx->address); + break; + case TransactionRecord::SendToSelf: + description = QString(); + break; case TransactionRecord::Generated: switch(wtx->status.maturity) { case TransactionStatus::Immature: - description = tr("Generated (matures in %n more blocks)", "", + description = tr("(matures in %n more blocks)", "", wtx->status.matures_in); break; case TransactionStatus::Mature: - description = tr("Generated"); + description = QString(); break; case TransactionStatus::MaturesWarning: - description = tr("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!"); + description = tr("(Warning: This block was not received by any other nodes and will probably not be accepted!)"); break; case TransactionStatus::NotAccepted: - description = tr("Generated (not accepted)"); + description = tr("(not accepted)"); break; } break; @@ -369,38 +396,14 @@ QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx return QVariant(description); } -QVariant TransactionTableModel::formatTxDebit(const TransactionRecord *wtx) const +QVariant TransactionTableModel::formatTxAmount(const TransactionRecord *wtx) const { - if(wtx->debit) + QString str = QString::fromStdString(FormatMoney(wtx->credit + wtx->debit)); + if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) { - QString str = QString::fromStdString(FormatMoney(wtx->debit)); - if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) - { - str = QString("[") + str + QString("]"); - } - return QVariant(str); - } - else - { - return QVariant(); - } -} - -QVariant TransactionTableModel::formatTxCredit(const TransactionRecord *wtx) const -{ - if(wtx->credit) - { - QString str = QString::fromStdString(FormatMoney(wtx->credit)); - if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) - { - str = QString("[") + str + QString("]"); - } - return QVariant(str); - } - else - { - return QVariant(); + str = QString("[") + str + QString("]"); } + return QVariant(str); } QVariant TransactionTableModel::formatTxDecoration(const TransactionRecord *wtx) const @@ -449,12 +452,12 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { case Date: return formatTxDate(rec); - case Description: - return formatTxDescription(rec); - case Debit: - return formatTxDebit(rec); - case Credit: - return formatTxCredit(rec); + case Type: + return formatTxType(rec); + case ToAddress: + return formatTxToAddress(rec); + case Amount: + return formatTxAmount(rec); } } else if(role == Qt::EditRole) @@ -466,12 +469,12 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return QString::fromStdString(rec->status.sortKey); case Date: return rec->time; - case Description: - return formatTxDescription(rec); - case Debit: - return rec->debit; - case Credit: - return rec->credit; + case Type: + return formatTxType(rec); + case ToAddress: + return formatTxToAddress(rec); + case Amount: + return rec->credit + rec->debit; } } else if (role == Qt::ToolTipRole) @@ -492,6 +495,10 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { return QColor(128, 128, 128); } + if(index.column() == Amount && (rec->credit+rec->debit) < 0) + { + return QColor(255, 0, 0); + } } else if (role == TypeRole) { @@ -535,12 +542,12 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat return tr("Transaction status. Hover over this field to show number of confirmations."); case Date: return tr("Date and time that the transaction was received."); - case Description: - return tr("Short description of the transaction."); - case Debit: - return tr("Amount removed from balance."); - case Credit: - return tr("Amount added to balance."); + case Type: + return tr("Type of transaction."); + case ToAddress: + return tr("Destination address of transaction."); + case Amount: + return tr("Amount removed from or added to balance."); } } } diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 72a645b3db..d19e1a3af6 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -18,9 +18,9 @@ public: enum { Status = 0, Date = 1, - Description = 2, - Debit = 3, - Credit = 4 + Type = 2, + ToAddress = 3, + Amount = 4 } ColumnIndex; enum { @@ -47,9 +47,9 @@ private: std::string lookupAddress(const std::string &address) const; QVariant formatTxStatus(const TransactionRecord *wtx) const; QVariant formatTxDate(const TransactionRecord *wtx) const; - QVariant formatTxDescription(const TransactionRecord *wtx) const; - QVariant formatTxDebit(const TransactionRecord *wtx) const; - QVariant formatTxCredit(const TransactionRecord *wtx) const; + QVariant formatTxType(const TransactionRecord *wtx) const; + QVariant formatTxToAddress(const TransactionRecord *wtx) const; + QVariant formatTxAmount(const TransactionRecord *wtx) const; QVariant formatTxDecoration(const TransactionRecord *wtx) const; private slots: -- cgit v1.2.3 From 7aff3d5852d263adb2b1b6ae608d29e91c2d2833 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 26 Jun 2011 23:09:41 +0200 Subject: add icons to "New..." and "Copy to clipboard" buttons --- doc/assets-attribution.txt | 3 ++- src/qt/bitcoin.qrc | 1 + src/qt/bitcoingui.cpp | 2 ++ src/qt/res/icons/add.png | Bin 0 -> 1279 bytes 4 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 src/qt/res/icons/add.png diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index b3c9c7c7aa..bd1f599c82 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -27,7 +27,8 @@ License: You are free to do with these icons as you wish, including selling, copying, modifying etc. Icon: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png, - src/qt/res/icons/editcopy.png, src/qt/res/icons/editpaste.png + src/qt/res/icons/editcopy.png, src/qt/res/icons/editpaste.png, + src/qt/res/icons/add.png Designer: http://www.everaldo.com Icon Pack: Crystal SVG License: LGPL diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 663eb4869d..2e4cbbb5bd 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -21,6 +21,7 @@ res/icons/receive.png res/icons/editpaste.png res/icons/editcopy.png + res/icons/add.png
res/images/about.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 24311bfd29..c8230a92b5 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -82,8 +82,10 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QPushButton *button_new = new QPushButton(tr("&New...")); button_new->setToolTip(tr("Create new receiving address")); + button_new->setIcon(QIcon(":/icons/add")); QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); button_clipboard->setToolTip(tr("Copy current receiving address to the system clipboard")); + button_clipboard->setIcon(QIcon(":/icons/editcopy")); hbox_address->addWidget(button_new); hbox_address->addWidget(button_clipboard); diff --git a/src/qt/res/icons/add.png b/src/qt/res/icons/add.png new file mode 100644 index 0000000000..f98e2a8ca7 Binary files /dev/null and b/src/qt/res/icons/add.png differ -- cgit v1.2.3 From 3cfe12c1b70efdcb2ad2e5c714444dda8435f7c8 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 28 Jun 2011 18:29:58 +0200 Subject: use 256x256 window icon, to prevent uglyness on OSes that show full-size icons for apps --- src/qt/res/icons/bitcoin.png | Bin 1023 -> 34168 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/qt/res/icons/bitcoin.png b/src/qt/res/icons/bitcoin.png index 55620f3e03..bf43144e5e 100644 Binary files a/src/qt/res/icons/bitcoin.png and b/src/qt/res/icons/bitcoin.png differ -- cgit v1.2.3 From 19a5975d5a024de37851524d72993f98dbdaff0c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 29 Jun 2011 15:07:14 +0200 Subject: add "BTC" to balance for clarity --- src/qt/bitcoingui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index c8230a92b5..5bf79ee6d1 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -344,7 +344,7 @@ void BitcoinGUI::copyClipboardClicked() void BitcoinGUI::setBalance(qint64 balance) { - labelBalance->setText(QString::fromStdString(FormatMoney(balance))); + labelBalance->setText(QString::fromStdString(FormatMoney(balance)) + QString(" BTC")); } void BitcoinGUI::setAddress(const QString &addr) -- cgit v1.2.3 From ceb6d4e11d8dab8a6778e20c433f6ed989c16221 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 28 Jun 2011 21:41:56 +0200 Subject: Implement filter row instead of tabs, allows for more expressive filters --- bitcoin-qt.pro | 8 +- src/qt/bitcoingui.cpp | 77 ++-------------- src/qt/bitcoingui.h | 4 +- src/qt/guiutil.cpp | 10 +++ src/qt/guiutil.h | 8 +- src/qt/sendcoinsdialog.cpp | 7 +- src/qt/transactionfilterproxy.cpp | 67 ++++++++++++++ src/qt/transactionfilterproxy.h | 45 ++++++++++ src/qt/transactionrecord.h | 3 +- src/qt/transactiontablemodel.cpp | 72 +++++++++------ src/qt/transactiontablemodel.h | 16 +++- src/qt/transactionview.cpp | 182 ++++++++++++++++++++++++++++++++++++++ src/qt/transactionview.h | 53 +++++++++++ 13 files changed, 443 insertions(+), 109 deletions(-) create mode 100644 src/qt/transactionfilterproxy.cpp create mode 100644 src/qt/transactionfilterproxy.h create mode 100644 src/qt/transactionview.cpp create mode 100644 src/qt/transactionview.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 4036c14191..76841f8462 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -72,7 +72,9 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/transactiondescdialog.h \ src/qt/bitcoinamountfield.h \ src/wallet.h \ - src/keystore.h + src/keystore.h \ + src/qt/transactionfilterproxy.h \ + src/qt/transactionview.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -105,7 +107,9 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/bitcoinstrings.cpp \ src/qt/bitcoinamountfield.cpp \ src/wallet.cpp \ - src/keystore.cpp + src/keystore.cpp \ + src/qt/transactionfilterproxy.cpp \ + src/qt/transactionview.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 5bf79ee6d1..44f77460e3 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -15,6 +15,7 @@ #include "optionsmodel.h" #include "transactiondescdialog.h" #include "addresstablemodel.h" +#include "transactionview.h" #include "headers.h" @@ -29,12 +30,9 @@ #include #include #include -#include #include #include -#include #include -#include #include #include #include @@ -104,7 +102,9 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): vbox->addLayout(hbox_address); vbox->addLayout(hbox_balance); - vbox->addWidget(createTabs()); + transactionView = new TransactionView(this); + connect(transactionView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(transactionDetails(const QModelIndex&))); + vbox->addWidget(transactionView); QWidget *centralwidget = new QWidget(this); centralwidget->setLayout(vbox); @@ -198,7 +198,11 @@ void BitcoinGUI::setModel(ClientModel *model) connect(model, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); // Put transaction list in tabs - setTabsModel(model->getTransactionTableModel()); + transactionView->setModel(model->getTransactionTableModel()); + + // Balloon popup for new transaction + connect(model->getTransactionTableModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(incomingTransaction(const QModelIndex &, int, int))); } void BitcoinGUI::createTrayIcon() @@ -227,69 +231,6 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) } } -QWidget *BitcoinGUI::createTabs() -{ - QStringList tab_labels; - tab_labels << tr("All transactions") - << tr("Sent/Received") - << tr("Sent") - << tr("Received"); - - QTabWidget *tabs = new QTabWidget(this); - for(int i = 0; i < tab_labels.size(); ++i) - { - QTableView *view = new QTableView(this); - tabs->addTab(view, tab_labels.at(i)); - - connect(view, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(transactionDetails(const QModelIndex&))); - transactionViews.append(view); - } - - return tabs; -} - -void BitcoinGUI::setTabsModel(QAbstractItemModel *transaction_model) -{ - QStringList tab_filters; - tab_filters << "^." - << "^["+TransactionTableModel::Sent+TransactionTableModel::Received+"]" - << "^["+TransactionTableModel::Sent+"]" - << "^["+TransactionTableModel::Received+"]"; - - for(int i = 0; i < transactionViews.size(); ++i) - { - QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this); - proxy_model->setSourceModel(transaction_model); - proxy_model->setDynamicSortFilter(true); - proxy_model->setFilterRole(TransactionTableModel::TypeRole); - proxy_model->setFilterRegExp(QRegExp(tab_filters.at(i))); - proxy_model->setSortRole(Qt::EditRole); - - QTableView *transaction_table = transactionViews.at(i); - transaction_table->setModel(proxy_model); - transaction_table->setAlternatingRowColors(true); - transaction_table->setSelectionBehavior(QAbstractItemView::SelectRows); - transaction_table->setSelectionMode(QAbstractItemView::ExtendedSelection); - transaction_table->setSortingEnabled(true); - transaction_table->sortByColumn(TransactionTableModel::Status, Qt::DescendingOrder); - transaction_table->verticalHeader()->hide(); - - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Status, 23); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Date, 120); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Type, 120); - transaction_table->horizontalHeader()->setResizeMode( - TransactionTableModel::ToAddress, QHeaderView::Stretch); - transaction_table->horizontalHeader()->resizeSection( - TransactionTableModel::Amount, 79); - } - - connect(transaction_model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), - this, SLOT(incomingTransaction(const QModelIndex &, int, int))); -} - void BitcoinGUI::sendcoinsClicked() { SendCoinsDialog dlg; diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index b3559c3bcd..f6241a476d 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -6,6 +6,7 @@ class TransactionTableModel; class ClientModel; +class TransactionView; QT_BEGIN_NAMESPACE class QLabel; @@ -56,12 +57,11 @@ private: QAction *openBitcoin; QSystemTrayIcon *trayIcon; - QList transactionViews; + TransactionView *transactionView; void createActions(); QWidget *createTabs(); void createTrayIcon(); - void setTabsModel(QAbstractItemModel *transaction_model); public slots: void setBalance(qint64 balance); diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index ec8b43526d..c68532b87c 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1,5 +1,6 @@ #include "guiutil.h" #include "bitcoinaddressvalidator.h" +#include "util.h" #include #include @@ -36,3 +37,12 @@ void GUIUtil::setupAmountWidget(QLineEdit *widget, QWidget *parent) widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter); } +bool GUIUtil::parseMoney(const QString &amount, qint64 *val_out) +{ + return ParseMoney(amount.toStdString(), *val_out); +} + +QString GUIUtil::formatMoney(qint64 amount, bool plussign) +{ + return QString::fromStdString(FormatMoney(amount, plussign)); +} diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 748e29bf37..58f8979511 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -14,12 +14,18 @@ class GUIUtil public: static QString DateTimeStr(qint64 nTime); - /* Render bitcoin addresses in monospace font */ + // Render bitcoin addresses in monospace font static QFont bitcoinAddressFont(); static void setupAddressWidget(QLineEdit *widget, QWidget *parent); static void setupAmountWidget(QLineEdit *widget, QWidget *parent); + + // Convenience wrapper around ParseMoney that takes QString + static bool parseMoney(const QString &amount, qint64 *val_out); + + // Convenience wrapper around FormatMoney that returns QString + static QString formatMoney(qint64 amount, bool plussign=false); }; #endif // GUIUTIL_H diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 67c270e6f8..33d9a25477 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -12,9 +12,6 @@ #include #include -#include "util.h" -#include "base58.h" - SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : QDialog(parent), ui(new Ui::SendCoinsDialog), @@ -49,7 +46,7 @@ void SendCoinsDialog::on_sendButton_clicked() QString label; qint64 payAmountParsed; - valid = ParseMoney(payAmount.toStdString(), payAmountParsed); + valid = GUIUtil::parseMoney(payAmount, &payAmountParsed); if(!valid) { @@ -88,7 +85,7 @@ void SendCoinsDialog::on_sendButton_clicked() case ClientModel::AmountWithFeeExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), tr("Total exceeds your balance when the %1 transaction fee is included"). - arg(QString::fromStdString(FormatMoney(model->getOptionsModel()->getTransactionFee()))), + arg(GUIUtil::formatMoney(model->getOptionsModel()->getTransactionFee())), QMessageBox::Ok, QMessageBox::Ok); ui->payAmount->setFocus(); break; diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp new file mode 100644 index 0000000000..cd1194d992 --- /dev/null +++ b/src/qt/transactionfilterproxy.cpp @@ -0,0 +1,67 @@ +#include "transactionfilterproxy.h" +#include "transactiontablemodel.h" + +#include +#include + +// Earliest date that can be represented (far in the past) +const QDateTime TransactionFilterProxy::MIN_DATE = QDateTime::fromTime_t(0); +// Last date that can be represented (far in the future) +const QDateTime TransactionFilterProxy::MAX_DATE = QDateTime::fromTime_t(0xFFFFFFFF); + +TransactionFilterProxy::TransactionFilterProxy(QObject *parent) : + QSortFilterProxyModel(parent), + dateFrom(MIN_DATE), + dateTo(MAX_DATE), + addrPrefix(), + typeFilter(ALL_TYPES), + minAmount(0) +{ +} + +bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + + int type = index.data(TransactionTableModel::TypeRole).toInt(); + QDateTime datetime = index.data(TransactionTableModel::DateRole).toDateTime(); + QString address = index.data(TransactionTableModel::AddressRole).toString(); + QString label = index.data(TransactionTableModel::LabelRole).toString(); + qint64 amount = index.data(TransactionTableModel::AbsoluteAmountRole).toLongLong(); + + if(!(TYPE(type) & typeFilter)) + return false; + if(datetime < dateFrom || datetime > dateTo) + return false; + if(!address.startsWith(addrPrefix) && !label.startsWith(addrPrefix)) + return false; + if(amount < minAmount) + return false; + + return true; +} + +void TransactionFilterProxy::setDateRange(const QDateTime &from, const QDateTime &to) +{ + this->dateFrom = from; + this->dateTo = to; + invalidateFilter(); +} + +void TransactionFilterProxy::setAddressPrefix(const QString &addrPrefix) +{ + this->addrPrefix = addrPrefix; + invalidateFilter(); +} + +void TransactionFilterProxy::setTypeFilter(quint32 modes) +{ + this->typeFilter = modes; + invalidateFilter(); +} + +void TransactionFilterProxy::setMinAmount(qint64 minimum) +{ + this->minAmount = minimum; + invalidateFilter(); +} diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h new file mode 100644 index 0000000000..a44c9c4df7 --- /dev/null +++ b/src/qt/transactionfilterproxy.h @@ -0,0 +1,45 @@ +#ifndef TRANSACTIONFILTERPROXY_H +#define TRANSACTIONFILTERPROXY_H + +#include +#include + +// Filter transaction list according to pre-specified rules +class TransactionFilterProxy : public QSortFilterProxyModel +{ + Q_OBJECT +public: + explicit TransactionFilterProxy(QObject *parent = 0); + + // Earliest date that can be represented (far in the past) + static const QDateTime MIN_DATE; + // Last date that can be represented (far in the future) + static const QDateTime MAX_DATE; + // Type filter bit field (all types) + static const quint32 ALL_TYPES = 0xFFFFFFFF; + + static quint32 TYPE(int type) { return 1< class CWallet; +class CWalletTx; class TransactionStatus { diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index e49629c9a3..28d22b8530 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include const QString TransactionTableModel::Sent = "s"; @@ -301,27 +302,38 @@ QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const } } -/* Look up address in address book, if found return - address[0:12]... (label) - otherwise just return address +/* Look up label for address in address book, if not found return empty string. + This should really move to the wallet class. */ -std::string TransactionTableModel::lookupAddress(const std::string &address) const +QString TransactionTableModel::labelForAddress(const std::string &address) const { - std::string description; CRITICAL_BLOCK(wallet->cs_mapAddressBook) { std::map::iterator mi = wallet->mapAddressBook.find(address); - if (mi != wallet->mapAddressBook.end() && !(*mi).second.empty()) - { - std::string label = (*mi).second; - description += address.substr(0,12) + "... "; - description += "(" + label + ")"; - } - else + if (mi != wallet->mapAddressBook.end()) { - description += address; + return QString::fromStdString(mi->second); } } + return QString(); +} + +/* Look up address in address book, if found return + address[0:12]... (label) + otherwise just return address + */ +QString TransactionTableModel::lookupAddress(const std::string &address) const +{ + QString label = labelForAddress(address); + QString description; + if(label.isEmpty()) + { + description = QString::fromStdString(address); + } + else + { + description = QString::fromStdString(address.substr(0,12)) + QString("... (") + label + QString(")"); + } return description; } @@ -360,13 +372,13 @@ QVariant TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx) switch(wtx->type) { case TransactionRecord::RecvWithAddress: - description = QString::fromStdString(lookupAddress(wtx->address)); + description = lookupAddress(wtx->address); break; case TransactionRecord::RecvFromIP: description = QString::fromStdString(wtx->address); break; case TransactionRecord::SendToAddress: - description = QString::fromStdString(lookupAddress(wtx->address)); + description = lookupAddress(wtx->address); break; case TransactionRecord::SendToIP: description = QString::fromStdString(wtx->address); @@ -502,24 +514,28 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const } else if (role == TypeRole) { - /* Role for filtering tabs by type */ - switch(rec->type) - { - case TransactionRecord::RecvWithAddress: - case TransactionRecord::RecvFromIP: - return TransactionTableModel::Received; - case TransactionRecord::SendToAddress: - case TransactionRecord::SendToIP: - case TransactionRecord::SendToSelf: - return TransactionTableModel::Sent; - default: - return TransactionTableModel::Other; - } + return rec->type; + } + else if (role == DateRole) + { + return QDateTime::fromTime_t(static_cast(rec->time)); } else if (role == LongDescriptionRole) { return priv->describe(rec); } + else if (role == AddressRole) + { + return QString::fromStdString(rec->address); + } + else if (role == LabelRole) + { + return labelForAddress(rec->address); + } + else if (role == AbsoluteAmountRole) + { + return llabs(rec->credit + rec->debit); + } return QVariant(); } diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index d19e1a3af6..c26acbc101 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -23,9 +23,20 @@ public: Amount = 4 } ColumnIndex; + // Roles to get specific information from a transaction row enum { + // Type of transaction TypeRole = Qt::UserRole, - LongDescriptionRole = Qt::UserRole+1 + // Date and time this transaction was created + DateRole, + // Long description (HTML format) + LongDescriptionRole, + // Address of transaction + AddressRole, + // Label of address related to transaction + LabelRole, + // Absolute net amount of transaction + AbsoluteAmountRole } RoleIndex; /* TypeRole values */ @@ -44,7 +55,8 @@ private: QStringList columns; TransactionTablePriv *priv; - std::string lookupAddress(const std::string &address) const; + QString labelForAddress(const std::string &address) const; + QString lookupAddress(const std::string &address) const; QVariant formatTxStatus(const TransactionRecord *wtx) const; QVariant formatTxDate(const TransactionRecord *wtx) const; QVariant formatTxType(const TransactionRecord *wtx) const; diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp new file mode 100644 index 0000000000..bf7d55d7db --- /dev/null +++ b/src/qt/transactionview.cpp @@ -0,0 +1,182 @@ +#include "transactionview.h" + +// Temp includes for filtering prototype +// Move to TransactionFilterRow class +#include "transactionfilterproxy.h" +#include "transactionrecord.h" +#include "transactiontablemodel.h" +#include "guiutil.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +TransactionView::TransactionView(QWidget *parent) : + QWidget(parent), model(0), transactionProxyModel(0), + transactionView(0) +{ + // Build filter row + QHBoxLayout *hlayout = new QHBoxLayout(); + hlayout->setContentsMargins(QMargins(0,0,0,0)); + hlayout->setSpacing(0); + + hlayout->addSpacing(23); + + dateWidget = new QComboBox(this); + dateWidget->setMaximumWidth(120); + dateWidget->setMinimumWidth(120); + dateWidget->addItem(tr("All"), All); + dateWidget->addItem(tr("Today"), Today); + dateWidget->addItem(tr("This week"), ThisWeek); + dateWidget->addItem(tr("This month"), ThisMonth); + dateWidget->addItem(tr("This year"), ThisYear); + dateWidget->addItem(tr("Range..."), Range); + hlayout->addWidget(dateWidget); + + typeWidget = new QComboBox(this); + typeWidget->setMaximumWidth(120); + typeWidget->setMinimumWidth(120); + + typeWidget->addItem(tr("All"), TransactionFilterProxy::ALL_TYPES); + typeWidget->addItem(tr("Received with"), TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) | + TransactionFilterProxy::TYPE(TransactionRecord::RecvFromIP)); + typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) | + TransactionFilterProxy::TYPE(TransactionRecord::SendToIP)); + typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf)); + typeWidget->addItem(tr("Generated"), TransactionFilterProxy::TYPE(TransactionRecord::Generated)); + typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other)); + + hlayout->addWidget(typeWidget); + + addressWidget = new QLineEdit(this); + addressWidget->setPlaceholderText("Enter address or label to search"); + hlayout->addWidget(addressWidget); + + amountWidget = new QLineEdit(this); + amountWidget->setPlaceholderText("Min amount"); + amountWidget->setMaximumWidth(79); + amountWidget->setMinimumWidth(79); + amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this)); + hlayout->addWidget(amountWidget); + + QVBoxLayout *vlayout = new QVBoxLayout(this); + + QTableView *view = new QTableView(this); + vlayout->addLayout(hlayout); + vlayout->addWidget(view); + vlayout->setSpacing(0); + int width = view->verticalScrollBar()->sizeHint().width(); + // Cover scroll bar width with spacing + hlayout->addSpacing(width); + // Always show scroll bar + view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + + transactionView = view; + + connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); + connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); + connect(addressWidget, SIGNAL(textChanged(const QString&)), this, SLOT(changedPrefix(const QString&))); + connect(amountWidget, SIGNAL(textChanged(const QString&)), this, SLOT(changedAmount(const QString&))); +} + +void TransactionView::setModel(TransactionTableModel *model) +{ + this->model = model; + + transactionProxyModel = new TransactionFilterProxy(this); + transactionProxyModel->setSourceModel(model); + transactionProxyModel->setDynamicSortFilter(true); + + transactionProxyModel->setSortRole(Qt::EditRole); + + transactionView->setModel(transactionProxyModel); + transactionView->setAlternatingRowColors(true); + transactionView->setSelectionBehavior(QAbstractItemView::SelectRows); + transactionView->setSelectionMode(QAbstractItemView::ExtendedSelection); + transactionView->setSortingEnabled(true); + transactionView->sortByColumn(TransactionTableModel::Status, Qt::DescendingOrder); + transactionView->verticalHeader()->hide(); + + transactionView->horizontalHeader()->resizeSection( + TransactionTableModel::Status, 23); + transactionView->horizontalHeader()->resizeSection( + TransactionTableModel::Date, 120); + transactionView->horizontalHeader()->resizeSection( + TransactionTableModel::Type, 120); + transactionView->horizontalHeader()->setResizeMode( + TransactionTableModel::ToAddress, QHeaderView::Stretch); + transactionView->horizontalHeader()->resizeSection( + TransactionTableModel::Amount, 79); + +} + +void TransactionView::chooseDate(int idx) +{ + QDate current = QDate::currentDate(); + switch(dateWidget->itemData(idx).toInt()) + { + case All: + transactionProxyModel->setDateRange( + TransactionFilterProxy::MIN_DATE, + TransactionFilterProxy::MAX_DATE); + break; + case Today: + transactionProxyModel->setDateRange( + QDateTime(current), + TransactionFilterProxy::MAX_DATE); + break; + case ThisWeek: { + // Find last monday + QDate startOfWeek = current.addDays(-(current.dayOfWeek()-1)); + transactionProxyModel->setDateRange( + QDateTime(startOfWeek), + TransactionFilterProxy::MAX_DATE); + + } break; + case ThisMonth: + transactionProxyModel->setDateRange( + QDateTime(QDate(current.year(), current.month(), 1)), + TransactionFilterProxy::MAX_DATE); + break; + case ThisYear: + transactionProxyModel->setDateRange( + QDateTime(QDate(current.year(), 1, 1)), + TransactionFilterProxy::MAX_DATE); + break; + case Range: + // TODO ask specific range + break; + } + +} + +void TransactionView::chooseType(int idx) +{ + transactionProxyModel->setTypeFilter( + typeWidget->itemData(idx).toInt()); +} + +void TransactionView::changedPrefix(const QString &prefix) +{ + transactionProxyModel->setAddressPrefix(prefix); +} + +void TransactionView::changedAmount(const QString &amount) +{ + qint64 amount_parsed; + if(GUIUtil::parseMoney(amount, &amount_parsed)) + { + transactionProxyModel->setMinAmount(amount_parsed); + } + else + { + transactionProxyModel->setMinAmount(0); + } +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h new file mode 100644 index 0000000000..e75dcc2593 --- /dev/null +++ b/src/qt/transactionview.h @@ -0,0 +1,53 @@ +#ifndef TRANSACTIONVIEW_H +#define TRANSACTIONVIEW_H + +#include + +class TransactionTableModel; +class TransactionFilterProxy; + +QT_BEGIN_NAMESPACE +class QTableView; +class QComboBox; +class QLineEdit; +QT_END_NAMESPACE + +class TransactionView : public QWidget +{ + Q_OBJECT +public: + explicit TransactionView(QWidget *parent = 0); + + void setModel(TransactionTableModel *model); + + enum DateEnum + { + All, + Today, + ThisWeek, + ThisMonth, + ThisYear, + Range + }; + +private: + TransactionTableModel *model; + TransactionFilterProxy *transactionProxyModel; + QTableView *transactionView; + + QComboBox *dateWidget; + QComboBox *typeWidget; + QLineEdit *addressWidget; + QLineEdit *amountWidget; + +signals: + +public slots: + void chooseDate(int idx); + void chooseType(int idx); + void changedPrefix(const QString &prefix); + void changedAmount(const QString &amount); + +}; + +#endif // TRANSACTIONVIEW_H -- cgit v1.2.3 From 6ed283946cc34d489e366cecb64142d551fc06f9 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 30 Jun 2011 17:22:03 +0200 Subject: Make it more clear what the "New" button does --- src/qt/bitcoingui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 44f77460e3..f5885dbe67 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -78,7 +78,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): address->setToolTip(tr("Your current default receiving address")); hbox_address->addWidget(address); - QPushButton *button_new = new QPushButton(tr("&New...")); + QPushButton *button_new = new QPushButton(tr("&New address...")); button_new->setToolTip(tr("Create new receiving address")); button_new->setIcon(QIcon(":/icons/add")); QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); @@ -86,7 +86,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): button_clipboard->setIcon(QIcon(":/icons/editcopy")); hbox_address->addWidget(button_new); hbox_address->addWidget(button_clipboard); - + // Balance: QHBoxLayout *hbox_balance = new QHBoxLayout(); hbox_balance->addWidget(new QLabel(tr("Balance:"))); -- cgit v1.2.3 From 929eb9dc6cc65d1ff47ff21dcb9fa5974a9278ee Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 30 Jun 2011 17:32:19 +0200 Subject: show an error if amount is not valid (either the amount or decimals is empty) --- src/qt/bitcoinamountfield.cpp | 2 ++ src/qt/sendcoinsdialog.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 8166ce6e6e..a4cbbe0a7e 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -49,6 +49,8 @@ void BitcoinAmountField::setText(const QString &text) QString BitcoinAmountField::text() const { + if(amount->text().isEmpty() || decimals->text().isEmpty()) + return QString(); return amount->text() + QString(".") + decimals->text(); } diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 33d9a25477..d83962d1bd 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -48,10 +48,10 @@ void SendCoinsDialog::on_sendButton_clicked() valid = GUIUtil::parseMoney(payAmount, &payAmountParsed); - if(!valid) + if(!valid || payAmount.isEmpty()) { QMessageBox::warning(this, tr("Send Coins"), - tr("The amount to pay must be a valid number."), + tr("Must fill in an amount to pay."), QMessageBox::Ok, QMessageBox::Ok); return; } -- cgit v1.2.3 From ef079e183bf1be9f5a61a05018ee4480db86bc45 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 30 Jun 2011 18:05:29 +0200 Subject: Split off WalletModel from ClientModel, to be able to support multi-wallets in future --- bitcoin-qt.pro | 6 ++- src/qt/bitcoin.cpp | 7 ++- src/qt/bitcoingui.cpp | 69 +++++++++++++++---------- src/qt/bitcoingui.h | 7 ++- src/qt/clientmodel.cpp | 93 +--------------------------------- src/qt/clientmodel.h | 31 +++--------- src/qt/sendcoinsdialog.cpp | 14 ++--- src/qt/sendcoinsdialog.h | 6 +-- src/qt/walletmodel.cpp | 124 +++++++++++++++++++++++++++++++++++++++++++++ src/qt/walletmodel.h | 62 +++++++++++++++++++++++ 10 files changed, 259 insertions(+), 160 deletions(-) create mode 100644 src/qt/walletmodel.cpp create mode 100644 src/qt/walletmodel.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 76841f8462..539c326447 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -74,7 +74,8 @@ HEADERS += src/qt/bitcoingui.h \ src/wallet.h \ src/keystore.h \ src/qt/transactionfilterproxy.h \ - src/qt/transactionview.h + src/qt/transactionview.h \ + src/qt/walletmodel.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -109,7 +110,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/wallet.cpp \ src/keystore.cpp \ src/qt/transactionfilterproxy.cpp \ - src/qt/transactionview.cpp + src/qt/transactionview.cpp \ + src/qt/walletmodel.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index c31be1b606..397af5fd38 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -3,6 +3,7 @@ */ #include "bitcoingui.h" #include "clientmodel.h" +#include "walletmodel.h" #include "headers.h" #include "init.h" @@ -113,9 +114,11 @@ int main(int argc, char *argv[]) if(AppInit2(argc, argv)) { BitcoinGUI window; - ClientModel model(pwalletMain); + ClientModel clientModel(pwalletMain); + WalletModel walletModel(pwalletMain); guiref = &window; - window.setModel(&model); + window.setClientModel(&clientModel); + window.setWalletModel(&walletModel); window.show(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index f5885dbe67..2b1990b9cb 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -10,6 +10,7 @@ #include "optionsdialog.h" #include "aboutdialog.h" #include "clientmodel.h" +#include "walletmodel.h" #include "guiutil.h" #include "editaddressdialog.h" #include "optionsmodel.h" @@ -42,7 +43,10 @@ #include BitcoinGUI::BitcoinGUI(QWidget *parent): - QMainWindow(parent), trayIcon(0) + QMainWindow(parent), + clientModel(0), + walletModel(0), + trayIcon(0) { resize(850, 550); setWindowTitle(tr("Bitcoin")); @@ -174,34 +178,43 @@ void BitcoinGUI::createActions() connect(openBitcoin, SIGNAL(triggered()), this, SLOT(show())); } -void BitcoinGUI::setModel(ClientModel *model) +void BitcoinGUI::setClientModel(ClientModel *clientModel) { - this->model = model; + this->clientModel = clientModel; // Keep up to date with client - setBalance(model->getBalance()); - connect(model, SIGNAL(balanceChanged(qint64)), this, SLOT(setBalance(qint64))); + setNumConnections(clientModel->getNumConnections()); + connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); - setNumConnections(model->getNumConnections()); - connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); + setNumBlocks(clientModel->getNumBlocks()); + connect(clientModel, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int))); - setNumTransactions(model->getNumTransactions()); - connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); + // Report errors from network/worker thread + connect(clientModel, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); +} + +void BitcoinGUI::setWalletModel(WalletModel *walletModel) +{ + this->walletModel = walletModel; - setNumBlocks(model->getNumBlocks()); - connect(model, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int))); + // Keep up to date with wallet + setBalance(walletModel->getBalance()); + connect(walletModel, SIGNAL(balanceChanged(qint64)), this, SLOT(setBalance(qint64))); - setAddress(model->getAddressTableModel()->getDefaultAddress()); - connect(model->getAddressTableModel(), SIGNAL(defaultAddressChanged(QString)), this, SLOT(setAddress(QString))); + setNumTransactions(walletModel->getNumTransactions()); + connect(walletModel, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); - // Report errors from network/worker thread - connect(model, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); + setAddress(walletModel->getAddressTableModel()->getDefaultAddress()); + connect(walletModel->getAddressTableModel(), SIGNAL(defaultAddressChanged(QString)), this, SLOT(setAddress(QString))); + + // Report errors from wallet thread + connect(walletModel, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); // Put transaction list in tabs - transactionView->setModel(model->getTransactionTableModel()); + transactionView->setModel(walletModel->getTransactionTableModel()); // Balloon popup for new transaction - connect(model->getTransactionTableModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(incomingTransaction(const QModelIndex &, int, int))); } @@ -234,14 +247,14 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) void BitcoinGUI::sendcoinsClicked() { SendCoinsDialog dlg; - dlg.setModel(model); + dlg.setModel(walletModel); dlg.exec(); } void BitcoinGUI::addressbookClicked() { AddressBookDialog dlg(AddressBookDialog::ForEditing); - dlg.setModel(model->getAddressTableModel()); + dlg.setModel(walletModel->getAddressTableModel()); dlg.setTab(AddressBookDialog::SendingTab); dlg.exec(); } @@ -249,7 +262,7 @@ void BitcoinGUI::addressbookClicked() void BitcoinGUI::receivingAddressesClicked() { AddressBookDialog dlg(AddressBookDialog::ForEditing); - dlg.setModel(model->getAddressTableModel()); + dlg.setModel(walletModel->getAddressTableModel()); dlg.setTab(AddressBookDialog::ReceivingTab); dlg.exec(); } @@ -257,7 +270,7 @@ void BitcoinGUI::receivingAddressesClicked() void BitcoinGUI::optionsClicked() { OptionsDialog dlg; - dlg.setModel(model->getOptionsModel()); + dlg.setModel(clientModel->getOptionsModel()); dlg.exec(); } @@ -270,7 +283,7 @@ void BitcoinGUI::aboutClicked() void BitcoinGUI::newAddressClicked() { EditAddressDialog dlg(EditAddressDialog::NewReceivingAddress); - dlg.setModel(model->getAddressTableModel()); + dlg.setModel(walletModel->getAddressTableModel()); if(dlg.exec()) { QString newAddress = dlg.saveCurrentRow(); @@ -310,7 +323,7 @@ void BitcoinGUI::setNumConnections(int count) void BitcoinGUI::setNumBlocks(int count) { - int total = model->getTotalBlocksEstimate(); + int total = clientModel->getTotalBlocksEstimate(); if(count < total) { progressBarLabel->setVisible(true); @@ -353,7 +366,7 @@ void BitcoinGUI::changeEvent(QEvent *e) { if (e->type() == QEvent::WindowStateChange) { - if(model->getOptionsModel()->getMinimizeToTray()) + if(clientModel->getOptionsModel()->getMinimizeToTray()) { if (isMinimized()) { @@ -371,8 +384,8 @@ void BitcoinGUI::changeEvent(QEvent *e) void BitcoinGUI::closeEvent(QCloseEvent *event) { - if(!model->getOptionsModel()->getMinimizeToTray() && - !model->getOptionsModel()->getMinimizeOnClose()) + if(!clientModel->getOptionsModel()->getMinimizeToTray() && + !clientModel->getOptionsModel()->getMinimizeOnClose()) { qApp->quit(); } @@ -400,10 +413,10 @@ void BitcoinGUI::transactionDetails(const QModelIndex& idx) void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) { - TransactionTableModel *ttm = model->getTransactionTableModel(); + TransactionTableModel *ttm = walletModel->getTransactionTableModel(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent) .data(Qt::EditRole).toULongLong(); - if(amount>0 && !model->inInitialBlockDownload()) + if(amount>0 && !clientModel->inInitialBlockDownload()) { // On incoming transaction, make an info balloon // Unless the initial block download is in progress, to prevent balloon-spam diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index f6241a476d..41b665c274 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -6,6 +6,7 @@ class TransactionTableModel; class ClientModel; +class WalletModel; class TransactionView; QT_BEGIN_NAMESPACE @@ -22,7 +23,8 @@ class BitcoinGUI : public QMainWindow Q_OBJECT public: explicit BitcoinGUI(QWidget *parent = 0); - void setModel(ClientModel *model); + void setClientModel(ClientModel *clientModel); + void setWalletModel(WalletModel *walletModel); /* Transaction table tab indices */ enum { @@ -37,7 +39,8 @@ protected: void closeEvent(QCloseEvent *event); private: - ClientModel *model; + ClientModel *clientModel; + WalletModel *walletModel; QLineEdit *address; QLabel *labelBalance; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index b70b71ee4f..125cb91017 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -9,8 +9,7 @@ #include ClientModel::ClientModel(CWallet *wallet, QObject *parent) : - QObject(parent), wallet(wallet), optionsModel(0), addressTableModel(0), - transactionTableModel(0) + QObject(parent), wallet(wallet), optionsModel(0) { // Until signal notifications is built into the bitcoin core, // simply update everything after polling using a timer. @@ -19,13 +18,6 @@ ClientModel::ClientModel(CWallet *wallet, QObject *parent) : timer->start(MODEL_UPDATE_DELAY); optionsModel = new OptionsModel(wallet, this); - addressTableModel = new AddressTableModel(wallet, this); - transactionTableModel = new TransactionTableModel(wallet, this); -} - -qint64 ClientModel::getBalance() const -{ - return wallet->GetBalance(); } int ClientModel::getNumConnections() const @@ -38,86 +30,13 @@ int ClientModel::getNumBlocks() const return nBestHeight; } -int ClientModel::getNumTransactions() const -{ - int numTransactions = 0; - CRITICAL_BLOCK(wallet->cs_mapWallet) - { - numTransactions = wallet->mapWallet.size(); - } - return numTransactions; -} - void ClientModel::update() { // Plainly emit all signals for now. To be more efficient this should check // whether the values actually changed first, although it'd be even better if these // were events coming in from the bitcoin core. - emit balanceChanged(getBalance()); emit numConnectionsChanged(getNumConnections()); emit numBlocksChanged(getNumBlocks()); - emit numTransactionsChanged(getNumTransactions()); - - addressTableModel->update(); -} - -ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs) -{ - uint160 hash160 = 0; - bool valid = false; - - if(!AddressToHash160(payTo.toUtf8().constData(), hash160)) - { - return InvalidAddress; - } - - if(payAmount <= 0) - { - return InvalidAmount; - } - - if(payAmount > getBalance()) - { - return AmountExceedsBalance; - } - - if((payAmount + nTransactionFee) > getBalance()) - { - return AmountWithFeeExceedsBalance; - } - - CRITICAL_BLOCK(cs_main) - { - // Send to bitcoin address - CWalletTx wtx; - CScript scriptPubKey; - scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; - - std::string strError = wallet->SendMoney(scriptPubKey, payAmount, wtx, true); - if (strError == "") - { - // OK - } - else if (strError == "ABORTED") - { - return Aborted; - } - else - { - emit error(tr("Sending..."), QString::fromStdString(strError)); - return MiscError; - } - } - - // Add addresses that we've sent to to the address book - std::string strAddress = payTo.toStdString(); - CRITICAL_BLOCK(wallet->cs_mapAddressBook) - { - if (!wallet->mapAddressBook.count(strAddress)) - wallet->SetAddressBookName(strAddress, addToAddressBookAs.toStdString()); - } - - return OK; } bool ClientModel::inInitialBlockDownload() const @@ -130,18 +49,8 @@ int ClientModel::getTotalBlocksEstimate() const return GetTotalBlocksEstimate(); } - OptionsModel *ClientModel::getOptionsModel() { return optionsModel; } -AddressTableModel *ClientModel::getAddressTableModel() -{ - return addressTableModel; -} - -TransactionTableModel *ClientModel::getTransactionTableModel() -{ - return transactionTableModel; -} diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 9c23a14a0a..a5028ff3b8 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -8,52 +8,35 @@ class AddressTableModel; class TransactionTableModel; class CWallet; +// Interface to Bitcoin network client class ClientModel : public QObject { Q_OBJECT public: + // The only reason that this constructor takes a wallet is because + // the global client settings are stored in the main wallet. explicit ClientModel(CWallet *wallet, QObject *parent = 0); - enum StatusCode - { - OK, - InvalidAmount, - InvalidAddress, - AmountExceedsBalance, - AmountWithFeeExceedsBalance, - Aborted, - MiscError - }; - OptionsModel *getOptionsModel(); - AddressTableModel *getAddressTableModel(); - TransactionTableModel *getTransactionTableModel(); - qint64 getBalance() const; int getNumConnections() const; int getNumBlocks() const; - int getNumTransactions() const; - /* Return true if core is doing initial block download */ + // Return true if core is doing initial block download bool inInitialBlockDownload() const; - /* Return conservative estimate of total number of blocks, or 0 if unknown */ + // Return conservative estimate of total number of blocks, or 0 if unknown int getTotalBlocksEstimate() const; - /* Send coins */ - StatusCode sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs=QString()); private: CWallet *wallet; OptionsModel *optionsModel; - AddressTableModel *addressTableModel; - TransactionTableModel *transactionTableModel; signals: - void balanceChanged(qint64 balance); void numConnectionsChanged(int count); void numBlocksChanged(int count); - void numTransactionsChanged(int count); - /* Asynchronous error notification */ + + // Asynchronous error notification void error(const QString &title, const QString &message); public slots: diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index d83962d1bd..5c889b2347 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1,6 +1,6 @@ #include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" -#include "clientmodel.h" +#include "walletmodel.h" #include "guiutil.h" #include "addressbookdialog.h" @@ -29,7 +29,7 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : } } -void SendCoinsDialog::setModel(ClientModel *model) +void SendCoinsDialog::setModel(WalletModel *model) { this->model = model; } @@ -64,32 +64,32 @@ void SendCoinsDialog::on_sendButton_clicked() switch(model->sendCoins(ui->payTo->text(), payAmountParsed, label)) { - case ClientModel::InvalidAddress: + case WalletModel::InvalidAddress: QMessageBox::warning(this, tr("Send Coins"), tr("The recepient address is not valid, please recheck."), QMessageBox::Ok, QMessageBox::Ok); ui->payTo->setFocus(); break; - case ClientModel::InvalidAmount: + case WalletModel::InvalidAmount: QMessageBox::warning(this, tr("Send Coins"), tr("The amount to pay must be larger than 0."), QMessageBox::Ok, QMessageBox::Ok); ui->payAmount->setFocus(); break; - case ClientModel::AmountExceedsBalance: + case WalletModel::AmountExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), tr("Amount exceeds your balance"), QMessageBox::Ok, QMessageBox::Ok); ui->payAmount->setFocus(); break; - case ClientModel::AmountWithFeeExceedsBalance: + case WalletModel::AmountWithFeeExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), tr("Total exceeds your balance when the %1 transaction fee is included"). arg(GUIUtil::formatMoney(model->getOptionsModel()->getTransactionFee())), QMessageBox::Ok, QMessageBox::Ok); ui->payAmount->setFocus(); break; - case ClientModel::OK: + case WalletModel::OK: accept(); break; } diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 206a854ee1..bbb6a5fc4d 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -6,7 +6,7 @@ namespace Ui { class SendCoinsDialog; } -class ClientModel; +class WalletModel; class SendCoinsDialog : public QDialog { @@ -16,11 +16,11 @@ public: explicit SendCoinsDialog(QWidget *parent = 0, const QString &address = ""); ~SendCoinsDialog(); - void setModel(ClientModel *model); + void setModel(WalletModel *model); private: Ui::SendCoinsDialog *ui; - ClientModel *model; + WalletModel *model; private slots: void on_addToAddressBook_toggled(bool checked); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp new file mode 100644 index 0000000000..0fb7d21087 --- /dev/null +++ b/src/qt/walletmodel.cpp @@ -0,0 +1,124 @@ +#include "walletmodel.h" +#include "guiconstants.h" +#include "optionsmodel.h" +#include "addresstablemodel.h" +#include "transactiontablemodel.h" + +#include "headers.h" + +#include + +WalletModel::WalletModel(CWallet *wallet, QObject *parent) : + QObject(parent), wallet(wallet), optionsModel(0), addressTableModel(0), + transactionTableModel(0) +{ + // Until signal notifications is built into the bitcoin core, + // simply update everything after polling using a timer. + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(update())); + timer->start(MODEL_UPDATE_DELAY); + + optionsModel = new OptionsModel(wallet, this); + addressTableModel = new AddressTableModel(wallet, this); + transactionTableModel = new TransactionTableModel(wallet, this); +} + +qint64 WalletModel::getBalance() const +{ + return wallet->GetBalance(); +} + +int WalletModel::getNumTransactions() const +{ + int numTransactions = 0; + CRITICAL_BLOCK(wallet->cs_mapWallet) + { + numTransactions = wallet->mapWallet.size(); + } + return numTransactions; +} + +void WalletModel::update() +{ + // Plainly emit all signals for now. To be more efficient this should check + // whether the values actually changed first, although it'd be even better if these + // were events coming in from the bitcoin core. + emit balanceChanged(getBalance()); + emit numTransactionsChanged(getNumTransactions()); + + addressTableModel->update(); +} + +WalletModel::StatusCode WalletModel::sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs) +{ + uint160 hash160 = 0; + bool valid = false; + + if(!AddressToHash160(payTo.toUtf8().constData(), hash160)) + { + return InvalidAddress; + } + + if(payAmount <= 0) + { + return InvalidAmount; + } + + if(payAmount > getBalance()) + { + return AmountExceedsBalance; + } + + if((payAmount + nTransactionFee) > getBalance()) + { + return AmountWithFeeExceedsBalance; + } + + CRITICAL_BLOCK(cs_main) + { + // Send to bitcoin address + CWalletTx wtx; + CScript scriptPubKey; + scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; + + std::string strError = wallet->SendMoney(scriptPubKey, payAmount, wtx, true); + if (strError == "") + { + // OK + } + else if (strError == "ABORTED") + { + return Aborted; + } + else + { + emit error(tr("Sending..."), QString::fromStdString(strError)); + return MiscError; + } + } + + // Add addresses that we've sent to to the address book + std::string strAddress = payTo.toStdString(); + CRITICAL_BLOCK(wallet->cs_mapAddressBook) + { + if (!wallet->mapAddressBook.count(strAddress)) + wallet->SetAddressBookName(strAddress, addToAddressBookAs.toStdString()); + } + + return OK; +} + +OptionsModel *WalletModel::getOptionsModel() +{ + return optionsModel; +} + +AddressTableModel *WalletModel::getAddressTableModel() +{ + return addressTableModel; +} + +TransactionTableModel *WalletModel::getTransactionTableModel() +{ + return transactionTableModel; +} diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h new file mode 100644 index 0000000000..5b46dfb69d --- /dev/null +++ b/src/qt/walletmodel.h @@ -0,0 +1,62 @@ +#ifndef WALLETMODEL_H +#define WALLETMODEL_H + +#include + +class OptionsModel; +class AddressTableModel; +class TransactionTableModel; +class CWallet; + +// Interface to a Bitcoin wallet +class WalletModel : public QObject +{ + Q_OBJECT +public: + explicit WalletModel(CWallet *wallet, QObject *parent = 0); + + enum StatusCode + { + OK, + InvalidAmount, + InvalidAddress, + AmountExceedsBalance, + AmountWithFeeExceedsBalance, + Aborted, + MiscError + }; + + OptionsModel *getOptionsModel(); + AddressTableModel *getAddressTableModel(); + TransactionTableModel *getTransactionTableModel(); + + qint64 getBalance() const; + int getNumTransactions() const; + + /* Send coins */ + StatusCode sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs=QString()); +private: + CWallet *wallet; + + // Wallet has an options model for wallet-specific options + // (transaction fee, for example) + OptionsModel *optionsModel; + + AddressTableModel *addressTableModel; + TransactionTableModel *transactionTableModel; + +signals: + void balanceChanged(qint64 balance); + void numTransactionsChanged(int count); + + // Asynchronous error notification + void error(const QString &title, const QString &message); + +public slots: + +private slots: + void update(); +}; + + +#endif // WALLETMODEL_H -- cgit v1.2.3 From d56c6f312c7784e30794ff71eb0b0ec94cdf15ef Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 30 Jun 2011 19:14:42 +0200 Subject: Make it very clear when on testnet (green icon, add [testnet] to title) --- src/qt/bitcoin.qrc | 2 ++ src/qt/bitcoingui.cpp | 12 ++++++++++++ src/qt/clientmodel.cpp | 5 +++++ src/qt/clientmodel.h | 2 ++ src/qt/res/icons/bitcoin_testnet.png | Bin 0 -> 28756 bytes src/qt/res/icons/toolbar_testnet.png | Bin 0 -> 1037 bytes 6 files changed, 21 insertions(+) create mode 100644 src/qt/res/icons/bitcoin_testnet.png create mode 100644 src/qt/res/icons/toolbar_testnet.png diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 2e4cbbb5bd..fc35f89532 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -22,6 +22,8 @@ res/icons/editpaste.png res/icons/editcopy.png res/icons/add.png + res/icons/bitcoin_testnet.png + res/icons/toolbar_testnet.png res/images/about.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 2b1990b9cb..4e5eaa0683 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -182,6 +182,17 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) { this->clientModel = clientModel; + if(clientModel->isTestNet()) + { + setWindowTitle(tr("Bitcoin [testnet]")); + setWindowIcon(QIcon(":icons/bitcoin_testnet")); + if(trayIcon) + { + trayIcon->setToolTip(tr("Bitcoin [testnet]")); + trayIcon->setIcon(QIcon(":/icons/toolbar_testnet")); + } + } + // Keep up to date with client setNumConnections(clientModel->getNumConnections()); connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); @@ -229,6 +240,7 @@ void BitcoinGUI::createTrayIcon() trayIcon = new QSystemTrayIcon(this); trayIcon->setContextMenu(trayIconMenu); + trayIcon->setToolTip("Bitcoin client"); trayIcon->setIcon(QIcon(":/icons/toolbar")); connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 125cb91017..30b4fe7235 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -39,6 +39,11 @@ void ClientModel::update() emit numBlocksChanged(getNumBlocks()); } +bool ClientModel::isTestNet() const +{ + return fTestNet; +} + bool ClientModel::inInitialBlockDownload() const { return IsInitialBlockDownload(); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index a5028ff3b8..18b3ba11e1 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -22,6 +22,8 @@ public: int getNumConnections() const; int getNumBlocks() const; + // Return true if client connected to testnet + bool isTestNet() const; // Return true if core is doing initial block download bool inInitialBlockDownload() const; // Return conservative estimate of total number of blocks, or 0 if unknown diff --git a/src/qt/res/icons/bitcoin_testnet.png b/src/qt/res/icons/bitcoin_testnet.png new file mode 100644 index 0000000000..ee2dc40563 Binary files /dev/null and b/src/qt/res/icons/bitcoin_testnet.png differ diff --git a/src/qt/res/icons/toolbar_testnet.png b/src/qt/res/icons/toolbar_testnet.png new file mode 100644 index 0000000000..90ed6d99f4 Binary files /dev/null and b/src/qt/res/icons/toolbar_testnet.png differ -- cgit v1.2.3 From d61b7d13e4b5ac7bcb864bc9ff8fdb2ee17b0126 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 30 Jun 2011 19:43:44 +0200 Subject: Add "receiving addresses" to toolbar --- src/qt/addresstablemodel.cpp | 4 ++-- src/qt/bitcoingui.cpp | 3 ++- src/qt/res/icons/receive.png | Bin 716 -> 1763 bytes 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index eece092f0d..ecf6f3f3d5 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -134,12 +134,12 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const } return font; } - else if (role == Qt::ForegroundRole) + else if (role == Qt::BackgroundRole) { // Show default address in alternative color if(priv->isDefaultAddress(rec)) { - return QColor(0,0,255); + return QColor(255,255,128); } } else if (role == Qt::ToolTipRole) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 4e5eaa0683..84eb366593 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -72,6 +72,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolbar->addAction(sendcoins); toolbar->addAction(addressbook); + toolbar->addAction(receivingAddresses); // Address:
: New... : Paste to clipboard QHBoxLayout *hbox_address = new QHBoxLayout(); @@ -162,7 +163,7 @@ void BitcoinGUI::createActions() addressbook->setToolTip(tr("Edit the list of stored addresses and labels")); about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); about->setToolTip(tr("Show information about Bitcoin")); - receivingAddresses = new QAction(QIcon(":/icons/receiving_addresses"), tr("Your &Receiving Addresses..."), this); + receivingAddresses = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receiving Addresses..."), this); receivingAddresses->setToolTip(tr("Show the list of receiving addresses and edit their labels")); options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); options->setToolTip(tr("Modify configuration options for bitcoin")); diff --git a/src/qt/res/icons/receive.png b/src/qt/res/icons/receive.png index ea9eac679f..e59a2cdc92 100644 Binary files a/src/qt/res/icons/receive.png and b/src/qt/res/icons/receive.png differ -- cgit v1.2.3 From c16eb949450a573baf4c5f868f58d870b3b34ab2 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 30 Jun 2011 20:20:46 +0200 Subject: add icons to address book dialog buttons --- doc/assets-attribution.txt | 3 ++- src/qt/bitcoin.qrc | 4 +++- src/qt/forms/addressbookdialog.ui | 22 ++++++++++++++++++++-- src/qt/res/icons/edit.png | Bin 0 -> 1627 bytes src/qt/res/icons/editdelete.png | Bin 0 -> 1368 bytes 5 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 src/qt/res/icons/edit.png create mode 100644 src/qt/res/icons/editdelete.png diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index bd1f599c82..f34c2c91b1 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -28,7 +28,8 @@ License: You are free to do with these icons as you wish, including selling, Icon: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png, src/qt/res/icons/editcopy.png, src/qt/res/icons/editpaste.png, - src/qt/res/icons/add.png + src/qt/res/icons/add.png, src/qt/res/icons/edit.png, + src/qt/res/icons/editdelete.png Designer: http://www.everaldo.com Icon Pack: Crystal SVG License: LGPL diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index fc35f89532..2dfc7dd987 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -1,5 +1,5 @@ - + res/icons/address-book.png res/icons/bitcoin.png res/icons/quit.png @@ -24,6 +24,8 @@ res/icons/add.png res/icons/bitcoin_testnet.png res/icons/toolbar_testnet.png + res/icons/edit.png + res/icons/editdelete.png res/images/about.png diff --git a/src/qt/forms/addressbookdialog.ui b/src/qt/forms/addressbookdialog.ui index 0427789687..40ade40a39 100644 --- a/src/qt/forms/addressbookdialog.ui +++ b/src/qt/forms/addressbookdialog.ui @@ -6,7 +6,7 @@ 0 0 - 591 + 620 347 @@ -115,6 +115,10 @@ &New Address... + + + :/icons/add:/icons/add + @@ -125,6 +129,10 @@ &Copy to Clipboard + + + :/icons/editcopy:/icons/editcopy + @@ -135,6 +143,10 @@ &Edit... + + + :/icons/edit:/icons/edit + @@ -145,6 +157,10 @@ &Delete + + + :/icons/editdelete:/icons/editdelete + @@ -164,6 +180,8 @@ - + + + diff --git a/src/qt/res/icons/edit.png b/src/qt/res/icons/edit.png new file mode 100644 index 0000000000..1d69145151 Binary files /dev/null and b/src/qt/res/icons/edit.png differ diff --git a/src/qt/res/icons/editdelete.png b/src/qt/res/icons/editdelete.png new file mode 100644 index 0000000000..945d221eea Binary files /dev/null and b/src/qt/res/icons/editdelete.png differ -- cgit v1.2.3 From 8b040f812af19aff3d3f408a604c17851ac3bf51 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 30 Jun 2011 21:11:51 +0200 Subject: fix sorting in address table dialog --- src/qt/addressbookdialog.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/qt/addressbookdialog.cpp b/src/qt/addressbookdialog.cpp index 4f99a3c7ea..ce9c6a5338 100644 --- a/src/qt/addressbookdialog.cpp +++ b/src/qt/addressbookdialog.cpp @@ -46,6 +46,8 @@ void AddressBookDialog::setModel(AddressTableModel *model) receive_model->setFilterRole(AddressTableModel::TypeRole); receive_model->setFilterFixedString(AddressTableModel::Receive); ui->receiveTableView->setModel(receive_model); + ui->receiveTableView->sortByColumn(0, Qt::AscendingOrder); + // Send filter QSortFilterProxyModel *send_model = new QSortFilterProxyModel(this); @@ -54,6 +56,7 @@ void AddressBookDialog::setModel(AddressTableModel *model) send_model->setFilterRole(AddressTableModel::TypeRole); send_model->setFilterFixedString(AddressTableModel::Send); ui->sendTableView->setModel(send_model); + ui->sendTableView->sortByColumn(0, Qt::AscendingOrder); // Set column widths ui->receiveTableView->horizontalHeader()->resizeSection( -- cgit v1.2.3 From 64f125f3539cf7faf6a1a259181299225072f0fe Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 30 Jun 2011 21:29:20 +0200 Subject: Address book: show unlabeled addresses as (no label) --- src/qt/addresstablemodel.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index ecf6f3f3d5..2550076408 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -114,7 +114,14 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const switch(index.column()) { case Label: - return rec->label; + if(rec->label.isEmpty()) + { + return tr("(no label)"); + } + else + { + return rec->label; + } case Address: return rec->address; case IsDefaultAddress: -- cgit v1.2.3 From c60015a26095ecf2e3a5fb6e04f8c1b6d31acad2 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 30 Jun 2011 21:34:00 +0200 Subject: Fix detailed transaction information on doubleclick --- src/qt/transactionview.cpp | 2 ++ src/qt/transactionview.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index bf7d55d7db..6b314f4f14 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -84,6 +84,8 @@ TransactionView::TransactionView(QWidget *parent) : connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); connect(addressWidget, SIGNAL(textChanged(const QString&)), this, SLOT(changedPrefix(const QString&))); connect(amountWidget, SIGNAL(textChanged(const QString&)), this, SLOT(changedAmount(const QString&))); + + connect(view, SIGNAL(doubleClicked(const QModelIndex&)), this, SIGNAL(doubleClicked(const QModelIndex&))); } void TransactionView::setModel(TransactionTableModel *model) diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index e75dcc2593..96cdf9779c 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -10,6 +10,7 @@ QT_BEGIN_NAMESPACE class QTableView; class QComboBox; class QLineEdit; +class QModelIndex; QT_END_NAMESPACE class TransactionView : public QWidget @@ -41,6 +42,7 @@ private: QLineEdit *amountWidget; signals: + void doubleClicked(const QModelIndex&); public slots: void chooseDate(int idx); -- cgit v1.2.3 From 0052fe7bbc2a4c244786e3a496263c045fb185c5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 1 Jul 2011 17:06:36 +0200 Subject: General cleanups --- src/qt/aboutdialog.cpp | 10 +++++++--- src/qt/aboutdialog.h | 2 ++ src/qt/bitcoinaddressvalidator.cpp | 2 -- src/qt/bitcoingui.cpp | 9 ++++----- src/qt/clientmodel.cpp | 4 ++++ src/qt/clientmodel.h | 2 ++ 6 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/qt/aboutdialog.cpp b/src/qt/aboutdialog.cpp index 13347961dd..13d263b75c 100644 --- a/src/qt/aboutdialog.cpp +++ b/src/qt/aboutdialog.cpp @@ -1,14 +1,18 @@ #include "aboutdialog.h" #include "ui_aboutdialog.h" - -#include "util.h" +#include "clientmodel.h" AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDialog) { ui->setupUi(this); - ui->versionLabel->setText(QString::fromStdString(FormatFullVersion())); + +} + +void AboutDialog::setModel(ClientModel *model) +{ + ui->versionLabel->setText(model->formatFullVersion()); } AboutDialog::~AboutDialog() diff --git a/src/qt/aboutdialog.h b/src/qt/aboutdialog.h index 827cc741c3..d2caa3eedf 100644 --- a/src/qt/aboutdialog.h +++ b/src/qt/aboutdialog.h @@ -6,6 +6,7 @@ namespace Ui { class AboutDialog; } +class ClientModel; class AboutDialog : public QDialog { @@ -15,6 +16,7 @@ public: explicit AboutDialog(QWidget *parent = 0); ~AboutDialog(); + void setModel(ClientModel *model); private: Ui::AboutDialog *ui; diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index 92d7daeb88..4308a893c2 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -1,7 +1,5 @@ #include "bitcoinaddressvalidator.h" -#include - /* Base58 characters are: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 84eb366593..bdedfc6923 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -18,8 +18,6 @@ #include "addresstablemodel.h" #include "transactionview.h" -#include "headers.h" - #include #include #include @@ -290,6 +288,7 @@ void BitcoinGUI::optionsClicked() void BitcoinGUI::aboutClicked() { AboutDialog dlg; + dlg.setModel(clientModel); dlg.exec(); } @@ -311,7 +310,7 @@ void BitcoinGUI::copyClipboardClicked() void BitcoinGUI::setBalance(qint64 balance) { - labelBalance->setText(QString::fromStdString(FormatMoney(balance)) + QString(" BTC")); + labelBalance->setText(GUIUtil::formatMoney(balance) + QString(" BTC")); } void BitcoinGUI::setAddress(const QString &addr) @@ -410,7 +409,7 @@ void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) QString strMessage = tr("This transaction is over the size limit. You can still send it for a fee of %1, " "which goes to the nodes that process your transaction and helps to support the network. " - "Do you want to pay the fee?").arg(QString::fromStdString(FormatMoney(nFeeRequired))); + "Do you want to pay the fee?").arg(GUIUtil::formatMoney(nFeeRequired)); QMessageBox::StandardButton retval = QMessageBox::question( this, tr("Sending..."), strMessage, QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); @@ -442,7 +441,7 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int trayIcon->showMessage(tr("Incoming transaction"), tr("Date: ") + date + "\n" + - tr("Amount: ") + QString::fromStdString(FormatMoney(amount, true)) + "\n" + + tr("Amount: ") + GUIUtil::formatMoney(amount, true) + "\n" + tr("Type: ") + type + "\n" + tr("Address: ") + address + "\n", QSystemTrayIcon::Information); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 30b4fe7235..06ad5adfc8 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -59,3 +59,7 @@ OptionsModel *ClientModel::getOptionsModel() return optionsModel; } +QString ClientModel::formatFullVersion() const +{ + return QString::fromStdString(FormatFullVersion()); +} diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 18b3ba11e1..659fa65762 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -29,6 +29,8 @@ public: // Return conservative estimate of total number of blocks, or 0 if unknown int getTotalBlocksEstimate() const; + QString formatFullVersion() const; + private: CWallet *wallet; -- cgit v1.2.3 From ab90d6e62a8ca17c3c8f617ead57b17e7ba7ee94 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 1 Jul 2011 17:26:57 +0200 Subject: reverse address and label (suggestion by Danube) --- src/qt/transactiontablemodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 28d22b8530..b2fe167bfe 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -332,7 +332,7 @@ QString TransactionTableModel::lookupAddress(const std::string &address) const } else { - description = QString::fromStdString(address.substr(0,12)) + QString("... (") + label + QString(")"); + description = label + QString(" (") + QString::fromStdString(address.substr(0,12)) + QString("...)"); } return description; } -- cgit v1.2.3 From cdff41c12ec83cebf0ce12c4715c9c8c54549057 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 1 Jul 2011 18:31:10 +0200 Subject: cleanup unused constants --- src/qt/transactiontablemodel.cpp | 4 ---- src/qt/transactiontablemodel.h | 5 ----- 2 files changed, 9 deletions(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index b2fe167bfe..5b11b3311b 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -15,10 +15,6 @@ #include #include -const QString TransactionTableModel::Sent = "s"; -const QString TransactionTableModel::Received = "r"; -const QString TransactionTableModel::Other = "o"; - // Credit and Debit columns are right-aligned as they contain numbers static int column_alignments[] = { Qt::AlignLeft|Qt::AlignVCenter, diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index c26acbc101..835b387575 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -39,11 +39,6 @@ public: AbsoluteAmountRole } RoleIndex; - /* TypeRole values */ - static const QString Sent; - static const QString Received; - static const QString Other; - int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; -- cgit v1.2.3 From b5384e93edff8218695c190a596cfb7a62dcb08f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 1 Jul 2011 18:55:13 +0200 Subject: make amount column wider, so that more decimals fit in --- src/qt/transactionview.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 6b314f4f14..84de157110 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -61,8 +61,8 @@ TransactionView::TransactionView(QWidget *parent) : amountWidget = new QLineEdit(this); amountWidget->setPlaceholderText("Min amount"); - amountWidget->setMaximumWidth(79); - amountWidget->setMinimumWidth(79); + amountWidget->setMaximumWidth(100); + amountWidget->setMinimumWidth(100); amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this)); hlayout->addWidget(amountWidget); @@ -115,7 +115,7 @@ void TransactionView::setModel(TransactionTableModel *model) transactionView->horizontalHeader()->setResizeMode( TransactionTableModel::ToAddress, QHeaderView::Stretch); transactionView->horizontalHeader()->resizeSection( - TransactionTableModel::Amount, 79); + TransactionTableModel::Amount, 100); } -- cgit v1.2.3 From 05bae43c3ccad4959b8cc49db26449d409289118 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 1 Jul 2011 20:28:11 +0200 Subject: Add "last month" filter --- src/qt/transactionview.cpp | 8 +++++++- src/qt/transactionview.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 84de157110..8763e10e7e 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -36,6 +36,7 @@ TransactionView::TransactionView(QWidget *parent) : dateWidget->addItem(tr("Today"), Today); dateWidget->addItem(tr("This week"), ThisWeek); dateWidget->addItem(tr("This month"), ThisMonth); + dateWidget->addItem(tr("Last month"), LastMonth); dateWidget->addItem(tr("This year"), ThisYear); dateWidget->addItem(tr("Range..."), Range); hlayout->addWidget(dateWidget); @@ -147,6 +148,11 @@ void TransactionView::chooseDate(int idx) QDateTime(QDate(current.year(), current.month(), 1)), TransactionFilterProxy::MAX_DATE); break; + case LastMonth: + transactionProxyModel->setDateRange( + QDateTime(QDate(current.year(), current.month()-1, 1)), + QDateTime(QDate(current.year(), current.month(), 1))); + break; case ThisYear: transactionProxyModel->setDateRange( QDateTime(QDate(current.year(), 1, 1)), @@ -172,7 +178,7 @@ void TransactionView::changedPrefix(const QString &prefix) void TransactionView::changedAmount(const QString &amount) { - qint64 amount_parsed; + qint64 amount_parsed = 0; if(GUIUtil::parseMoney(amount, &amount_parsed)) { transactionProxyModel->setMinAmount(amount_parsed); diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 96cdf9779c..fe0f154b8e 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -27,6 +27,7 @@ public: Today, ThisWeek, ThisMonth, + LastMonth, ThisYear, Range }; -- cgit v1.2.3 From e5b47b4328ce3de8e657430bf38c8051d59df298 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 1 Jul 2011 21:41:14 +0200 Subject: Remove "default address" from main GUI screen, it only confuses people --- src/qt/bitcoingui.cpp | 49 +-------------------------------------- src/qt/bitcoingui.h | 4 ---- src/qt/forms/addressbookdialog.ui | 6 ++--- 3 files changed, 4 insertions(+), 55 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bdedfc6923..eeca18eff3 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -72,24 +72,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): toolbar->addAction(addressbook); toolbar->addAction(receivingAddresses); - // Address:
: New... : Paste to clipboard - QHBoxLayout *hbox_address = new QHBoxLayout(); - hbox_address->addWidget(new QLabel(tr("Your Bitcoin address:"))); - address = new QLineEdit(); - address->setReadOnly(true); - address->setFont(GUIUtil::bitcoinAddressFont()); - address->setToolTip(tr("Your current default receiving address")); - hbox_address->addWidget(address); - - QPushButton *button_new = new QPushButton(tr("&New address...")); - button_new->setToolTip(tr("Create new receiving address")); - button_new->setIcon(QIcon(":/icons/add")); - QPushButton *button_clipboard = new QPushButton(tr("&Copy to clipboard")); - button_clipboard->setToolTip(tr("Copy current receiving address to the system clipboard")); - button_clipboard->setIcon(QIcon(":/icons/editcopy")); - hbox_address->addWidget(button_new); - hbox_address->addWidget(button_clipboard); - // Balance: QHBoxLayout *hbox_balance = new QHBoxLayout(); hbox_balance->addWidget(new QLabel(tr("Balance:"))); @@ -102,7 +84,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): hbox_balance->addStretch(1); QVBoxLayout *vbox = new QVBoxLayout(); - vbox->addLayout(hbox_address); vbox->addLayout(hbox_balance); transactionView = new TransactionView(this); @@ -144,10 +125,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): statusBar()->addPermanentWidget(labelBlocks); statusBar()->addPermanentWidget(labelTransactions); - // Action bindings - connect(button_new, SIGNAL(clicked()), this, SLOT(newAddressClicked())); - connect(button_clipboard, SIGNAL(clicked()), this, SLOT(copyClipboardClicked())); - createTrayIcon(); } @@ -162,7 +139,7 @@ void BitcoinGUI::createActions() about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); about->setToolTip(tr("Show information about Bitcoin")); receivingAddresses = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receiving Addresses..."), this); - receivingAddresses->setToolTip(tr("Show the list of receiving addresses and edit their labels")); + receivingAddresses->setToolTip(tr("Show the list of addresses for receiving payments")); options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); options->setToolTip(tr("Modify configuration options for bitcoin")); openBitcoin = new QAction(QIcon(":/icons/bitcoin"), "Open &Bitcoin", this); @@ -214,9 +191,6 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) setNumTransactions(walletModel->getNumTransactions()); connect(walletModel, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); - setAddress(walletModel->getAddressTableModel()->getDefaultAddress()); - connect(walletModel->getAddressTableModel(), SIGNAL(defaultAddressChanged(QString)), this, SLOT(setAddress(QString))); - // Report errors from wallet thread connect(walletModel, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); @@ -292,32 +266,11 @@ void BitcoinGUI::aboutClicked() dlg.exec(); } -void BitcoinGUI::newAddressClicked() -{ - EditAddressDialog dlg(EditAddressDialog::NewReceivingAddress); - dlg.setModel(walletModel->getAddressTableModel()); - if(dlg.exec()) - { - QString newAddress = dlg.saveCurrentRow(); - } -} - -void BitcoinGUI::copyClipboardClicked() -{ - // Copy text in address to clipboard - QApplication::clipboard()->setText(address->text()); -} - void BitcoinGUI::setBalance(qint64 balance) { labelBalance->setText(GUIUtil::formatMoney(balance) + QString(" BTC")); } -void BitcoinGUI::setAddress(const QString &addr) -{ - address->setText(addr); -} - void BitcoinGUI::setNumConnections(int count) { QString icon; diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 41b665c274..954953727e 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -42,7 +42,6 @@ private: ClientModel *clientModel; WalletModel *walletModel; - QLineEdit *address; QLabel *labelBalance; QLabel *labelConnections; QLabel *labelConnectionsIcon; @@ -68,7 +67,6 @@ private: public slots: void setBalance(qint64 balance); - void setAddress(const QString &address); void setNumConnections(int count); void setNumBlocks(int count); void setNumTransactions(int count); @@ -85,8 +83,6 @@ private slots: void optionsClicked(); void receivingAddressesClicked(); void aboutClicked(); - void newAddressClicked(); - void copyClipboardClicked(); void trayIconActivated(QSystemTrayIcon::ActivationReason reason); void transactionDetails(const QModelIndex& idx); void incomingTransaction(const QModelIndex & parent, int start, int end); diff --git a/src/qt/forms/addressbookdialog.ui b/src/qt/forms/addressbookdialog.ui index 40ade40a39..d99651d6ab 100644 --- a/src/qt/forms/addressbookdialog.ui +++ b/src/qt/forms/addressbookdialog.ui @@ -6,7 +6,7 @@ 0 0 - 620 + 627 347 @@ -17,7 +17,7 @@ - 0 + 1 @@ -59,7 +59,7 @@ - These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. The highlighted address is displayed in the main window. + These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. The highlighted address is your default receiving address. Qt::AutoText -- cgit v1.2.3 From f48b4c88978e11916d0cb134f9ff89a25383d339 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 2 Jul 2011 09:21:16 +0200 Subject: Placeholder text can only be used for Qt 4.7+ --- src/qt/transactionview.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 8763e10e7e..7bbe55001d 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -57,11 +57,15 @@ TransactionView::TransactionView(QWidget *parent) : hlayout->addWidget(typeWidget); addressWidget = new QLineEdit(this); +#if QT_VERSION >= 0x040700 addressWidget->setPlaceholderText("Enter address or label to search"); +#endif hlayout->addWidget(addressWidget); amountWidget = new QLineEdit(this); +#if QT_VERSION >= 0x040700 amountWidget->setPlaceholderText("Min amount"); +#endif amountWidget->setMaximumWidth(100); amountWidget->setMinimumWidth(100); amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this)); -- cgit v1.2.3 From bb82fdb543b90cee0026784ed32d705d468aacbb Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 2 Jul 2011 10:13:29 +0200 Subject: "Receive coins" instead of "Receiving addresses" --- src/qt/bitcoingui.cpp | 28 +++++++++++++--------------- src/qt/bitcoingui.h | 8 ++++---- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index eeca18eff3..f6b87c5459 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -25,14 +25,12 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #include @@ -54,12 +52,12 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Menus QMenu *file = menuBar()->addMenu("&File"); - file->addAction(sendcoins); + file->addAction(sendCoins); + file->addAction(receiveCoins); file->addSeparator(); file->addAction(quit); QMenu *settings = menuBar()->addMenu("&Settings"); - settings->addAction(receivingAddresses); settings->addAction(options); QMenu *help = menuBar()->addMenu("&Help"); @@ -68,9 +66,9 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Toolbar QToolBar *toolbar = addToolBar("Main toolbar"); toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - toolbar->addAction(sendcoins); + toolbar->addAction(sendCoins); + toolbar->addAction(receiveCoins); toolbar->addAction(addressbook); - toolbar->addAction(receivingAddresses); // Balance: QHBoxLayout *hbox_balance = new QHBoxLayout(); @@ -132,23 +130,23 @@ void BitcoinGUI::createActions() { quit = new QAction(QIcon(":/icons/quit"), tr("&Exit"), this); quit->setToolTip(tr("Quit application")); - sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); - sendcoins->setToolTip(tr("Send coins to a bitcoin address")); + sendCoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); + sendCoins->setToolTip(tr("Send coins to a bitcoin address")); addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); addressbook->setToolTip(tr("Edit the list of stored addresses and labels")); about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); about->setToolTip(tr("Show information about Bitcoin")); - receivingAddresses = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receiving Addresses..."), this); - receivingAddresses->setToolTip(tr("Show the list of addresses for receiving payments")); + receiveCoins = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive coins"), this); + receiveCoins->setToolTip(tr("Show the list of addresses for receiving payments")); options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); options->setToolTip(tr("Modify configuration options for bitcoin")); openBitcoin = new QAction(QIcon(":/icons/bitcoin"), "Open &Bitcoin", this); openBitcoin->setToolTip(tr("Show the Bitcoin window")); connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); - connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked())); + connect(sendCoins, SIGNAL(triggered()), this, SLOT(sendCoinsClicked())); connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked())); - connect(receivingAddresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked())); + connect(receiveCoins, SIGNAL(triggered()), this, SLOT(receiveCoinsClicked())); connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); connect(openBitcoin, SIGNAL(triggered()), this, SLOT(show())); @@ -206,7 +204,7 @@ void BitcoinGUI::createTrayIcon() { QMenu *trayIconMenu = new QMenu(this); trayIconMenu->addAction(openBitcoin); - trayIconMenu->addAction(sendcoins); + trayIconMenu->addAction(sendCoins); trayIconMenu->addAction(options); trayIconMenu->addSeparator(); trayIconMenu->addAction(quit); @@ -229,7 +227,7 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) } } -void BitcoinGUI::sendcoinsClicked() +void BitcoinGUI::sendCoinsClicked() { SendCoinsDialog dlg; dlg.setModel(walletModel); @@ -244,7 +242,7 @@ void BitcoinGUI::addressbookClicked() dlg.exec(); } -void BitcoinGUI::receivingAddressesClicked() +void BitcoinGUI::receiveCoinsClicked() { AddressBookDialog dlg(AddressBookDialog::ForEditing); dlg.setModel(walletModel->getAddressTableModel()); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 954953727e..046186c562 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -51,10 +51,10 @@ private: QProgressBar *progressBar; QAction *quit; - QAction *sendcoins; + QAction *sendCoins; QAction *addressbook; QAction *about; - QAction *receivingAddresses; + QAction *receiveCoins; QAction *options; QAction *openBitcoin; @@ -78,10 +78,10 @@ public slots: void askFee(qint64 nFeeRequired, bool *payFee); private slots: - void sendcoinsClicked(); + void sendCoinsClicked(); void addressbookClicked(); void optionsClicked(); - void receivingAddressesClicked(); + void receiveCoinsClicked(); void aboutClicked(); void trayIconActivated(QSystemTrayIcon::ActivationReason reason); void transactionDetails(const QModelIndex& idx); -- cgit v1.2.3 From 05da981f05d7b2e1551345a042d3379e9244f09b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 2 Jul 2011 10:33:36 +0200 Subject: update build instructions (thanks tschaboo) --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9d7ad10f18..3119914520 100644 --- a/README.rst +++ b/README.rst @@ -47,7 +47,9 @@ distribution are installed, for Debian and Ubuntu these are: :: - apt-get install qt4-qmake libqt4-dev + apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \ + libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev libboost-all-dev \ + libssl-dev libdb4.8++-dev then execute the following: -- cgit v1.2.3 From ebff5c40a234f38429965c391da020bbf8312b1b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 2 Jul 2011 13:45:59 +0200 Subject: Send: dialog redesign (automatically look up label for entered address) --- src/qt/addresstablemodel.cpp | 50 ++------------------------------------- src/qt/addresstablemodel.h | 9 ++----- src/qt/editaddressdialog.cpp | 9 ++----- src/qt/forms/addressbookdialog.ui | 2 +- src/qt/forms/editaddressdialog.ui | 25 +++++++------------- src/qt/forms/sendcoinsdialog.ui | 31 ++++++++++++++---------- src/qt/sendcoinsdialog.cpp | 12 ++++------ src/qt/sendcoinsdialog.h | 2 +- src/qt/transactiontablemodel.cpp | 24 ++++--------------- src/qt/transactiontablemodel.h | 5 ++-- src/qt/walletmodel.cpp | 16 +++++++++++++ src/qt/walletmodel.h | 4 ++++ 12 files changed, 68 insertions(+), 121 deletions(-) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 2550076408..ca605241ed 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -70,11 +70,6 @@ struct AddressTablePriv return 0; } } - - bool isDefaultAddress(const AddressTableEntry *rec) - { - return rec->address == QString::fromStdString(wallet->GetDefaultAddress()); - } }; AddressTableModel::AddressTableModel(CWallet *wallet, QObject *parent) : @@ -124,8 +119,6 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const } case Address: return rec->address; - case IsDefaultAddress: - return priv->isDefaultAddress(rec); } } else if (role == Qt::FontRole) @@ -135,27 +128,8 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const { font = GUIUtil::bitcoinAddressFont(); } - if(priv->isDefaultAddress(rec)) - { - font.setBold(true); - } return font; } - else if (role == Qt::BackgroundRole) - { - // Show default address in alternative color - if(priv->isDefaultAddress(rec)) - { - return QColor(255,255,128); - } - } - else if (role == Qt::ToolTipRole) - { - if(priv->isDefaultAddress(rec)) - { - return tr("Default receiving address"); - } - } else if (role == TypeRole) { switch(rec->type) @@ -196,12 +170,6 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu rec->address = value.toString(); } break; - case IsDefaultAddress: - if(value.toBool()) - { - setDefaultAddress(rec->address); - } - break; } emit dataChanged(index, index); @@ -244,7 +212,7 @@ void AddressTableModel::updateList() endResetModel(); } -QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address, bool setAsDefault) +QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address) { std::string strLabel = label.toStdString(); std::string strAddress = address.toStdString(); @@ -265,10 +233,6 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con // Generate a new address to associate with given label, optionally // set as default receiving address. strAddress = PubKeyToAddress(wallet->GetKeyFromKeyPool()); - if(setAsDefault) - { - setDefaultAddress(QString::fromStdString(strAddress)); - } } else { @@ -295,17 +259,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex & paren return true; } -QString AddressTableModel::getDefaultAddress() const -{ - return QString::fromStdString(wallet->GetDefaultAddress()); -} - -void AddressTableModel::setDefaultAddress(const QString &defaultAddress) -{ - wallet->SetDefaultAddress(defaultAddress.toStdString()); -} - void AddressTableModel::update() { - emit defaultAddressChanged(getDefaultAddress()); + } diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index d8465853b2..3ababfc6b5 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -16,8 +16,7 @@ public: enum ColumnIndex { Label = 0, /* User specified label */ - Address = 1, /* Bitcoin address */ - IsDefaultAddress = 2 /* Is default address? */ + Address = 1 /* Bitcoin address */ }; enum { @@ -39,11 +38,7 @@ public: /* Add an address to the model. Returns the added address on success, and an empty string otherwise. */ - QString addRow(const QString &type, const QString &label, const QString &address, bool setAsDefault); - - /* Set and get default address */ - QString getDefaultAddress() const; - void setDefaultAddress(const QString &defaultAddress); + QString addRow(const QString &type, const QString &label, const QString &address); /* Update address list from core. Invalidates any indices. */ diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 58ecb49486..8ffabf4798 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -19,19 +19,16 @@ EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : case NewReceivingAddress: setWindowTitle(tr("New receiving address")); ui->addressEdit->setEnabled(false); - ui->setAsDefault->setChecked(true); break; case NewSendingAddress: setWindowTitle(tr("New sending address")); - ui->setAsDefault->setVisible(false); break; case EditReceivingAddress: setWindowTitle(tr("Edit receiving address")); - ui->addressEdit->setReadOnly(true); + ui->addressEdit->setDisabled(true); break; case EditSendingAddress: setWindowTitle(tr("Edit sending address")); - ui->setAsDefault->setVisible(false); break; } @@ -50,7 +47,6 @@ void EditAddressDialog::setModel(AddressTableModel *model) mapper->setModel(model); mapper->addMapping(ui->labelEdit, AddressTableModel::Label); mapper->addMapping(ui->addressEdit, AddressTableModel::Address); - mapper->addMapping(ui->setAsDefault, AddressTableModel::IsDefaultAddress); } void EditAddressDialog::loadRow(int row) @@ -68,8 +64,7 @@ QString EditAddressDialog::saveCurrentRow() address = model->addRow( mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive, ui->labelEdit->text(), - ui->addressEdit->text(), - ui->setAsDefault->isChecked()); + ui->addressEdit->text()); if(address.isEmpty()) { QMessageBox::warning(this, windowTitle(), diff --git a/src/qt/forms/addressbookdialog.ui b/src/qt/forms/addressbookdialog.ui index d99651d6ab..9bfcb30f46 100644 --- a/src/qt/forms/addressbookdialog.ui +++ b/src/qt/forms/addressbookdialog.ui @@ -59,7 +59,7 @@ - These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. The highlighted address is your default receiving address. + These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. Qt::AutoText diff --git a/src/qt/forms/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui index 95909fb964..b4a4c1b1e9 100644 --- a/src/qt/forms/editaddressdialog.ui +++ b/src/qt/forms/editaddressdialog.ui @@ -6,8 +6,8 @@ 0 0 - 458 - 114 + 457 + 126 @@ -29,6 +29,13 @@ + + + + The label associated with this address book entry + + + @@ -39,13 +46,6 @@ - - - - The label associated with this address book entry - - - @@ -53,13 +53,6 @@ - - - - Set as default receiving address - - - diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 3641f61eca..59599f2e5b 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -7,7 +7,7 @@ 0 0 660 - 151 + 193 @@ -122,28 +122,34 @@ 0 - - - - Add specified destination address to address book - - - A&dd to address book as - - - - false + true Label to add address as + + Enter a label for this address to add it to your address book + + + + + &Label: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + addAsLabel + + + @@ -221,7 +227,6 @@ payTo - addToAddressBook addAsLabel payAmount addressBookButton diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 5c889b2347..14d50963f2 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -56,11 +56,8 @@ void SendCoinsDialog::on_sendButton_clicked() return; } - if(ui->addToAddressBook->isChecked()) - { - // Add address to address book under label, if specified - label = ui->addAsLabel->text(); - } + // Add address to address book under label, if specified + label = ui->addAsLabel->text(); switch(model->sendCoins(ui->payTo->text(), payAmountParsed, label)) { @@ -108,6 +105,7 @@ void SendCoinsDialog::on_addressBookButton_clicked() dlg.setTab(AddressBookDialog::SendingTab); dlg.exec(); ui->payTo->setText(dlg.getReturnValue()); + ui->payAmount->setFocus(); } void SendCoinsDialog::on_buttonBox_rejected() @@ -115,7 +113,7 @@ void SendCoinsDialog::on_buttonBox_rejected() reject(); } -void SendCoinsDialog::on_addToAddressBook_toggled(bool checked) +void SendCoinsDialog::on_payTo_textChanged(const QString &address) { - ui->addAsLabel->setEnabled(checked); + ui->addAsLabel->setText(model->labelForAddress(address)); } diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index bbb6a5fc4d..968cbe760f 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -23,7 +23,7 @@ private: WalletModel *model; private slots: - void on_addToAddressBook_toggled(bool checked); + void on_payTo_textChanged(const QString &address); void on_buttonBox_rejected(); void on_addressBookButton_clicked(); void on_pasteButton_clicked(); diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 5b11b3311b..0d0d97bbde 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -3,6 +3,7 @@ #include "transactionrecord.h" #include "guiconstants.h" #include "transactiondesc.h" +#include "walletmodel.h" #include "headers.h" @@ -201,9 +202,10 @@ struct TransactionTablePriv }; -TransactionTableModel::TransactionTableModel(CWallet* wallet, QObject *parent): +TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *parent): QAbstractTableModel(parent), wallet(wallet), + walletModel(parent), priv(new TransactionTablePriv(wallet, this)) { columns << tr("Status") << tr("Date") << tr("Type") << tr("Address") << tr("Amount"); @@ -298,29 +300,13 @@ QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const } } -/* Look up label for address in address book, if not found return empty string. - This should really move to the wallet class. - */ -QString TransactionTableModel::labelForAddress(const std::string &address) const -{ - CRITICAL_BLOCK(wallet->cs_mapAddressBook) - { - std::map::iterator mi = wallet->mapAddressBook.find(address); - if (mi != wallet->mapAddressBook.end()) - { - return QString::fromStdString(mi->second); - } - } - return QString(); -} - /* Look up address in address book, if found return address[0:12]... (label) otherwise just return address */ QString TransactionTableModel::lookupAddress(const std::string &address) const { - QString label = labelForAddress(address); + QString label = walletModel->labelForAddress(QString::fromStdString(address)); QString description; if(label.isEmpty()) { @@ -526,7 +512,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const } else if (role == LabelRole) { - return labelForAddress(rec->address); + return walletModel->labelForAddress(QString::fromStdString(rec->address)); } else if (role == AbsoluteAmountRole) { diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 835b387575..f75f414d4c 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -7,12 +7,13 @@ class CWallet; class TransactionTablePriv; class TransactionRecord; +class WalletModel; class TransactionTableModel : public QAbstractTableModel { Q_OBJECT public: - explicit TransactionTableModel(CWallet* wallet, QObject *parent = 0); + explicit TransactionTableModel(CWallet* wallet, WalletModel *parent = 0); ~TransactionTableModel(); enum { @@ -47,10 +48,10 @@ public: QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; private: CWallet* wallet; + WalletModel *walletModel; QStringList columns; TransactionTablePriv *priv; - QString labelForAddress(const std::string &address) const; QString lookupAddress(const std::string &address) const; QVariant formatTxStatus(const TransactionRecord *wtx) const; QVariant formatTxDate(const TransactionRecord *wtx) const; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 0fb7d21087..f962b9a7c7 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -122,3 +122,19 @@ TransactionTableModel *WalletModel::getTransactionTableModel() { return transactionTableModel; } + +/* Look up label for address in address book, if not found return empty string. + */ +QString WalletModel::labelForAddress(const QString &address) const +{ + CRITICAL_BLOCK(wallet->cs_mapAddressBook) + { + std::map::iterator mi = wallet->mapAddressBook.find(address.toStdString()); + if (mi != wallet->mapAddressBook.end()) + { + return QString::fromStdString(mi->second); + } + } + return QString(); +} + diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 5b46dfb69d..9c7d16fce6 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -33,6 +33,10 @@ public: qint64 getBalance() const; int getNumTransactions() const; + /* Look up label for address in address book, if not found return empty string. + */ + QString labelForAddress(const QString &address) const; + /* Send coins */ StatusCode sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs=QString()); private: -- cgit v1.2.3 From 669b0a5835500c41b15501c5b9eb60ba1a2c7735 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 2 Jul 2011 15:09:53 +0200 Subject: Check addresses in address book for validity --- src/qt/addressbookdialog.cpp | 11 ++--------- src/qt/addresstablemodel.cpp | 7 +++++++ src/qt/addresstablemodel.h | 4 ++++ src/qt/editaddressdialog.cpp | 31 +++++++++++++++++++++++++------ src/qt/editaddressdialog.h | 5 ++++- 5 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/qt/addressbookdialog.cpp b/src/qt/addressbookdialog.cpp index ce9c6a5338..5eb60b77fd 100644 --- a/src/qt/addressbookdialog.cpp +++ b/src/qt/addressbookdialog.cpp @@ -48,7 +48,6 @@ void AddressBookDialog::setModel(AddressTableModel *model) ui->receiveTableView->setModel(receive_model); ui->receiveTableView->sortByColumn(0, Qt::AscendingOrder); - // Send filter QSortFilterProxyModel *send_model = new QSortFilterProxyModel(this); send_model->setSourceModel(model); @@ -120,10 +119,7 @@ void AddressBookDialog::on_editButton_clicked() EditAddressDialog::EditReceivingAddress); dlg.setModel(model); dlg.loadRow(selected.row()); - if(dlg.exec()) - { - dlg.saveCurrentRow(); - } + dlg.exec(); } void AddressBookDialog::on_newAddressButton_clicked() @@ -133,10 +129,7 @@ void AddressBookDialog::on_newAddressButton_clicked() EditAddressDialog::NewSendingAddress : EditAddressDialog::NewReceivingAddress); dlg.setModel(model); - if(dlg.exec()) - { - dlg.saveCurrentRow(); - } + dlg.exec(); } void AddressBookDialog::on_tabWidget_currentChanged(int index) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index ca605241ed..6829fea6e9 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -263,3 +263,10 @@ void AddressTableModel::update() { } + +bool AddressTableModel::validateAddress(const QString &address) +{ + uint160 hash160 = 0; + + return AddressToHash160(address.toStdString(), hash160); +} diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 3ababfc6b5..b509481e52 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -44,6 +44,10 @@ public: */ void updateList(); + /* Check address for validity + */ + bool validateAddress(const QString &address); + private: CWallet *wallet; AddressTablePriv *priv; diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 8ffabf4798..7ea5638b4b 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -65,12 +65,6 @@ QString EditAddressDialog::saveCurrentRow() mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive, ui->labelEdit->text(), ui->addressEdit->text()); - if(address.isEmpty()) - { - QMessageBox::warning(this, windowTitle(), - tr("The address %1 is already in the address book.").arg(ui->addressEdit->text()), - QMessageBox::Ok, QMessageBox::Ok); - } break; case EditReceivingAddress: case EditSendingAddress: @@ -82,3 +76,28 @@ QString EditAddressDialog::saveCurrentRow() } return address; } + +void EditAddressDialog::accept() +{ + if(mode == NewSendingAddress || mode == EditSendingAddress) + { + // For sending addresses, check validity + // Not needed for receiving addresses, as those are generated + if(!model->validateAddress(ui->addressEdit->text())) + { + QMessageBox::warning(this, windowTitle(), + tr("The entered address \"%1\" is not a valid bitcoin address.").arg(ui->addressEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); + return; + } + } + if(saveCurrentRow().isEmpty()) + { + QMessageBox::warning(this, windowTitle(), + tr("The entered address \"%1\" is already in the address book.").arg(ui->addressEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); + return; + } + QDialog::accept(); +} + diff --git a/src/qt/editaddressdialog.h b/src/qt/editaddressdialog.h index 6f396d0457..6219961161 100644 --- a/src/qt/editaddressdialog.h +++ b/src/qt/editaddressdialog.h @@ -29,9 +29,12 @@ public: void setModel(AddressTableModel *model); void loadRow(int row); - QString saveCurrentRow(); + + void accept(); private: + QString saveCurrentRow(); + Ui::EditAddressDialog *ui; QDataWidgetMapper *mapper; Mode mode; -- cgit v1.2.3 From c1ffa5b1c5d79a6c52547ff58292c571c2bc3952 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 2 Jul 2011 15:42:12 +0200 Subject: make tooltip equal to placeholder --- src/qt/forms/sendcoinsdialog.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 59599f2e5b..73a42b638a 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -128,7 +128,7 @@ true - Label to add address as + Enter a label for this address to add it to your address book Enter a label for this address to add it to your address book -- cgit v1.2.3 From 154e25ff60115b9ff286b97ffc87d65736593c86 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 2 Jul 2011 17:31:27 +0200 Subject: ui improvements: allow inline editing of labels/addresses in address book table, better tab order in send dialog, set focus on sending address table when coming from send coins dialog --- src/qt/addressbookdialog.cpp | 15 +++++++++------ src/qt/addressbookdialog.h | 1 + src/qt/addresstablemodel.cpp | 22 +++++++++++++++++++++- src/qt/addresstablemodel.h | 1 + src/qt/forms/sendcoinsdialog.ui | 4 ++-- 5 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/qt/addressbookdialog.cpp b/src/qt/addressbookdialog.cpp index 5eb60b77fd..5a744aec48 100644 --- a/src/qt/addressbookdialog.cpp +++ b/src/qt/addressbookdialog.cpp @@ -11,19 +11,16 @@ AddressBookDialog::AddressBookDialog(Mode mode, QWidget *parent) : QDialog(parent), ui(new Ui::AddressBookDialog), - model(0) + model(0), + mode(mode) { ui->setupUi(this); - switch(mode) { case ForSending: connect(ui->receiveTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_buttonBox_accepted())); connect(ui->sendTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_buttonBox_accepted())); - break; - case ForEditing: - connect(ui->receiveTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_editButton_clicked())); - connect(ui->sendTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_editButton_clicked())); + ui->sendTableView->setFocus(); break; } } @@ -66,6 +63,12 @@ void AddressBookDialog::setModel(AddressTableModel *model) AddressTableModel::Address, 320); ui->sendTableView->horizontalHeader()->setResizeMode( AddressTableModel::Label, QHeaderView::Stretch); + + if(mode == ForSending) + { + // Auto-select first row when in sending mode + ui->sendTableView->selectRow(0); + } } void AddressBookDialog::setTab(int tab) diff --git a/src/qt/addressbookdialog.h b/src/qt/addressbookdialog.h index 25b8839cd6..b032e554d6 100644 --- a/src/qt/addressbookdialog.h +++ b/src/qt/addressbookdialog.h @@ -36,6 +36,7 @@ public: private: Ui::AddressBookDialog *ui; AddressTableModel *model; + Mode mode; QString returnValue; QTableView *getCurrentTable(); diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 6829fea6e9..e375ff8cbc 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -109,7 +109,7 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const switch(index.column()) { case Label: - if(rec->label.isEmpty()) + if(rec->label.isEmpty() && role == Qt::DisplayRole) { return tr("(no label)"); } @@ -159,6 +159,9 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu rec->label = value.toString(); break; case Address: + // Refuse to set invalid address + if(!validateAddress(value.toString())) + return false; // Double-check that we're not overwriting receiving address if(rec->type == AddressTableEntry::Sending) { @@ -190,6 +193,23 @@ QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, return QVariant(); } +Qt::ItemFlags AddressTableModel::flags(const QModelIndex & index) const +{ + if(!index.isValid()) + return 0; + AddressTableEntry *rec = static_cast(index.internalPointer()); + + Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + // Can edit address and label for sending addresses, + // and only label for receiving addresses. + if(rec->type == AddressTableEntry::Sending || + (rec->type == AddressTableEntry::Receiving && index.column()==Label)) + { + retval |= Qt::ItemIsEditable; + } + return retval; +} + QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & parent) const { Q_UNUSED(parent); diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index b509481e52..6f34a60061 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -34,6 +34,7 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role) const; QModelIndex index(int row, int column, const QModelIndex & parent) const; bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex()); + Qt::ItemFlags flags(const QModelIndex & index) const; /* Add an address to the model. Returns the added address on success, and an empty string otherwise. diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 73a42b638a..26de3d2a96 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -227,10 +227,10 @@ payTo - addAsLabel - payAmount addressBookButton pasteButton + addAsLabel + payAmount sendButton buttonBox -- cgit v1.2.3 From ecde936aeecb7d3cd2450aa84f6fff078ed542fd Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 2 Jul 2011 18:30:41 +0200 Subject: remove "edit" button, document double-click behaviour to edit --- src/qt/forms/addressbookdialog.ui | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/qt/forms/addressbookdialog.ui b/src/qt/forms/addressbookdialog.ui index 9bfcb30f46..12ecb13651 100644 --- a/src/qt/forms/addressbookdialog.ui +++ b/src/qt/forms/addressbookdialog.ui @@ -29,6 +29,9 @@ + + Double-click to edit address or label + true @@ -71,6 +74,9 @@ + + Double-click to edit address or label + true @@ -135,20 +141,6 @@ - - - - Edit the currently selected address - - - &Edit... - - - - :/icons/edit:/icons/edit - - - -- cgit v1.2.3 From 21e47f8d0403e2b45fd403f212ace20f3bfd2888 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 3 Jul 2011 07:51:20 +0200 Subject: remove libboostall-dev is not needed dependency --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 3119914520..9c1db1e6d2 100644 --- a/README.rst +++ b/README.rst @@ -48,7 +48,7 @@ distribution are installed, for Debian and Ubuntu these are: :: apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \ - libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev libboost-all-dev \ + libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ libssl-dev libdb4.8++-dev then execute the following: -- cgit v1.2.3 From 482e57812bb5d3b2c608eab7ae3929ab2bec04cc Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 3 Jul 2011 08:24:07 +0200 Subject: move another setPlaceHolderText to 4.7+ only code --- src/qt/forms/sendcoinsdialog.ui | 3 --- src/qt/sendcoinsdialog.cpp | 4 +++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 26de3d2a96..7f843c4518 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -130,9 +130,6 @@ Enter a label for this address to add it to your address book - - Enter a label for this address to add it to your address book - diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 14d50963f2..4b974371fe 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -18,7 +18,9 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : model(0) { ui->setupUi(this); - +#if QT_VERSION >= 0x040700 + ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); +#endif GUIUtil::setupAddressWidget(ui->payTo, this); // Set initial send-to address if provided -- cgit v1.2.3 From 8fe2308b34c444f74fed453133e0ada34ed9dd23 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 3 Jul 2011 20:53:56 +0200 Subject: windows build fixes --- bitcoin-qt.pro | 15 +- src/bitcoinrpc.cpp | 2215 +++++++++++++++++++++++++++++++++++++ src/bitcoinrpc.h | 6 + src/init.cpp | 2 +- src/qt/bitcoin.cpp | 31 +- src/qt/forms/addressbookdialog.ui | 26 +- src/qt/guiutil.cpp | 3 +- src/qt/transactiondesc.cpp | 3 +- src/rpc.cpp | 2215 ------------------------------------- src/rpc.h | 6 - 10 files changed, 2264 insertions(+), 2258 deletions(-) create mode 100644 src/bitcoinrpc.cpp create mode 100644 src/bitcoinrpc.h delete mode 100644 src/rpc.cpp delete mode 100644 src/rpc.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 539c326447..40c0ded6ce 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -1,19 +1,20 @@ TEMPLATE = app TARGET = -DEPENDPATH += . INCLUDEPATH += src src/json src/cryptopp src/qt DEFINES += QT_GUI +# DEFINES += SSL +CONFIG += no_include_pwd # for boost 1.37, add -mt to the boost libraries unix:LIBS += -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 BOOST_FILESYSTEM_VERSION=3 macx:LIBS += -lboost_thread-mt +windows:DEFINES += __WXMSW__ +windows:LIBS += -lssl -lcrypto -lboost_system-mgw44-mt-1_43 -lboost_filesystem-mgw44-mt-1_43 -lboost_program_options-mgw44-mt-1_43 -lboost_thread-mgw44-mt-1_43 -ldb_cxx -lws2_32 -lgdi32 # disable quite some warnings because bitcoin core "sins" a lot QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wno-invalid-offsetof -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare -Wno-char-subscripts -Wno-unused-value -Wno-sequence-point -Wno-parentheses -Wno-unknown-pragmas -Wno-switch -# TODO: WINDOWS defines, -DSSL - # Input DEPENDPATH += src/qt src src/cryptopp src json/include HEADERS += src/qt/bitcoingui.h \ @@ -60,7 +61,6 @@ HEADERS += src/qt/bitcoingui.h \ src/json/json_spirit_reader.h \ src/json/json_spirit_error_position.h \ src/json/json_spirit.h \ - src/rpc.h \ src/qt/clientmodel.h \ src/qt/guiutil.h \ src/qt/transactionrecord.h \ @@ -75,7 +75,8 @@ HEADERS += src/qt/bitcoingui.h \ src/keystore.h \ src/qt/transactionfilterproxy.h \ src/qt/transactionview.h \ - src/qt/walletmodel.h + src/qt/walletmodel.h \ + src/bitcoinrpc.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -91,7 +92,6 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/script.cpp \ src/main.cpp \ src/init.cpp \ - src/rpc.cpp \ src/net.cpp \ src/irc.cpp \ src/db.cpp \ @@ -111,7 +111,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/keystore.cpp \ src/qt/transactionfilterproxy.cpp \ src/qt/transactionview.cpp \ - src/qt/walletmodel.cpp + src/qt/walletmodel.cpp \ + src/bitcoinrpc.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp new file mode 100644 index 0000000000..644ad92297 --- /dev/null +++ b/src/bitcoinrpc.cpp @@ -0,0 +1,2215 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" +#include "cryptopp/sha.h" +#include "db.h" +#include "net.h" +#include "init.h" +#undef printf +#include +#include +#include +#include +#ifdef USE_SSL +#include +#include +#include +typedef boost::asio::ssl::stream SSLStream; +#endif +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_writer_template.h" +#include "json/json_spirit_utils.h" +#define printf OutputDebugStringF +// MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are +// precompiled in headers.h. The problem might be when the pch file goes over +// a certain size around 145MB. If we need access to json_spirit outside this +// file, we could use the compiled json_spirit option. + +using namespace std; +using namespace boost; +using namespace boost::asio; +using namespace json_spirit; + +void ThreadRPCServer2(void* parg); +typedef Value(*rpcfn_type)(const Array& params, bool fHelp); +extern map mapCallTable; + + +Object JSONRPCError(int code, const string& message) +{ + Object error; + error.push_back(Pair("code", code)); + error.push_back(Pair("message", message)); + return error; +} + + +void PrintConsole(const std::string &format, ...) +{ + char buffer[50000]; + int limit = sizeof(buffer); + va_list arg_ptr; + va_start(arg_ptr, format); + int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + ret = limit - 1; + buffer[limit-1] = 0; + } + printf("%s", buffer); +#if defined(__WXMSW__) && defined(GUI) + MyMessageBox(buffer, "Bitcoin", wxOK | wxICON_EXCLAMATION); +#else + fprintf(stdout, "%s", buffer); +#endif +} + + +int64 AmountFromValue(const Value& value) +{ + double dAmount = value.get_real(); + if (dAmount <= 0.0 || dAmount > 21000000.0) + throw JSONRPCError(-3, "Invalid amount"); + int64 nAmount = roundint64(dAmount * COIN); + if (!MoneyRange(nAmount)) + throw JSONRPCError(-3, "Invalid amount"); + return nAmount; +} + +Value ValueFromAmount(int64 amount) +{ + return (double)amount / (double)COIN; +} + +void WalletTxToJSON(const CWalletTx& wtx, Object& entry) +{ + entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain())); + entry.push_back(Pair("txid", wtx.GetHash().GetHex())); + entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); + BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) + entry.push_back(Pair(item.first, item.second)); +} + +string AccountFromValue(const Value& value) +{ + string strAccount = value.get_str(); + if (strAccount == "*") + throw JSONRPCError(-11, "Invalid account name"); + return strAccount; +} + + + +/// +/// Note: This interface may still be subject to change. +/// + + +Value help(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "help [command]\n" + "List commands, or get help for a command."); + + string strCommand; + if (params.size() > 0) + strCommand = params[0].get_str(); + + string strRet; + set setDone; + for (map::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi) + { + string strMethod = (*mi).first; + // We already filter duplicates, but these deprecated screw up the sort order + if (strMethod == "getamountreceived" || + strMethod == "getallreceived" || + (strMethod.find("label") != string::npos)) + continue; + if (strCommand != "" && strMethod != strCommand) + continue; + try + { + Array params; + rpcfn_type pfn = (*mi).second; + if (setDone.insert(pfn).second) + (*pfn)(params, true); + } + catch (std::exception& e) + { + // Help text is returned in an exception + string strHelp = string(e.what()); + if (strCommand == "") + if (strHelp.find('\n') != -1) + strHelp = strHelp.substr(0, strHelp.find('\n')); + strRet += strHelp + "\n"; + } + } + if (strRet == "") + strRet = strprintf("help: unknown command: %s\n", strCommand.c_str()); + strRet = strRet.substr(0,strRet.size()-1); + return strRet; +} + + +Value stop(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "stop\n" + "Stop bitcoin server."); + + // Shutdown will take long enough that the response should get back + CreateThread(Shutdown, NULL); + return "bitcoin server stopping"; +} + + +Value getblockcount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getblockcount\n" + "Returns the number of blocks in the longest block chain."); + + return nBestHeight; +} + + +Value getblocknumber(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getblocknumber\n" + "Returns the block number of the latest block in the longest block chain."); + + return nBestHeight; +} + + +Value getconnectioncount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getconnectioncount\n" + "Returns the number of connections to other nodes."); + + return (int)vNodes.size(); +} + + +double GetDifficulty() +{ + // Floating point number that is a multiple of the minimum difficulty, + // minimum difficulty = 1.0. + + if (pindexBest == NULL) + return 1.0; + int nShift = (pindexBest->nBits >> 24) & 0xff; + + double dDiff = + (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff); + + while (nShift < 29) + { + dDiff *= 256.0; + nShift++; + } + while (nShift > 29) + { + dDiff /= 256.0; + nShift--; + } + + return dDiff; +} + +Value getdifficulty(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getdifficulty\n" + "Returns the proof-of-work difficulty as a multiple of the minimum difficulty."); + + return GetDifficulty(); +} + + +Value getgenerate(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getgenerate\n" + "Returns true or false."); + + return (bool)fGenerateBitcoins; +} + + +Value setgenerate(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "setgenerate [genproclimit]\n" + " is true or false to turn generation on or off.\n" + "Generation is limited to [genproclimit] processors, -1 is unlimited."); + + bool fGenerate = true; + if (params.size() > 0) + fGenerate = params[0].get_bool(); + + if (params.size() > 1) + { + int nGenProcLimit = params[1].get_int(); + fLimitProcessors = (nGenProcLimit != -1); + WriteSetting("fLimitProcessors", fLimitProcessors); + if (nGenProcLimit != -1) + WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit); + if (nGenProcLimit == 0) + fGenerate = false; + } + + GenerateBitcoins(fGenerate, pwalletMain); + return Value::null; +} + + +Value gethashespersec(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "gethashespersec\n" + "Returns a recent hashes per second performance measurement while generating."); + + if (GetTimeMillis() - nHPSTimerStart > 8000) + return (boost::int64_t)0; + return (boost::int64_t)dHashesPerSec; +} + + +Value getinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getinfo\n" + "Returns an object containing various state info."); + + Object obj; + obj.push_back(Pair("version", (int)VERSION)); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("connections", (int)vNodes.size())); + obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string()))); + obj.push_back(Pair("generate", (bool)fGenerateBitcoins)); + obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1))); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("hashespersec", gethashespersec(params, false))); + obj.push_back(Pair("testnet", fTestNet)); + obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + return obj; +} + + +Value getnewaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getnewaddress [account]\n" + "Returns a new bitcoin address for receiving payments. " + "If [account] is specified (recommended), it is added to the address book " + "so payments received with the address will be credited to [account]."); + + // Parse the account first so we don't generate a key if there's an error + string strAccount; + if (params.size() > 0) + strAccount = AccountFromValue(params[0]); + + // Generate a new key that is added to wallet + string strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool()); + + pwalletMain->SetAddressBookName(strAddress, strAccount); + return strAddress; +} + + +// requires cs_main, cs_mapWallet locks +string GetAccountAddress(string strAccount, bool bForceNew=false) +{ + string strAddress; + + CWalletDB walletdb(pwalletMain->strWalletFile); + walletdb.TxnBegin(); + + CAccount account; + walletdb.ReadAccount(strAccount, account); + + // Check if the current key has been used + if (!account.vchPubKey.empty()) + { + CScript scriptPubKey; + scriptPubKey.SetBitcoinAddress(account.vchPubKey); + for (map::iterator it = pwalletMain->mapWallet.begin(); + it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty(); + ++it) + { + const CWalletTx& wtx = (*it).second; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + account.vchPubKey.clear(); + } + } + + // Generate a new key + if (account.vchPubKey.empty() || bForceNew) + { + account.vchPubKey = pwalletMain->GetKeyFromKeyPool(); + string strAddress = PubKeyToAddress(account.vchPubKey); + pwalletMain->SetAddressBookName(strAddress, strAccount); + walletdb.WriteAccount(strAccount, account); + } + + walletdb.TxnCommit(); + strAddress = PubKeyToAddress(account.vchPubKey); + + return strAddress; +} + +Value getaccountaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaccountaddress \n" + "Returns the current bitcoin address for receiving payments to this account."); + + // Parse the account first so we don't generate a key if there's an error + string strAccount = AccountFromValue(params[0]); + + Value ret; + + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + ret = GetAccountAddress(strAccount); + } + + return ret; +} + + + +Value setaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "setaccount \n" + "Sets the account associated with the given address."); + + string strAddress = params[0].get_str(); + uint160 hash160; + bool isValid = AddressToHash160(strAddress, hash160); + if (!isValid) + throw JSONRPCError(-5, "Invalid bitcoin address"); + + + string strAccount; + if (params.size() > 1) + strAccount = AccountFromValue(params[1]); + + // Detect when changing the account of an address that is the 'unused current key' of another account: + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + if (pwalletMain->mapAddressBook.count(strAddress)) + { + string strOldAccount = pwalletMain->mapAddressBook[strAddress]; + if (strAddress == GetAccountAddress(strOldAccount)) + GetAccountAddress(strOldAccount, true); + } + } + + pwalletMain->SetAddressBookName(strAddress, strAccount); + return Value::null; +} + + +Value getaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaccount \n" + "Returns the account associated with the given address."); + + string strAddress = params[0].get_str(); + + string strAccount; + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + map::iterator mi = pwalletMain->mapAddressBook.find(strAddress); + if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty()) + strAccount = (*mi).second; + } + return strAccount; +} + + +Value getaddressesbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaddressesbyaccount \n" + "Returns the list of addresses for the given account."); + + string strAccount = AccountFromValue(params[0]); + + // Find all addresses that have the given account + Array ret; + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) + { + const string& strAddress = item.first; + const string& strName = item.second; + if (strName == strAccount) + { + // We're only adding valid bitcoin addresses and not ip addresses + CScript scriptPubKey; + if (scriptPubKey.SetBitcoinAddress(strAddress)) + ret.push_back(strAddress); + } + } + } + return ret; +} + +Value settxfee(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "settxfee \n" + " is a real and is rounded to the nearest 0.00000001"); + + // Amount + int64 nAmount = 0; + if (params[0].get_real() != 0.0) + nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts + + nTransactionFee = nAmount; + return true; +} + +Value sendtoaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "sendtoaddress [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.00000001"); + + string strAddress = params[0].get_str(); + + // Amount + int64 nAmount = AmountFromValue(params[1]); + + // Wallet comments + CWalletTx wtx; + if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty()) + wtx.mapValue["comment"] = params[2].get_str(); + if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) + wtx.mapValue["to"] = params[3].get_str(); + + CRITICAL_BLOCK(cs_main) + { + string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); + if (strError != "") + throw JSONRPCError(-4, strError); + } + + return wtx.GetHash().GetHex(); +} + + +Value getreceivedbyaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getreceivedbyaddress [minconf=1]\n" + "Returns the total amount received by in transactions with at least [minconf] confirmations."); + + // Bitcoin address + string strAddress = params[0].get_str(); + CScript scriptPubKey; + if (!scriptPubKey.SetBitcoinAddress(strAddress)) + throw JSONRPCError(-5, "Invalid bitcoin address"); + if (!IsMine(*pwalletMain,scriptPubKey)) + return (double)0.0; + + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + // Tally + int64 nAmount = 0; + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + } + + return ValueFromAmount(nAmount); +} + + +void GetAccountPubKeys(string strAccount, set& setPubKey) +{ + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) + { + const string& strAddress = item.first; + const string& strName = item.second; + if (strName == strAccount) + { + // We're only counting our own valid bitcoin addresses and not ip addresses + CScript scriptPubKey; + if (scriptPubKey.SetBitcoinAddress(strAddress)) + if (IsMine(*pwalletMain,scriptPubKey)) + setPubKey.insert(scriptPubKey); + } + } + } +} + + +Value getreceivedbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getreceivedbyaccount [minconf=1]\n" + "Returns the total amount received by addresses with in transactions with at least [minconf] confirmations."); + + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + // Get the set of pub keys that have the label + string strAccount = AccountFromValue(params[0]); + set setPubKey; + GetAccountPubKeys(strAccount, setPubKey); + + // Tally + int64 nAmount = 0; + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (setPubKey.count(txout.scriptPubKey)) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + } + + return (double)nAmount / (double)COIN; +} + + +int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) +{ + int64 nBalance = 0; + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + // Tally wallet transactions + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!wtx.IsFinal()) + continue; + + int64 nGenerated, nReceived, nSent, nFee; + wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee); + + if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) + nBalance += nReceived; + nBalance += nGenerated - nSent - nFee; + } + + // Tally internal accounting entries + nBalance += walletdb.GetAccountCreditDebit(strAccount); + } + + return nBalance; +} + +int64 GetAccountBalance(const string& strAccount, int nMinDepth) +{ + CWalletDB walletdb(pwalletMain->strWalletFile); + return GetAccountBalance(walletdb, strAccount, nMinDepth); +} + + +Value getbalance(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 0 || params.size() > 2) + throw runtime_error( + "getbalance [account] [minconf=1]\n" + "If [account] is not specified, returns the server's total available balance.\n" + "If [account] is specified, returns the balance in the account."); + + if (params.size() == 0) + return ValueFromAmount(pwalletMain->GetBalance()); + + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + if (params[0].get_str() == "*") { + // Calculate total balance a different way from GetBalance() + // (GetBalance() sums up all unspent TxOuts) + // getbalance and getbalance '*' should always return the same number. + int64 nBalance = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!wtx.IsFinal()) + continue; + + int64 allGeneratedImmature, allGeneratedMature, allFee; + allGeneratedImmature = allGeneratedMature = allFee = 0; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); + if (wtx.GetDepthInMainChain() >= nMinDepth) + BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) + nBalance += r.second; + BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listSent) + nBalance -= r.second; + nBalance -= allFee; + nBalance += allGeneratedMature; + } + return ValueFromAmount(nBalance); + } + + string strAccount = AccountFromValue(params[0]); + + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + + return ValueFromAmount(nBalance); +} + + +Value movecmd(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 5) + throw runtime_error( + "move [minconf=1] [comment]\n" + "Move from one account in your wallet to another."); + + string strFrom = AccountFromValue(params[0]); + string strTo = AccountFromValue(params[1]); + int64 nAmount = AmountFromValue(params[2]); + int nMinDepth = 1; + if (params.size() > 3) + nMinDepth = params[3].get_int(); + string strComment; + if (params.size() > 4) + strComment = params[4].get_str(); + + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + CWalletDB walletdb(pwalletMain->strWalletFile); + walletdb.TxnBegin(); + + int64 nNow = GetAdjustedTime(); + + // Debit + CAccountingEntry debit; + debit.strAccount = strFrom; + debit.nCreditDebit = -nAmount; + debit.nTime = nNow; + debit.strOtherAccount = strTo; + debit.strComment = strComment; + walletdb.WriteAccountingEntry(debit); + + // Credit + CAccountingEntry credit; + credit.strAccount = strTo; + credit.nCreditDebit = nAmount; + credit.nTime = nNow; + credit.strOtherAccount = strFrom; + credit.strComment = strComment; + walletdb.WriteAccountingEntry(credit); + + walletdb.TxnCommit(); + } + return true; +} + + +Value sendfrom(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 6) + throw runtime_error( + "sendfrom [minconf=1] [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.00000001"); + + string strAccount = AccountFromValue(params[0]); + string strAddress = params[1].get_str(); + int64 nAmount = AmountFromValue(params[2]); + int nMinDepth = 1; + if (params.size() > 3) + nMinDepth = params[3].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) + wtx.mapValue["comment"] = params[4].get_str(); + if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) + wtx.mapValue["to"] = params[5].get_str(); + + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + // Check funds + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + if (nAmount > nBalance) + throw JSONRPCError(-6, "Account has insufficient funds"); + + // Send + string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); + if (strError != "") + throw JSONRPCError(-4, strError); + } + + return wtx.GetHash().GetHex(); +} + +Value sendmany(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "sendmany {address:amount,...} [minconf=1] [comment]\n" + "amounts are double-precision floating point numbers"); + + string strAccount = AccountFromValue(params[0]); + Object sendTo = params[1].get_obj(); + int nMinDepth = 1; + if (params.size() > 2) + nMinDepth = params[2].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) + wtx.mapValue["comment"] = params[3].get_str(); + + set setAddress; + vector > vecSend; + + int64 totalAmount = 0; + BOOST_FOREACH(const Pair& s, sendTo) + { + uint160 hash160; + string strAddress = s.name_; + + if (setAddress.count(strAddress)) + throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+strAddress); + setAddress.insert(strAddress); + + CScript scriptPubKey; + if (!scriptPubKey.SetBitcoinAddress(strAddress)) + throw JSONRPCError(-5, string("Invalid bitcoin address:")+strAddress); + int64 nAmount = AmountFromValue(s.value_); + totalAmount += nAmount; + + vecSend.push_back(make_pair(scriptPubKey, nAmount)); + } + + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + // Check funds + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + if (totalAmount > nBalance) + throw JSONRPCError(-6, "Account has insufficient funds"); + + // Send + CReserveKey keyChange(pwalletMain); + int64 nFeeRequired = 0; + bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); + if (!fCreated) + { + if (totalAmount + nFeeRequired > pwalletMain->GetBalance()) + throw JSONRPCError(-6, "Insufficient funds"); + throw JSONRPCError(-4, "Transaction creation failed"); + } + if (!pwalletMain->CommitTransaction(wtx, keyChange)) + throw JSONRPCError(-4, "Transaction commit failed"); + } + + return wtx.GetHash().GetHex(); +} + + +struct tallyitem +{ + int64 nAmount; + int nConf; + tallyitem() + { + nAmount = 0; + nConf = INT_MAX; + } +}; + +Value ListReceived(const Array& params, bool fByAccounts) +{ + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + // Whether to include empty accounts + bool fIncludeEmpty = false; + if (params.size() > 1) + fIncludeEmpty = params[1].get_bool(); + + // Tally + map mapTally; + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + int nDepth = wtx.GetDepthInMainChain(); + if (nDepth < nMinDepth) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + // Only counting our own bitcoin addresses and not ip addresses + uint160 hash160 = txout.scriptPubKey.GetBitcoinAddressHash160(); + if (hash160 == 0 || !mapPubKeys.count(hash160)) // IsMine + continue; + + tallyitem& item = mapTally[hash160]; + item.nAmount += txout.nValue; + item.nConf = min(item.nConf, nDepth); + } + } + } + + // Reply + Array ret; + map mapAccountTally; + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) + { + const string& strAddress = item.first; + const string& strAccount = item.second; + uint160 hash160; + if (!AddressToHash160(strAddress, hash160)) + continue; + map::iterator it = mapTally.find(hash160); + if (it == mapTally.end() && !fIncludeEmpty) + continue; + + int64 nAmount = 0; + int nConf = INT_MAX; + if (it != mapTally.end()) + { + nAmount = (*it).second.nAmount; + nConf = (*it).second.nConf; + } + + if (fByAccounts) + { + tallyitem& item = mapAccountTally[strAccount]; + item.nAmount += nAmount; + item.nConf = min(item.nConf, nConf); + } + else + { + Object obj; + obj.push_back(Pair("address", strAddress)); + obj.push_back(Pair("account", strAccount)); + obj.push_back(Pair("label", strAccount)); // deprecated + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); + ret.push_back(obj); + } + } + } + + if (fByAccounts) + { + for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) + { + int64 nAmount = (*it).second.nAmount; + int nConf = (*it).second.nConf; + Object obj; + obj.push_back(Pair("account", (*it).first)); + obj.push_back(Pair("label", (*it).first)); // deprecated + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); + ret.push_back(obj); + } + } + + return ret; +} + +Value listreceivedbyaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "listreceivedbyaddress [minconf=1] [includeempty=false]\n" + "[minconf] is the minimum number of confirmations before payments are included.\n" + "[includeempty] whether to include addresses that haven't received any payments.\n" + "Returns an array of objects containing:\n" + " \"address\" : receiving address\n" + " \"account\" : the account of the receiving address\n" + " \"amount\" : total amount received by the address\n" + " \"confirmations\" : number of confirmations of the most recent transaction included"); + + return ListReceived(params, false); +} + +Value listreceivedbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "listreceivedbyaccount [minconf=1] [includeempty=false]\n" + "[minconf] is the minimum number of confirmations before payments are included.\n" + "[includeempty] whether to include accounts that haven't received any payments.\n" + "Returns an array of objects containing:\n" + " \"account\" : the account of the receiving addresses\n" + " \"amount\" : total amount received by addresses with this account\n" + " \"confirmations\" : number of confirmations of the most recent transaction included"); + + return ListReceived(params, true); +} + +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret) +{ + int64 nGeneratedImmature, nGeneratedMature, nFee; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); + + bool fAllAccounts = (strAccount == string("*")); + + // Generated blocks assigned to account "" + if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == "")) + { + Object entry; + entry.push_back(Pair("account", string(""))); + if (nGeneratedImmature) + { + entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan")); + entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature))); + } + else + { + entry.push_back(Pair("category", "generate")); + entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature))); + } + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + + // Sent + if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) + { + BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) + { + Object entry; + entry.push_back(Pair("account", strSentAccount)); + entry.push_back(Pair("address", s.first)); + entry.push_back(Pair("category", "send")); + entry.push_back(Pair("amount", ValueFromAmount(-s.second))); + entry.push_back(Pair("fee", ValueFromAmount(-nFee))); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + + // Received + if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) + { + string account; + if (pwalletMain->mapAddressBook.count(r.first)) + account = pwalletMain->mapAddressBook[r.first]; + if (fAllAccounts || (account == strAccount)) + { + Object entry; + entry.push_back(Pair("account", account)); + entry.push_back(Pair("address", r.first)); + entry.push_back(Pair("category", "receive")); + entry.push_back(Pair("amount", ValueFromAmount(r.second))); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + } + +} + +void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) +{ + bool fAllAccounts = (strAccount == string("*")); + + if (fAllAccounts || acentry.strAccount == strAccount) + { + Object entry; + entry.push_back(Pair("account", acentry.strAccount)); + entry.push_back(Pair("category", "move")); + entry.push_back(Pair("time", (boost::int64_t)acentry.nTime)); + entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); + entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); + entry.push_back(Pair("comment", acentry.strComment)); + ret.push_back(entry); + } +} + +Value listtransactions(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 3) + throw runtime_error( + "listtransactions [account] [count=10] [from=0]\n" + "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account]."); + + string strAccount = "*"; + if (params.size() > 0) + strAccount = params[0].get_str(); + int nCount = 10; + if (params.size() > 1) + nCount = params[1].get_int(); + int nFrom = 0; + if (params.size() > 2) + nFrom = params[2].get_int(); + + Array ret; + CWalletDB walletdb(pwalletMain->strWalletFile); + + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap: + typedef pair TxPair; + typedef multimap TxItems; + TxItems txByTime; + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0))); + } + list acentries; + walletdb.ListAccountCreditDebit(strAccount, acentries); + BOOST_FOREACH(CAccountingEntry& entry, acentries) + { + txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + } + + // Now: iterate backwards until we have nCount items to return: + TxItems::reverse_iterator it = txByTime.rbegin(); + for (std::advance(it, nFrom); it != txByTime.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx != 0) + ListTransactions(*pwtx, strAccount, 0, true, ret); + CAccountingEntry *const pacentry = (*it).second.second; + if (pacentry != 0) + AcentryToJSON(*pacentry, strAccount, ret); + + if (ret.size() >= nCount) break; + } + // ret is now newest to oldest + } + + // Make sure we return only last nCount items (sends-to-self might give us an extra): + if (ret.size() > nCount) + { + Array::iterator last = ret.begin(); + std::advance(last, nCount); + ret.erase(last, ret.end()); + } + std::reverse(ret.begin(), ret.end()); // oldest to newest + + return ret; +} + +Value listaccounts(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "listaccounts [minconf=1]\n" + "Returns Object that has account names as keys, account balances as values."); + + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + map mapAccountBalances; + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& entry, pwalletMain->mapAddressBook) { + uint160 hash160; + if(AddressToHash160(entry.first, hash160) && mapPubKeys.count(hash160)) // This address belongs to me + mapAccountBalances[entry.second] = 0; + } + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + int64 nGeneratedImmature, nGeneratedMature, nFee; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); + mapAccountBalances[strSentAccount] -= nFee; + BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) + mapAccountBalances[strSentAccount] -= s.second; + if (wtx.GetDepthInMainChain() >= nMinDepth) + { + mapAccountBalances[""] += nGeneratedMature; + BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) + if (pwalletMain->mapAddressBook.count(r.first)) + mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second; + else + mapAccountBalances[""] += r.second; + } + } + } + + list acentries; + CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); + BOOST_FOREACH(const CAccountingEntry& entry, acentries) + mapAccountBalances[entry.strAccount] += entry.nCreditDebit; + + Object ret; + BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) { + ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); + } + return ret; +} + +Value gettransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "gettransaction \n" + "Get detailed information about "); + + uint256 hash; + hash.SetHex(params[0].get_str()); + + Object entry; + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + if (!pwalletMain->mapWallet.count(hash)) + throw JSONRPCError(-5, "Invalid or non-wallet transaction id"); + const CWalletTx& wtx = pwalletMain->mapWallet[hash]; + + int64 nCredit = wtx.GetCredit(); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); + + entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); + if (wtx.IsFromMe()) + entry.push_back(Pair("fee", ValueFromAmount(nFee))); + + WalletTxToJSON(pwalletMain->mapWallet[hash], entry); + + Array details; + ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details); + entry.push_back(Pair("details", details)); + } + + return entry; +} + + +Value backupwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "backupwallet \n" + "Safely copies wallet.dat to destination, which can be a directory or a path with filename."); + + string strDest = params[0].get_str(); + BackupWallet(*pwalletMain, strDest); + + return Value::null; +} + + +Value validateaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "validateaddress \n" + "Return information about ."); + + string strAddress = params[0].get_str(); + uint160 hash160; + bool isValid = AddressToHash160(strAddress, hash160); + + Object ret; + ret.push_back(Pair("isvalid", isValid)); + if (isValid) + { + // Call Hash160ToAddress() so we always return current ADDRESSVERSION + // version of the address: + string currentAddress = Hash160ToAddress(hash160); + ret.push_back(Pair("address", currentAddress)); + ret.push_back(Pair("ismine", (mapPubKeys.count(hash160) > 0))); + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + if (pwalletMain->mapAddressBook.count(currentAddress)) + ret.push_back(Pair("account", pwalletMain->mapAddressBook[currentAddress])); + } + } + return ret; +} + + +Value getwork(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getwork [data]\n" + "If [data] is not specified, returns formatted hash data to work on:\n" + " \"midstate\" : precomputed hash state after hashing the first half of the data\n" + " \"data\" : block data\n" + " \"hash1\" : formatted hash buffer for second hash\n" + " \"target\" : little endian hash target\n" + "If [data] is specified, tries to solve the block and returns true if it was successful."); + + if (vNodes.empty()) + throw JSONRPCError(-9, "Bitcoin is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(-10, "Bitcoin is downloading blocks..."); + + static map > mapNewBlock; + static vector vNewBlock; + static CReserveKey reservekey(pwalletMain); + + if (params.size() == 0) + { + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64 nStart; + static CBlock* pblock; + if (pindexPrev != pindexBest || + (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) + { + if (pindexPrev != pindexBest) + { + // Deallocate old blocks since they're obsolete now + mapNewBlock.clear(); + BOOST_FOREACH(CBlock* pblock, vNewBlock) + delete pblock; + vNewBlock.clear(); + } + nTransactionsUpdatedLast = nTransactionsUpdated; + pindexPrev = pindexBest; + nStart = GetTime(); + + // Create new block + pblock = CreateNewBlock(reservekey); + if (!pblock) + throw JSONRPCError(-7, "Out of memory"); + vNewBlock.push_back(pblock); + } + + // Update nTime + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->nNonce = 0; + + // Update nExtraNonce + static unsigned int nExtraNonce = 0; + static int64 nPrevTime = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, nPrevTime); + + // Save + mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, nExtraNonce); + + // Prebuild hash buffers + char pmidstate[32]; + char pdata[128]; + char phash1[64]; + FormatHashBuffers(pblock, pmidstate, pdata, phash1); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + Object result; + result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); + result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); + result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); + result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); + return result; + } + else + { + // Parse parameters + vector vchData = ParseHex(params[0].get_str()); + if (vchData.size() != 128) + throw JSONRPCError(-8, "Invalid parameter"); + CBlock* pdata = (CBlock*)&vchData[0]; + + // Byte reverse + for (int i = 0; i < 128/4; i++) + ((unsigned int*)pdata)[i] = CryptoPP::ByteReverse(((unsigned int*)pdata)[i]); + + // Get saved block + if (!mapNewBlock.count(pdata->hashMerkleRoot)) + return false; + CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; + unsigned int nExtraNonce = mapNewBlock[pdata->hashMerkleRoot].second; + + pblock->nTime = pdata->nTime; + pblock->nNonce = pdata->nNonce; + pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + + return CheckWork(pblock, *pwalletMain, reservekey); + } +} + + + + + + + + + + + +// +// Call Table +// + +pair pCallTable[] = +{ + make_pair("help", &help), + make_pair("stop", &stop), + make_pair("getblockcount", &getblockcount), + make_pair("getblocknumber", &getblocknumber), + make_pair("getconnectioncount", &getconnectioncount), + make_pair("getdifficulty", &getdifficulty), + make_pair("getgenerate", &getgenerate), + make_pair("setgenerate", &setgenerate), + make_pair("gethashespersec", &gethashespersec), + make_pair("getinfo", &getinfo), + make_pair("getnewaddress", &getnewaddress), + make_pair("getaccountaddress", &getaccountaddress), + make_pair("setaccount", &setaccount), + make_pair("setlabel", &setaccount), // deprecated + make_pair("getaccount", &getaccount), + make_pair("getlabel", &getaccount), // deprecated + make_pair("getaddressesbyaccount", &getaddressesbyaccount), + make_pair("getaddressesbylabel", &getaddressesbyaccount), // deprecated + make_pair("sendtoaddress", &sendtoaddress), + make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress + make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress + make_pair("getreceivedbyaddress", &getreceivedbyaddress), + make_pair("getreceivedbyaccount", &getreceivedbyaccount), + make_pair("getreceivedbylabel", &getreceivedbyaccount), // deprecated + make_pair("listreceivedbyaddress", &listreceivedbyaddress), + make_pair("listreceivedbyaccount", &listreceivedbyaccount), + make_pair("listreceivedbylabel", &listreceivedbyaccount), // deprecated + make_pair("backupwallet", &backupwallet), + make_pair("validateaddress", &validateaddress), + make_pair("getbalance", &getbalance), + make_pair("move", &movecmd), + make_pair("sendfrom", &sendfrom), + make_pair("sendmany", &sendmany), + make_pair("gettransaction", &gettransaction), + make_pair("listtransactions", &listtransactions), + make_pair("getwork", &getwork), + make_pair("listaccounts", &listaccounts), + make_pair("settxfee", &settxfee), +}; +map mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0])); + +string pAllowInSafeMode[] = +{ + "help", + "stop", + "getblockcount", + "getblocknumber", + "getconnectioncount", + "getdifficulty", + "getgenerate", + "setgenerate", + "gethashespersec", + "getinfo", + "getnewaddress", + "getaccountaddress", + "setlabel", + "getaccount", + "getlabel", // deprecated + "getaddressesbyaccount", + "getaddressesbylabel", // deprecated + "backupwallet", + "validateaddress", + "getwork", +}; +set setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0])); + + + + +// +// HTTP protocol +// +// This ain't Apache. We're just using HTTP header for the length field +// and to be compatible with other JSON-RPC implementations. +// + +string HTTPPost(const string& strMsg, const map& mapRequestHeaders) +{ + ostringstream s; + s << "POST / HTTP/1.1\r\n" + << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n" + << "Host: 127.0.0.1\r\n" + << "Content-Type: application/json\r\n" + << "Content-Length: " << strMsg.size() << "\r\n" + << "Accept: application/json\r\n"; + BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders) + s << item.first << ": " << item.second << "\r\n"; + s << "\r\n" << strMsg; + + return s.str(); +} + +string rfc1123Time() +{ + char buffer[64]; + time_t now; + time(&now); + struct tm* now_gmt = gmtime(&now); + string locale(setlocale(LC_TIME, NULL)); + setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings + strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); + setlocale(LC_TIME, locale.c_str()); + return string(buffer); +} + +string HTTPReply(int nStatus, const string& strMsg) +{ + if (nStatus == 401) + return strprintf("HTTP/1.0 401 Authorization Required\r\n" + "Date: %s\r\n" + "Server: bitcoin-json-rpc/%s\r\n" + "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 296\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Error\r\n" + "\r\n" + "\r\n" + "

401 Unauthorized.

\r\n" + "\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); + string strStatus; + if (nStatus == 200) strStatus = "OK"; + else if (nStatus == 400) strStatus = "Bad Request"; + else if (nStatus == 404) strStatus = "Not Found"; + else if (nStatus == 500) strStatus = "Internal Server Error"; + return strprintf( + "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Connection: close\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/json\r\n" + "Server: bitcoin-json-rpc/%s\r\n" + "\r\n" + "%s", + nStatus, + strStatus.c_str(), + rfc1123Time().c_str(), + strMsg.size(), + FormatFullVersion().c_str(), + strMsg.c_str()); +} + +int ReadHTTPStatus(std::basic_istream& stream) +{ + string str; + getline(stream, str); + vector vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return 500; + return atoi(vWords[1].c_str()); +} + +int ReadHTTPHeader(std::basic_istream& stream, map& mapHeadersRet) +{ + int nLen = 0; + loop + { + string str; + std::getline(stream, str); + if (str.empty() || str == "\r") + break; + string::size_type nColon = str.find(":"); + if (nColon != string::npos) + { + string strHeader = str.substr(0, nColon); + boost::trim(strHeader); + boost::to_lower(strHeader); + string strValue = str.substr(nColon+1); + boost::trim(strValue); + mapHeadersRet[strHeader] = strValue; + if (strHeader == "content-length") + nLen = atoi(strValue.c_str()); + } + } + return nLen; +} + +int ReadHTTP(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet) +{ + mapHeadersRet.clear(); + strMessageRet = ""; + + // Read status + int nStatus = ReadHTTPStatus(stream); + + // Read header + int nLen = ReadHTTPHeader(stream, mapHeadersRet); + if (nLen < 0 || nLen > MAX_SIZE) + return 500; + + // Read message + if (nLen > 0) + { + vector vch(nLen); + stream.read(&vch[0], nLen); + strMessageRet = string(vch.begin(), vch.end()); + } + + return nStatus; +} + +string EncodeBase64(string s) +{ + BIO *b64, *bmem; + BUF_MEM *bptr; + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_new(BIO_s_mem()); + b64 = BIO_push(b64, bmem); + BIO_write(b64, s.c_str(), s.size()); + BIO_flush(b64); + BIO_get_mem_ptr(b64, &bptr); + + string result(bptr->data, bptr->length); + BIO_free_all(b64); + + return result; +} + +string DecodeBase64(string s) +{ + BIO *b64, *bmem; + + char* buffer = static_cast(calloc(s.size(), sizeof(char))); + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_new_mem_buf(const_cast(s.c_str()), s.size()); + bmem = BIO_push(b64, bmem); + BIO_read(bmem, buffer, s.size()); + BIO_free_all(bmem); + + string result(buffer); + free(buffer); + return result; +} + +bool HTTPAuthorized(map& mapHeaders) +{ + string strAuth = mapHeaders["authorization"]; + if (strAuth.substr(0,6) != "Basic ") + return false; + string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); + string strUserPass = DecodeBase64(strUserPass64); + string::size_type nColon = strUserPass.find(":"); + if (nColon == string::npos) + return false; + string strUser = strUserPass.substr(0, nColon); + string strPassword = strUserPass.substr(nColon+1); + return (strUser == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]); +} + +// +// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, +// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were +// unspecified (HTTP errors and contents of 'error'). +// +// 1.0 spec: http://json-rpc.org/wiki/specification +// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http +// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx +// + +string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) +{ + Object request; + request.push_back(Pair("method", strMethod)); + request.push_back(Pair("params", params)); + request.push_back(Pair("id", id)); + return write_string(Value(request), false) + "\n"; +} + +string JSONRPCReply(const Value& result, const Value& error, const Value& id) +{ + Object reply; + if (error.type() != null_type) + reply.push_back(Pair("result", Value::null)); + else + reply.push_back(Pair("result", result)); + reply.push_back(Pair("error", error)); + reply.push_back(Pair("id", id)); + return write_string(Value(reply), false) + "\n"; +} + +void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) +{ + // Send error reply from json-rpc error object + int nStatus = 500; + int code = find_value(objError, "code").get_int(); + if (code == -32600) nStatus = 400; + else if (code == -32601) nStatus = 404; + string strReply = JSONRPCReply(Value::null, objError, id); + stream << HTTPReply(nStatus, strReply) << std::flush; +} + +bool ClientAllowed(const string& strAddress) +{ + if (strAddress == asio::ip::address_v4::loopback().to_string()) + return true; + const vector& vAllow = mapMultiArgs["-rpcallowip"]; + BOOST_FOREACH(string strAllow, vAllow) + if (WildcardMatch(strAddress, strAllow)) + return true; + return false; +} + +#ifdef USE_SSL +// +// IOStream device that speaks SSL but can also speak non-SSL +// +class SSLIOStreamDevice : public iostreams::device { +public: + SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn) + { + fUseSSL = fUseSSLIn; + fNeedHandshake = fUseSSLIn; + } + + void handshake(ssl::stream_base::handshake_type role) + { + if (!fNeedHandshake) return; + fNeedHandshake = false; + stream.handshake(role); + } + std::streamsize read(char* s, std::streamsize n) + { + handshake(ssl::stream_base::server); // HTTPS servers read first + if (fUseSSL) return stream.read_some(asio::buffer(s, n)); + return stream.next_layer().read_some(asio::buffer(s, n)); + } + std::streamsize write(const char* s, std::streamsize n) + { + handshake(ssl::stream_base::client); // HTTPS clients write first + if (fUseSSL) return asio::write(stream, asio::buffer(s, n)); + return asio::write(stream.next_layer(), asio::buffer(s, n)); + } + bool connect(const std::string& server, const std::string& port) + { + ip::tcp::resolver resolver(stream.get_io_service()); + ip::tcp::resolver::query query(server.c_str(), port.c_str()); + ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); + ip::tcp::resolver::iterator end; + boost::system::error_code error = asio::error::host_not_found; + while (error && endpoint_iterator != end) + { + stream.lowest_layer().close(); + stream.lowest_layer().connect(*endpoint_iterator++, error); + } + if (error) + return false; + return true; + } + +private: + bool fNeedHandshake; + bool fUseSSL; + SSLStream& stream; +}; +#endif + +void ThreadRPCServer(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg)); + try + { + vnThreadsRunning[4]++; + ThreadRPCServer2(parg); + vnThreadsRunning[4]--; + } + catch (std::exception& e) { + vnThreadsRunning[4]--; + PrintException(&e, "ThreadRPCServer()"); + } catch (...) { + vnThreadsRunning[4]--; + PrintException(NULL, "ThreadRPCServer()"); + } + printf("ThreadRPCServer exiting\n"); +} + +void ThreadRPCServer2(void* parg) +{ + printf("ThreadRPCServer started\n"); + + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + { + string strWhatAmI = "To use bitcoind"; + if (mapArgs.count("-server")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); + else if (mapArgs.count("-daemon")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); + PrintConsole( + _("Warning: %s, you must set rpcpassword=\nin the configuration file: %s\n" + "If the file does not exist, create it with owner-readable-only file permissions.\n"), + strWhatAmI.c_str(), + GetConfigFile().c_str()); + CreateThread(Shutdown, NULL); + return; + } + + bool fUseSSL = GetBoolArg("-rpcssl"); + asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); + + asio::io_service io_service; + ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332)); + ip::tcp::acceptor acceptor(io_service, endpoint); + + acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + +#ifdef USE_SSL + ssl::context context(io_service, ssl::context::sslv23); + if (fUseSSL) + { + context.set_options(ssl::context::no_sslv2); + filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert"); + if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile; + if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str()); + else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str()); + filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem"); + if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile; + if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem); + else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str()); + + string ciphers = GetArg("-rpcsslciphers", + "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); + SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str()); + } +#else + if (fUseSSL) + throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); +#endif + + loop + { + // Accept connection +#ifdef USE_SSL + SSLStream sslStream(io_service, context); + SSLIOStreamDevice d(sslStream, fUseSSL); + iostreams::stream stream(d); +#else + ip::tcp::iostream stream; +#endif + + ip::tcp::endpoint peer; + vnThreadsRunning[4]--; +#ifdef USE_SSL + acceptor.accept(sslStream.lowest_layer(), peer); +#else + acceptor.accept(*stream.rdbuf(), peer); +#endif + vnThreadsRunning[4]++; + if (fShutdown) + return; + + // Restrict callers by IP + if (!ClientAllowed(peer.address().to_string())) + continue; + + map mapHeaders; + string strRequest; + + boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest)); + if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30)))) + { // Timed out: + acceptor.cancel(); + printf("ThreadRPCServer ReadHTTP timeout\n"); + continue; + } + + // Check authorization + if (mapHeaders.count("authorization") == 0) + { + stream << HTTPReply(401, "") << std::flush; + continue; + } + if (!HTTPAuthorized(mapHeaders)) + { + // Deter brute-forcing short passwords + if (mapArgs["-rpcpassword"].size() < 15) + Sleep(50); + + stream << HTTPReply(401, "") << std::flush; + printf("ThreadRPCServer incorrect password attempt\n"); + continue; + } + + Value id = Value::null; + try + { + // Parse request + Value valRequest; + if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type) + throw JSONRPCError(-32700, "Parse error"); + const Object& request = valRequest.get_obj(); + + // Parse id now so errors from here on will have the id + id = find_value(request, "id"); + + // Parse method + Value valMethod = find_value(request, "method"); + if (valMethod.type() == null_type) + throw JSONRPCError(-32600, "Missing method"); + if (valMethod.type() != str_type) + throw JSONRPCError(-32600, "Method must be a string"); + string strMethod = valMethod.get_str(); + if (strMethod != "getwork") + printf("ThreadRPCServer method=%s\n", strMethod.c_str()); + + // Parse params + Value valParams = find_value(request, "params"); + Array params; + if (valParams.type() == array_type) + params = valParams.get_array(); + else if (valParams.type() == null_type) + params = Array(); + else + throw JSONRPCError(-32600, "Params must be an array"); + + // Find method + map::iterator mi = mapCallTable.find(strMethod); + if (mi == mapCallTable.end()) + throw JSONRPCError(-32601, "Method not found"); + + // Observe safe mode + string strWarning = GetWarnings("rpc"); + if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod)) + throw JSONRPCError(-2, string("Safe mode: ") + strWarning); + + try + { + // Execute + Value result = (*(*mi).second)(params, false); + + // Send reply + string strReply = JSONRPCReply(result, Value::null, id); + stream << HTTPReply(200, strReply) << std::flush; + } + catch (std::exception& e) + { + ErrorReply(stream, JSONRPCError(-1, e.what()), id); + } + } + catch (Object& objError) + { + ErrorReply(stream, objError, id); + } + catch (std::exception& e) + { + ErrorReply(stream, JSONRPCError(-32700, e.what()), id); + } + } +} + + + + +Object CallRPC(const string& strMethod, const Array& params) +{ + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + throw runtime_error(strprintf( + _("You must set rpcpassword= in the configuration file:\n%s\n" + "If the file does not exist, create it with owner-readable-only file permissions."), + GetConfigFile().c_str())); + + // Connect to localhost + bool fUseSSL = GetBoolArg("-rpcssl"); +#ifdef USE_SSL + asio::io_service io_service; + ssl::context context(io_service, ssl::context::sslv23); + context.set_options(ssl::context::no_sslv2); + SSLStream sslStream(io_service, context); + SSLIOStreamDevice d(sslStream, fUseSSL); + iostreams::stream stream(d); + if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"))) + throw runtime_error("couldn't connect to server"); +#else + if (fUseSSL) + throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); + + ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")); + if (stream.fail()) + throw runtime_error("couldn't connect to server"); +#endif + + + // HTTP basic authentication + string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); + map mapRequestHeaders; + mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; + + // Send request + string strRequest = JSONRPCRequest(strMethod, params, 1); + string strPost = HTTPPost(strRequest, mapRequestHeaders); + stream << strPost << std::flush; + + // Receive reply + map mapHeaders; + string strReply; + int nStatus = ReadHTTP(stream, mapHeaders, strReply); + if (nStatus == 401) + throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500) + throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); + else if (strReply.empty()) + throw runtime_error("no response from server"); + + // Parse reply + Value valReply; + if (!read_string(strReply, valReply)) + throw runtime_error("couldn't parse reply from server"); + const Object& reply = valReply.get_obj(); + if (reply.empty()) + throw runtime_error("expected reply to have result, error and id properties"); + + return reply; +} + + + + +template +void ConvertTo(Value& value) +{ + if (value.type() == str_type) + { + // reinterpret string as unquoted json value + Value value2; + if (!read_string(value.get_str(), value2)) + throw runtime_error("type mismatch"); + value = value2.get_value(); + } + else + { + value = value.get_value(); + } +} + +int CommandLineRPC(int argc, char *argv[]) +{ + string strPrint; + int nRet = 0; + try + { + // Skip switches + while (argc > 1 && IsSwitchChar(argv[1][0])) + { + argc--; + argv++; + } + + // Method + if (argc < 2) + throw runtime_error("too few parameters"); + string strMethod = argv[1]; + + // Parameters default to strings + Array params; + for (int i = 2; i < argc; i++) + params.push_back(argv[i]); + int n = params.size(); + + // + // Special case non-string parameter types + // + if (strMethod == "setgenerate" && n > 0) ConvertTo(params[0]); + if (strMethod == "setgenerate" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); + if (strMethod == "getamountreceived" && n > 1) ConvertTo(params[1]); // deprecated + if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo(params[1]); + if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated + if (strMethod == "getallreceived" && n > 0) ConvertTo(params[0]); // deprecated + if (strMethod == "getallreceived" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo(params[0]); + if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo(params[0]); // deprecated + if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated + if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); + if (strMethod == "move" && n > 2) ConvertTo(params[2]); + if (strMethod == "move" && n > 3) ConvertTo(params[3]); + if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]); + if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); + if (strMethod == "listtransactions" && n > 1) ConvertTo(params[1]); + if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); + if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); + if (strMethod == "sendmany" && n > 1) + { + string s = params[1].get_str(); + Value v; + if (!read_string(s, v) || v.type() != obj_type) + throw runtime_error("type mismatch"); + params[1] = v.get_obj(); + } + if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + + // Execute + Object reply = CallRPC(strMethod, params); + + // Parse reply + const Value& result = find_value(reply, "result"); + const Value& error = find_value(reply, "error"); + const Value& id = find_value(reply, "id"); + + if (error.type() != null_type) + { + // Error + strPrint = "error: " + write_string(error, false); + int code = find_value(error.get_obj(), "code").get_int(); + nRet = abs(code); + } + else + { + // Result + if (result.type() == null_type) + strPrint = ""; + else if (result.type() == str_type) + strPrint = result.get_str(); + else + strPrint = write_string(result, true); + } + } + catch (std::exception& e) + { + strPrint = string("error: ") + e.what(); + nRet = 87; + } + catch (...) + { + PrintException(NULL, "CommandLineRPC()"); + } + + if (strPrint != "") + { +#if defined(__WXMSW__) && defined(GUI) + // Windows GUI apps can't print to command line, + // so settle for a message box yuck + MyMessageBox(strPrint, "Bitcoin", wxOK); +#else + fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); +#endif + } + return nRet; +} + + + + +#ifdef TEST +int main(int argc, char *argv[]) +{ +#ifdef _MSC_VER + // Turn off microsoft heap dump noise + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); +#endif + setbuf(stdin, NULL); + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + try + { + if (argc >= 2 && string(argv[1]) == "-server") + { + printf("server ready\n"); + ThreadRPCServer(NULL); + } + else + { + return CommandLineRPC(argc, argv); + } + } + catch (std::exception& e) { + PrintException(&e, "main()"); + } catch (...) { + PrintException(NULL, "main()"); + } + return 0; +} +#endif diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h new file mode 100644 index 0000000000..48a7b8a8a6 --- /dev/null +++ b/src/bitcoinrpc.h @@ -0,0 +1,6 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +void ThreadRPCServer(void* parg); +int CommandLineRPC(int argc, char *argv[]); diff --git a/src/init.cpp b/src/init.cpp index e4605a2b42..cac921e2b2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -3,7 +3,7 @@ // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" #include "db.h" -#include "rpc.h" +#include "bitcoinrpc.h" #include "net.h" #include "init.h" #include "strlcpy.h" diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 397af5fd38..78a20c51c0 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -113,21 +113,23 @@ int main(int argc, char *argv[]) { if(AppInit2(argc, argv)) { - BitcoinGUI window; - ClientModel clientModel(pwalletMain); - WalletModel walletModel(pwalletMain); - guiref = &window; - window.setClientModel(&clientModel); - window.setWalletModel(&walletModel); - - window.show(); - - int retval = app.exec(); - - guiref = 0; + { + // Put this in a block, so that BitcoinGUI is cleaned up properly before + // calling shutdown. + BitcoinGUI window; + ClientModel clientModel(pwalletMain); + WalletModel walletModel(pwalletMain); + guiref = &window; + window.setClientModel(&clientModel); + window.setWalletModel(&walletModel); + + window.show(); + + app.exec(); + + guiref = 0; + } Shutdown(NULL); - - return retval; } else { @@ -138,4 +140,5 @@ int main(int argc, char *argv[]) } catch (...) { PrintException(NULL, "Runaway exception"); } + return 0; } diff --git a/src/qt/forms/addressbookdialog.ui b/src/qt/forms/addressbookdialog.ui index 12ecb13651..66f1076afb 100644 --- a/src/qt/forms/addressbookdialog.ui +++ b/src/qt/forms/addressbookdialog.ui @@ -100,19 +100,6 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -155,6 +142,19 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index c68532b87c..31b28024df 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1,6 +1,7 @@ #include "guiutil.h" #include "bitcoinaddressvalidator.h" -#include "util.h" + +#include "headers.h" #include #include diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index bb2537a479..809e473060 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -1,7 +1,8 @@ #include #include "guiutil.h" -#include "main.h" + +#include "headers.h" #include "qtui.h" #include diff --git a/src/rpc.cpp b/src/rpc.cpp deleted file mode 100644 index 644ad92297..0000000000 --- a/src/rpc.cpp +++ /dev/null @@ -1,2215 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. - -#include "headers.h" -#include "cryptopp/sha.h" -#include "db.h" -#include "net.h" -#include "init.h" -#undef printf -#include -#include -#include -#include -#ifdef USE_SSL -#include -#include -#include -typedef boost::asio::ssl::stream SSLStream; -#endif -#include "json/json_spirit_reader_template.h" -#include "json/json_spirit_writer_template.h" -#include "json/json_spirit_utils.h" -#define printf OutputDebugStringF -// MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are -// precompiled in headers.h. The problem might be when the pch file goes over -// a certain size around 145MB. If we need access to json_spirit outside this -// file, we could use the compiled json_spirit option. - -using namespace std; -using namespace boost; -using namespace boost::asio; -using namespace json_spirit; - -void ThreadRPCServer2(void* parg); -typedef Value(*rpcfn_type)(const Array& params, bool fHelp); -extern map mapCallTable; - - -Object JSONRPCError(int code, const string& message) -{ - Object error; - error.push_back(Pair("code", code)); - error.push_back(Pair("message", message)); - return error; -} - - -void PrintConsole(const std::string &format, ...) -{ - char buffer[50000]; - int limit = sizeof(buffer); - va_list arg_ptr; - va_start(arg_ptr, format); - int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr); - va_end(arg_ptr); - if (ret < 0 || ret >= limit) - { - ret = limit - 1; - buffer[limit-1] = 0; - } - printf("%s", buffer); -#if defined(__WXMSW__) && defined(GUI) - MyMessageBox(buffer, "Bitcoin", wxOK | wxICON_EXCLAMATION); -#else - fprintf(stdout, "%s", buffer); -#endif -} - - -int64 AmountFromValue(const Value& value) -{ - double dAmount = value.get_real(); - if (dAmount <= 0.0 || dAmount > 21000000.0) - throw JSONRPCError(-3, "Invalid amount"); - int64 nAmount = roundint64(dAmount * COIN); - if (!MoneyRange(nAmount)) - throw JSONRPCError(-3, "Invalid amount"); - return nAmount; -} - -Value ValueFromAmount(int64 amount) -{ - return (double)amount / (double)COIN; -} - -void WalletTxToJSON(const CWalletTx& wtx, Object& entry) -{ - entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain())); - entry.push_back(Pair("txid", wtx.GetHash().GetHex())); - entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); - BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) - entry.push_back(Pair(item.first, item.second)); -} - -string AccountFromValue(const Value& value) -{ - string strAccount = value.get_str(); - if (strAccount == "*") - throw JSONRPCError(-11, "Invalid account name"); - return strAccount; -} - - - -/// -/// Note: This interface may still be subject to change. -/// - - -Value help(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "help [command]\n" - "List commands, or get help for a command."); - - string strCommand; - if (params.size() > 0) - strCommand = params[0].get_str(); - - string strRet; - set setDone; - for (map::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi) - { - string strMethod = (*mi).first; - // We already filter duplicates, but these deprecated screw up the sort order - if (strMethod == "getamountreceived" || - strMethod == "getallreceived" || - (strMethod.find("label") != string::npos)) - continue; - if (strCommand != "" && strMethod != strCommand) - continue; - try - { - Array params; - rpcfn_type pfn = (*mi).second; - if (setDone.insert(pfn).second) - (*pfn)(params, true); - } - catch (std::exception& e) - { - // Help text is returned in an exception - string strHelp = string(e.what()); - if (strCommand == "") - if (strHelp.find('\n') != -1) - strHelp = strHelp.substr(0, strHelp.find('\n')); - strRet += strHelp + "\n"; - } - } - if (strRet == "") - strRet = strprintf("help: unknown command: %s\n", strCommand.c_str()); - strRet = strRet.substr(0,strRet.size()-1); - return strRet; -} - - -Value stop(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "stop\n" - "Stop bitcoin server."); - - // Shutdown will take long enough that the response should get back - CreateThread(Shutdown, NULL); - return "bitcoin server stopping"; -} - - -Value getblockcount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblockcount\n" - "Returns the number of blocks in the longest block chain."); - - return nBestHeight; -} - - -Value getblocknumber(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblocknumber\n" - "Returns the block number of the latest block in the longest block chain."); - - return nBestHeight; -} - - -Value getconnectioncount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getconnectioncount\n" - "Returns the number of connections to other nodes."); - - return (int)vNodes.size(); -} - - -double GetDifficulty() -{ - // Floating point number that is a multiple of the minimum difficulty, - // minimum difficulty = 1.0. - - if (pindexBest == NULL) - return 1.0; - int nShift = (pindexBest->nBits >> 24) & 0xff; - - double dDiff = - (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff); - - while (nShift < 29) - { - dDiff *= 256.0; - nShift++; - } - while (nShift > 29) - { - dDiff /= 256.0; - nShift--; - } - - return dDiff; -} - -Value getdifficulty(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getdifficulty\n" - "Returns the proof-of-work difficulty as a multiple of the minimum difficulty."); - - return GetDifficulty(); -} - - -Value getgenerate(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getgenerate\n" - "Returns true or false."); - - return (bool)fGenerateBitcoins; -} - - -Value setgenerate(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "setgenerate [genproclimit]\n" - " is true or false to turn generation on or off.\n" - "Generation is limited to [genproclimit] processors, -1 is unlimited."); - - bool fGenerate = true; - if (params.size() > 0) - fGenerate = params[0].get_bool(); - - if (params.size() > 1) - { - int nGenProcLimit = params[1].get_int(); - fLimitProcessors = (nGenProcLimit != -1); - WriteSetting("fLimitProcessors", fLimitProcessors); - if (nGenProcLimit != -1) - WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit); - if (nGenProcLimit == 0) - fGenerate = false; - } - - GenerateBitcoins(fGenerate, pwalletMain); - return Value::null; -} - - -Value gethashespersec(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "gethashespersec\n" - "Returns a recent hashes per second performance measurement while generating."); - - if (GetTimeMillis() - nHPSTimerStart > 8000) - return (boost::int64_t)0; - return (boost::int64_t)dHashesPerSec; -} - - -Value getinfo(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getinfo\n" - "Returns an object containing various state info."); - - Object obj; - obj.push_back(Pair("version", (int)VERSION)); - obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); - obj.push_back(Pair("blocks", (int)nBestHeight)); - obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string()))); - obj.push_back(Pair("generate", (bool)fGenerateBitcoins)); - obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1))); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("hashespersec", gethashespersec(params, false))); - obj.push_back(Pair("testnet", fTestNet)); - obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); - obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - return obj; -} - - -Value getnewaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getnewaddress [account]\n" - "Returns a new bitcoin address for receiving payments. " - "If [account] is specified (recommended), it is added to the address book " - "so payments received with the address will be credited to [account]."); - - // Parse the account first so we don't generate a key if there's an error - string strAccount; - if (params.size() > 0) - strAccount = AccountFromValue(params[0]); - - // Generate a new key that is added to wallet - string strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool()); - - pwalletMain->SetAddressBookName(strAddress, strAccount); - return strAddress; -} - - -// requires cs_main, cs_mapWallet locks -string GetAccountAddress(string strAccount, bool bForceNew=false) -{ - string strAddress; - - CWalletDB walletdb(pwalletMain->strWalletFile); - walletdb.TxnBegin(); - - CAccount account; - walletdb.ReadAccount(strAccount, account); - - // Check if the current key has been used - if (!account.vchPubKey.empty()) - { - CScript scriptPubKey; - scriptPubKey.SetBitcoinAddress(account.vchPubKey); - for (map::iterator it = pwalletMain->mapWallet.begin(); - it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty(); - ++it) - { - const CWalletTx& wtx = (*it).second; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - account.vchPubKey.clear(); - } - } - - // Generate a new key - if (account.vchPubKey.empty() || bForceNew) - { - account.vchPubKey = pwalletMain->GetKeyFromKeyPool(); - string strAddress = PubKeyToAddress(account.vchPubKey); - pwalletMain->SetAddressBookName(strAddress, strAccount); - walletdb.WriteAccount(strAccount, account); - } - - walletdb.TxnCommit(); - strAddress = PubKeyToAddress(account.vchPubKey); - - return strAddress; -} - -Value getaccountaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaccountaddress \n" - "Returns the current bitcoin address for receiving payments to this account."); - - // Parse the account first so we don't generate a key if there's an error - string strAccount = AccountFromValue(params[0]); - - Value ret; - - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - ret = GetAccountAddress(strAccount); - } - - return ret; -} - - - -Value setaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "setaccount \n" - "Sets the account associated with the given address."); - - string strAddress = params[0].get_str(); - uint160 hash160; - bool isValid = AddressToHash160(strAddress, hash160); - if (!isValid) - throw JSONRPCError(-5, "Invalid bitcoin address"); - - - string strAccount; - if (params.size() > 1) - strAccount = AccountFromValue(params[1]); - - // Detect when changing the account of an address that is the 'unused current key' of another account: - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - if (pwalletMain->mapAddressBook.count(strAddress)) - { - string strOldAccount = pwalletMain->mapAddressBook[strAddress]; - if (strAddress == GetAccountAddress(strOldAccount)) - GetAccountAddress(strOldAccount, true); - } - } - - pwalletMain->SetAddressBookName(strAddress, strAccount); - return Value::null; -} - - -Value getaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaccount \n" - "Returns the account associated with the given address."); - - string strAddress = params[0].get_str(); - - string strAccount; - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - map::iterator mi = pwalletMain->mapAddressBook.find(strAddress); - if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty()) - strAccount = (*mi).second; - } - return strAccount; -} - - -Value getaddressesbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaddressesbyaccount \n" - "Returns the list of addresses for the given account."); - - string strAccount = AccountFromValue(params[0]); - - // Find all addresses that have the given account - Array ret; - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) - { - const string& strAddress = item.first; - const string& strName = item.second; - if (strName == strAccount) - { - // We're only adding valid bitcoin addresses and not ip addresses - CScript scriptPubKey; - if (scriptPubKey.SetBitcoinAddress(strAddress)) - ret.push_back(strAddress); - } - } - } - return ret; -} - -Value settxfee(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 1) - throw runtime_error( - "settxfee \n" - " is a real and is rounded to the nearest 0.00000001"); - - // Amount - int64 nAmount = 0; - if (params[0].get_real() != 0.0) - nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts - - nTransactionFee = nAmount; - return true; -} - -Value sendtoaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 4) - throw runtime_error( - "sendtoaddress [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001"); - - string strAddress = params[0].get_str(); - - // Amount - int64 nAmount = AmountFromValue(params[1]); - - // Wallet comments - CWalletTx wtx; - if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty()) - wtx.mapValue["comment"] = params[2].get_str(); - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) - wtx.mapValue["to"] = params[3].get_str(); - - CRITICAL_BLOCK(cs_main) - { - string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); - if (strError != "") - throw JSONRPCError(-4, strError); - } - - return wtx.GetHash().GetHex(); -} - - -Value getreceivedbyaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getreceivedbyaddress [minconf=1]\n" - "Returns the total amount received by in transactions with at least [minconf] confirmations."); - - // Bitcoin address - string strAddress = params[0].get_str(); - CScript scriptPubKey; - if (!scriptPubKey.SetBitcoinAddress(strAddress)) - throw JSONRPCError(-5, "Invalid bitcoin address"); - if (!IsMine(*pwalletMain,scriptPubKey)) - return (double)0.0; - - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - // Tally - int64 nAmount = 0; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; - } - } - - return ValueFromAmount(nAmount); -} - - -void GetAccountPubKeys(string strAccount, set& setPubKey) -{ - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) - { - const string& strAddress = item.first; - const string& strName = item.second; - if (strName == strAccount) - { - // We're only counting our own valid bitcoin addresses and not ip addresses - CScript scriptPubKey; - if (scriptPubKey.SetBitcoinAddress(strAddress)) - if (IsMine(*pwalletMain,scriptPubKey)) - setPubKey.insert(scriptPubKey); - } - } - } -} - - -Value getreceivedbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getreceivedbyaccount [minconf=1]\n" - "Returns the total amount received by addresses with in transactions with at least [minconf] confirmations."); - - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - // Get the set of pub keys that have the label - string strAccount = AccountFromValue(params[0]); - set setPubKey; - GetAccountPubKeys(strAccount, setPubKey); - - // Tally - int64 nAmount = 0; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (setPubKey.count(txout.scriptPubKey)) - if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; - } - } - - return (double)nAmount / (double)COIN; -} - - -int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) -{ - int64 nBalance = 0; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - // Tally wallet transactions - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!wtx.IsFinal()) - continue; - - int64 nGenerated, nReceived, nSent, nFee; - wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee); - - if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) - nBalance += nReceived; - nBalance += nGenerated - nSent - nFee; - } - - // Tally internal accounting entries - nBalance += walletdb.GetAccountCreditDebit(strAccount); - } - - return nBalance; -} - -int64 GetAccountBalance(const string& strAccount, int nMinDepth) -{ - CWalletDB walletdb(pwalletMain->strWalletFile); - return GetAccountBalance(walletdb, strAccount, nMinDepth); -} - - -Value getbalance(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 0 || params.size() > 2) - throw runtime_error( - "getbalance [account] [minconf=1]\n" - "If [account] is not specified, returns the server's total available balance.\n" - "If [account] is specified, returns the balance in the account."); - - if (params.size() == 0) - return ValueFromAmount(pwalletMain->GetBalance()); - - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - if (params[0].get_str() == "*") { - // Calculate total balance a different way from GetBalance() - // (GetBalance() sums up all unspent TxOuts) - // getbalance and getbalance '*' should always return the same number. - int64 nBalance = 0; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!wtx.IsFinal()) - continue; - - int64 allGeneratedImmature, allGeneratedMature, allFee; - allGeneratedImmature = allGeneratedMature = allFee = 0; - string strSentAccount; - list > listReceived; - list > listSent; - wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); - if (wtx.GetDepthInMainChain() >= nMinDepth) - BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) - nBalance += r.second; - BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listSent) - nBalance -= r.second; - nBalance -= allFee; - nBalance += allGeneratedMature; - } - return ValueFromAmount(nBalance); - } - - string strAccount = AccountFromValue(params[0]); - - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - - return ValueFromAmount(nBalance); -} - - -Value movecmd(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 3 || params.size() > 5) - throw runtime_error( - "move [minconf=1] [comment]\n" - "Move from one account in your wallet to another."); - - string strFrom = AccountFromValue(params[0]); - string strTo = AccountFromValue(params[1]); - int64 nAmount = AmountFromValue(params[2]); - int nMinDepth = 1; - if (params.size() > 3) - nMinDepth = params[3].get_int(); - string strComment; - if (params.size() > 4) - strComment = params[4].get_str(); - - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - CWalletDB walletdb(pwalletMain->strWalletFile); - walletdb.TxnBegin(); - - int64 nNow = GetAdjustedTime(); - - // Debit - CAccountingEntry debit; - debit.strAccount = strFrom; - debit.nCreditDebit = -nAmount; - debit.nTime = nNow; - debit.strOtherAccount = strTo; - debit.strComment = strComment; - walletdb.WriteAccountingEntry(debit); - - // Credit - CAccountingEntry credit; - credit.strAccount = strTo; - credit.nCreditDebit = nAmount; - credit.nTime = nNow; - credit.strOtherAccount = strFrom; - credit.strComment = strComment; - walletdb.WriteAccountingEntry(credit); - - walletdb.TxnCommit(); - } - return true; -} - - -Value sendfrom(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 3 || params.size() > 6) - throw runtime_error( - "sendfrom [minconf=1] [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001"); - - string strAccount = AccountFromValue(params[0]); - string strAddress = params[1].get_str(); - int64 nAmount = AmountFromValue(params[2]); - int nMinDepth = 1; - if (params.size() > 3) - nMinDepth = params[3].get_int(); - - CWalletTx wtx; - wtx.strFromAccount = strAccount; - if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) - wtx.mapValue["comment"] = params[4].get_str(); - if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) - wtx.mapValue["to"] = params[5].get_str(); - - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - // Check funds - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - if (nAmount > nBalance) - throw JSONRPCError(-6, "Account has insufficient funds"); - - // Send - string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); - if (strError != "") - throw JSONRPCError(-4, strError); - } - - return wtx.GetHash().GetHex(); -} - -Value sendmany(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 4) - throw runtime_error( - "sendmany {address:amount,...} [minconf=1] [comment]\n" - "amounts are double-precision floating point numbers"); - - string strAccount = AccountFromValue(params[0]); - Object sendTo = params[1].get_obj(); - int nMinDepth = 1; - if (params.size() > 2) - nMinDepth = params[2].get_int(); - - CWalletTx wtx; - wtx.strFromAccount = strAccount; - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) - wtx.mapValue["comment"] = params[3].get_str(); - - set setAddress; - vector > vecSend; - - int64 totalAmount = 0; - BOOST_FOREACH(const Pair& s, sendTo) - { - uint160 hash160; - string strAddress = s.name_; - - if (setAddress.count(strAddress)) - throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+strAddress); - setAddress.insert(strAddress); - - CScript scriptPubKey; - if (!scriptPubKey.SetBitcoinAddress(strAddress)) - throw JSONRPCError(-5, string("Invalid bitcoin address:")+strAddress); - int64 nAmount = AmountFromValue(s.value_); - totalAmount += nAmount; - - vecSend.push_back(make_pair(scriptPubKey, nAmount)); - } - - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - // Check funds - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - if (totalAmount > nBalance) - throw JSONRPCError(-6, "Account has insufficient funds"); - - // Send - CReserveKey keyChange(pwalletMain); - int64 nFeeRequired = 0; - bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); - if (!fCreated) - { - if (totalAmount + nFeeRequired > pwalletMain->GetBalance()) - throw JSONRPCError(-6, "Insufficient funds"); - throw JSONRPCError(-4, "Transaction creation failed"); - } - if (!pwalletMain->CommitTransaction(wtx, keyChange)) - throw JSONRPCError(-4, "Transaction commit failed"); - } - - return wtx.GetHash().GetHex(); -} - - -struct tallyitem -{ - int64 nAmount; - int nConf; - tallyitem() - { - nAmount = 0; - nConf = INT_MAX; - } -}; - -Value ListReceived(const Array& params, bool fByAccounts) -{ - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].get_int(); - - // Whether to include empty accounts - bool fIncludeEmpty = false; - if (params.size() > 1) - fIncludeEmpty = params[1].get_bool(); - - // Tally - map mapTally; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; - - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < nMinDepth) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - // Only counting our own bitcoin addresses and not ip addresses - uint160 hash160 = txout.scriptPubKey.GetBitcoinAddressHash160(); - if (hash160 == 0 || !mapPubKeys.count(hash160)) // IsMine - continue; - - tallyitem& item = mapTally[hash160]; - item.nAmount += txout.nValue; - item.nConf = min(item.nConf, nDepth); - } - } - } - - // Reply - Array ret; - map mapAccountTally; - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) - { - const string& strAddress = item.first; - const string& strAccount = item.second; - uint160 hash160; - if (!AddressToHash160(strAddress, hash160)) - continue; - map::iterator it = mapTally.find(hash160); - if (it == mapTally.end() && !fIncludeEmpty) - continue; - - int64 nAmount = 0; - int nConf = INT_MAX; - if (it != mapTally.end()) - { - nAmount = (*it).second.nAmount; - nConf = (*it).second.nConf; - } - - if (fByAccounts) - { - tallyitem& item = mapAccountTally[strAccount]; - item.nAmount += nAmount; - item.nConf = min(item.nConf, nConf); - } - else - { - Object obj; - obj.push_back(Pair("address", strAddress)); - obj.push_back(Pair("account", strAccount)); - obj.push_back(Pair("label", strAccount)); // deprecated - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); - ret.push_back(obj); - } - } - } - - if (fByAccounts) - { - for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) - { - int64 nAmount = (*it).second.nAmount; - int nConf = (*it).second.nConf; - Object obj; - obj.push_back(Pair("account", (*it).first)); - obj.push_back(Pair("label", (*it).first)); // deprecated - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); - ret.push_back(obj); - } - } - - return ret; -} - -Value listreceivedbyaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "listreceivedbyaddress [minconf=1] [includeempty=false]\n" - "[minconf] is the minimum number of confirmations before payments are included.\n" - "[includeempty] whether to include addresses that haven't received any payments.\n" - "Returns an array of objects containing:\n" - " \"address\" : receiving address\n" - " \"account\" : the account of the receiving address\n" - " \"amount\" : total amount received by the address\n" - " \"confirmations\" : number of confirmations of the most recent transaction included"); - - return ListReceived(params, false); -} - -Value listreceivedbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "listreceivedbyaccount [minconf=1] [includeempty=false]\n" - "[minconf] is the minimum number of confirmations before payments are included.\n" - "[includeempty] whether to include accounts that haven't received any payments.\n" - "Returns an array of objects containing:\n" - " \"account\" : the account of the receiving addresses\n" - " \"amount\" : total amount received by addresses with this account\n" - " \"confirmations\" : number of confirmations of the most recent transaction included"); - - return ListReceived(params, true); -} - -void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret) -{ - int64 nGeneratedImmature, nGeneratedMature, nFee; - string strSentAccount; - list > listReceived; - list > listSent; - wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); - - bool fAllAccounts = (strAccount == string("*")); - - // Generated blocks assigned to account "" - if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == "")) - { - Object entry; - entry.push_back(Pair("account", string(""))); - if (nGeneratedImmature) - { - entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan")); - entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature))); - } - else - { - entry.push_back(Pair("category", "generate")); - entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature))); - } - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } - - // Sent - if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) - { - BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) - { - Object entry; - entry.push_back(Pair("account", strSentAccount)); - entry.push_back(Pair("address", s.first)); - entry.push_back(Pair("category", "send")); - entry.push_back(Pair("amount", ValueFromAmount(-s.second))); - entry.push_back(Pair("fee", ValueFromAmount(-nFee))); - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } - } - - // Received - if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) - { - string account; - if (pwalletMain->mapAddressBook.count(r.first)) - account = pwalletMain->mapAddressBook[r.first]; - if (fAllAccounts || (account == strAccount)) - { - Object entry; - entry.push_back(Pair("account", account)); - entry.push_back(Pair("address", r.first)); - entry.push_back(Pair("category", "receive")); - entry.push_back(Pair("amount", ValueFromAmount(r.second))); - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } - } - } - -} - -void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) -{ - bool fAllAccounts = (strAccount == string("*")); - - if (fAllAccounts || acentry.strAccount == strAccount) - { - Object entry; - entry.push_back(Pair("account", acentry.strAccount)); - entry.push_back(Pair("category", "move")); - entry.push_back(Pair("time", (boost::int64_t)acentry.nTime)); - entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); - entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); - entry.push_back(Pair("comment", acentry.strComment)); - ret.push_back(entry); - } -} - -Value listtransactions(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 3) - throw runtime_error( - "listtransactions [account] [count=10] [from=0]\n" - "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account]."); - - string strAccount = "*"; - if (params.size() > 0) - strAccount = params[0].get_str(); - int nCount = 10; - if (params.size() > 1) - nCount = params[1].get_int(); - int nFrom = 0; - if (params.size() > 2) - nFrom = params[2].get_int(); - - Array ret; - CWalletDB walletdb(pwalletMain->strWalletFile); - - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap: - typedef pair TxPair; - typedef multimap TxItems; - TxItems txByTime; - - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - CWalletTx* wtx = &((*it).second); - txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0))); - } - list acentries; - walletdb.ListAccountCreditDebit(strAccount, acentries); - BOOST_FOREACH(CAccountingEntry& entry, acentries) - { - txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); - } - - // Now: iterate backwards until we have nCount items to return: - TxItems::reverse_iterator it = txByTime.rbegin(); - for (std::advance(it, nFrom); it != txByTime.rend(); ++it) - { - CWalletTx *const pwtx = (*it).second.first; - if (pwtx != 0) - ListTransactions(*pwtx, strAccount, 0, true, ret); - CAccountingEntry *const pacentry = (*it).second.second; - if (pacentry != 0) - AcentryToJSON(*pacentry, strAccount, ret); - - if (ret.size() >= nCount) break; - } - // ret is now newest to oldest - } - - // Make sure we return only last nCount items (sends-to-self might give us an extra): - if (ret.size() > nCount) - { - Array::iterator last = ret.begin(); - std::advance(last, nCount); - ret.erase(last, ret.end()); - } - std::reverse(ret.begin(), ret.end()); // oldest to newest - - return ret; -} - -Value listaccounts(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "listaccounts [minconf=1]\n" - "Returns Object that has account names as keys, account balances as values."); - - int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].get_int(); - - map mapAccountBalances; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, string)& entry, pwalletMain->mapAddressBook) { - uint160 hash160; - if(AddressToHash160(entry.first, hash160) && mapPubKeys.count(hash160)) // This address belongs to me - mapAccountBalances[entry.second] = 0; - } - - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - int64 nGeneratedImmature, nGeneratedMature, nFee; - string strSentAccount; - list > listReceived; - list > listSent; - wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); - mapAccountBalances[strSentAccount] -= nFee; - BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) - mapAccountBalances[strSentAccount] -= s.second; - if (wtx.GetDepthInMainChain() >= nMinDepth) - { - mapAccountBalances[""] += nGeneratedMature; - BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) - if (pwalletMain->mapAddressBook.count(r.first)) - mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second; - else - mapAccountBalances[""] += r.second; - } - } - } - - list acentries; - CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); - BOOST_FOREACH(const CAccountingEntry& entry, acentries) - mapAccountBalances[entry.strAccount] += entry.nCreditDebit; - - Object ret; - BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) { - ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); - } - return ret; -} - -Value gettransaction(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "gettransaction \n" - "Get detailed information about "); - - uint256 hash; - hash.SetHex(params[0].get_str()); - - Object entry; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - if (!pwalletMain->mapWallet.count(hash)) - throw JSONRPCError(-5, "Invalid or non-wallet transaction id"); - const CWalletTx& wtx = pwalletMain->mapWallet[hash]; - - int64 nCredit = wtx.GetCredit(); - int64 nDebit = wtx.GetDebit(); - int64 nNet = nCredit - nDebit; - int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); - - entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); - if (wtx.IsFromMe()) - entry.push_back(Pair("fee", ValueFromAmount(nFee))); - - WalletTxToJSON(pwalletMain->mapWallet[hash], entry); - - Array details; - ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details); - entry.push_back(Pair("details", details)); - } - - return entry; -} - - -Value backupwallet(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "backupwallet \n" - "Safely copies wallet.dat to destination, which can be a directory or a path with filename."); - - string strDest = params[0].get_str(); - BackupWallet(*pwalletMain, strDest); - - return Value::null; -} - - -Value validateaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "validateaddress \n" - "Return information about ."); - - string strAddress = params[0].get_str(); - uint160 hash160; - bool isValid = AddressToHash160(strAddress, hash160); - - Object ret; - ret.push_back(Pair("isvalid", isValid)); - if (isValid) - { - // Call Hash160ToAddress() so we always return current ADDRESSVERSION - // version of the address: - string currentAddress = Hash160ToAddress(hash160); - ret.push_back(Pair("address", currentAddress)); - ret.push_back(Pair("ismine", (mapPubKeys.count(hash160) > 0))); - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - if (pwalletMain->mapAddressBook.count(currentAddress)) - ret.push_back(Pair("account", pwalletMain->mapAddressBook[currentAddress])); - } - } - return ret; -} - - -Value getwork(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getwork [data]\n" - "If [data] is not specified, returns formatted hash data to work on:\n" - " \"midstate\" : precomputed hash state after hashing the first half of the data\n" - " \"data\" : block data\n" - " \"hash1\" : formatted hash buffer for second hash\n" - " \"target\" : little endian hash target\n" - "If [data] is specified, tries to solve the block and returns true if it was successful."); - - if (vNodes.empty()) - throw JSONRPCError(-9, "Bitcoin is not connected!"); - - if (IsInitialBlockDownload()) - throw JSONRPCError(-10, "Bitcoin is downloading blocks..."); - - static map > mapNewBlock; - static vector vNewBlock; - static CReserveKey reservekey(pwalletMain); - - if (params.size() == 0) - { - // Update block - static unsigned int nTransactionsUpdatedLast; - static CBlockIndex* pindexPrev; - static int64 nStart; - static CBlock* pblock; - if (pindexPrev != pindexBest || - (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) - { - if (pindexPrev != pindexBest) - { - // Deallocate old blocks since they're obsolete now - mapNewBlock.clear(); - BOOST_FOREACH(CBlock* pblock, vNewBlock) - delete pblock; - vNewBlock.clear(); - } - nTransactionsUpdatedLast = nTransactionsUpdated; - pindexPrev = pindexBest; - nStart = GetTime(); - - // Create new block - pblock = CreateNewBlock(reservekey); - if (!pblock) - throw JSONRPCError(-7, "Out of memory"); - vNewBlock.push_back(pblock); - } - - // Update nTime - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - pblock->nNonce = 0; - - // Update nExtraNonce - static unsigned int nExtraNonce = 0; - static int64 nPrevTime = 0; - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, nPrevTime); - - // Save - mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, nExtraNonce); - - // Prebuild hash buffers - char pmidstate[32]; - char pdata[128]; - char phash1[64]; - FormatHashBuffers(pblock, pmidstate, pdata, phash1); - - uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - - Object result; - result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); - result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); - result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); - result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); - return result; - } - else - { - // Parse parameters - vector vchData = ParseHex(params[0].get_str()); - if (vchData.size() != 128) - throw JSONRPCError(-8, "Invalid parameter"); - CBlock* pdata = (CBlock*)&vchData[0]; - - // Byte reverse - for (int i = 0; i < 128/4; i++) - ((unsigned int*)pdata)[i] = CryptoPP::ByteReverse(((unsigned int*)pdata)[i]); - - // Get saved block - if (!mapNewBlock.count(pdata->hashMerkleRoot)) - return false; - CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; - unsigned int nExtraNonce = mapNewBlock[pdata->hashMerkleRoot].second; - - pblock->nTime = pdata->nTime; - pblock->nNonce = pdata->nNonce; - pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce); - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - - return CheckWork(pblock, *pwalletMain, reservekey); - } -} - - - - - - - - - - - -// -// Call Table -// - -pair pCallTable[] = -{ - make_pair("help", &help), - make_pair("stop", &stop), - make_pair("getblockcount", &getblockcount), - make_pair("getblocknumber", &getblocknumber), - make_pair("getconnectioncount", &getconnectioncount), - make_pair("getdifficulty", &getdifficulty), - make_pair("getgenerate", &getgenerate), - make_pair("setgenerate", &setgenerate), - make_pair("gethashespersec", &gethashespersec), - make_pair("getinfo", &getinfo), - make_pair("getnewaddress", &getnewaddress), - make_pair("getaccountaddress", &getaccountaddress), - make_pair("setaccount", &setaccount), - make_pair("setlabel", &setaccount), // deprecated - make_pair("getaccount", &getaccount), - make_pair("getlabel", &getaccount), // deprecated - make_pair("getaddressesbyaccount", &getaddressesbyaccount), - make_pair("getaddressesbylabel", &getaddressesbyaccount), // deprecated - make_pair("sendtoaddress", &sendtoaddress), - make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress - make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress - make_pair("getreceivedbyaddress", &getreceivedbyaddress), - make_pair("getreceivedbyaccount", &getreceivedbyaccount), - make_pair("getreceivedbylabel", &getreceivedbyaccount), // deprecated - make_pair("listreceivedbyaddress", &listreceivedbyaddress), - make_pair("listreceivedbyaccount", &listreceivedbyaccount), - make_pair("listreceivedbylabel", &listreceivedbyaccount), // deprecated - make_pair("backupwallet", &backupwallet), - make_pair("validateaddress", &validateaddress), - make_pair("getbalance", &getbalance), - make_pair("move", &movecmd), - make_pair("sendfrom", &sendfrom), - make_pair("sendmany", &sendmany), - make_pair("gettransaction", &gettransaction), - make_pair("listtransactions", &listtransactions), - make_pair("getwork", &getwork), - make_pair("listaccounts", &listaccounts), - make_pair("settxfee", &settxfee), -}; -map mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0])); - -string pAllowInSafeMode[] = -{ - "help", - "stop", - "getblockcount", - "getblocknumber", - "getconnectioncount", - "getdifficulty", - "getgenerate", - "setgenerate", - "gethashespersec", - "getinfo", - "getnewaddress", - "getaccountaddress", - "setlabel", - "getaccount", - "getlabel", // deprecated - "getaddressesbyaccount", - "getaddressesbylabel", // deprecated - "backupwallet", - "validateaddress", - "getwork", -}; -set setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0])); - - - - -// -// HTTP protocol -// -// This ain't Apache. We're just using HTTP header for the length field -// and to be compatible with other JSON-RPC implementations. -// - -string HTTPPost(const string& strMsg, const map& mapRequestHeaders) -{ - ostringstream s; - s << "POST / HTTP/1.1\r\n" - << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n" - << "Host: 127.0.0.1\r\n" - << "Content-Type: application/json\r\n" - << "Content-Length: " << strMsg.size() << "\r\n" - << "Accept: application/json\r\n"; - BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders) - s << item.first << ": " << item.second << "\r\n"; - s << "\r\n" << strMsg; - - return s.str(); -} - -string rfc1123Time() -{ - char buffer[64]; - time_t now; - time(&now); - struct tm* now_gmt = gmtime(&now); - string locale(setlocale(LC_TIME, NULL)); - setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); - setlocale(LC_TIME, locale.c_str()); - return string(buffer); -} - -string HTTPReply(int nStatus, const string& strMsg) -{ - if (nStatus == 401) - return strprintf("HTTP/1.0 401 Authorization Required\r\n" - "Date: %s\r\n" - "Server: bitcoin-json-rpc/%s\r\n" - "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" - "Content-Type: text/html\r\n" - "Content-Length: 296\r\n" - "\r\n" - "\r\n" - "\r\n" - "\r\n" - "Error\r\n" - "\r\n" - "\r\n" - "

401 Unauthorized.

\r\n" - "\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); - string strStatus; - if (nStatus == 200) strStatus = "OK"; - else if (nStatus == 400) strStatus = "Bad Request"; - else if (nStatus == 404) strStatus = "Not Found"; - else if (nStatus == 500) strStatus = "Internal Server Error"; - return strprintf( - "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Connection: close\r\n" - "Content-Length: %d\r\n" - "Content-Type: application/json\r\n" - "Server: bitcoin-json-rpc/%s\r\n" - "\r\n" - "%s", - nStatus, - strStatus.c_str(), - rfc1123Time().c_str(), - strMsg.size(), - FormatFullVersion().c_str(), - strMsg.c_str()); -} - -int ReadHTTPStatus(std::basic_istream& stream) -{ - string str; - getline(stream, str); - vector vWords; - boost::split(vWords, str, boost::is_any_of(" ")); - if (vWords.size() < 2) - return 500; - return atoi(vWords[1].c_str()); -} - -int ReadHTTPHeader(std::basic_istream& stream, map& mapHeadersRet) -{ - int nLen = 0; - loop - { - string str; - std::getline(stream, str); - if (str.empty() || str == "\r") - break; - string::size_type nColon = str.find(":"); - if (nColon != string::npos) - { - string strHeader = str.substr(0, nColon); - boost::trim(strHeader); - boost::to_lower(strHeader); - string strValue = str.substr(nColon+1); - boost::trim(strValue); - mapHeadersRet[strHeader] = strValue; - if (strHeader == "content-length") - nLen = atoi(strValue.c_str()); - } - } - return nLen; -} - -int ReadHTTP(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet) -{ - mapHeadersRet.clear(); - strMessageRet = ""; - - // Read status - int nStatus = ReadHTTPStatus(stream); - - // Read header - int nLen = ReadHTTPHeader(stream, mapHeadersRet); - if (nLen < 0 || nLen > MAX_SIZE) - return 500; - - // Read message - if (nLen > 0) - { - vector vch(nLen); - stream.read(&vch[0], nLen); - strMessageRet = string(vch.begin(), vch.end()); - } - - return nStatus; -} - -string EncodeBase64(string s) -{ - BIO *b64, *bmem; - BUF_MEM *bptr; - - b64 = BIO_new(BIO_f_base64()); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_new(BIO_s_mem()); - b64 = BIO_push(b64, bmem); - BIO_write(b64, s.c_str(), s.size()); - BIO_flush(b64); - BIO_get_mem_ptr(b64, &bptr); - - string result(bptr->data, bptr->length); - BIO_free_all(b64); - - return result; -} - -string DecodeBase64(string s) -{ - BIO *b64, *bmem; - - char* buffer = static_cast(calloc(s.size(), sizeof(char))); - - b64 = BIO_new(BIO_f_base64()); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_new_mem_buf(const_cast(s.c_str()), s.size()); - bmem = BIO_push(b64, bmem); - BIO_read(bmem, buffer, s.size()); - BIO_free_all(bmem); - - string result(buffer); - free(buffer); - return result; -} - -bool HTTPAuthorized(map& mapHeaders) -{ - string strAuth = mapHeaders["authorization"]; - if (strAuth.substr(0,6) != "Basic ") - return false; - string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); - string strUserPass = DecodeBase64(strUserPass64); - string::size_type nColon = strUserPass.find(":"); - if (nColon == string::npos) - return false; - string strUser = strUserPass.substr(0, nColon); - string strPassword = strUserPass.substr(nColon+1); - return (strUser == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]); -} - -// -// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, -// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were -// unspecified (HTTP errors and contents of 'error'). -// -// 1.0 spec: http://json-rpc.org/wiki/specification -// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http -// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx -// - -string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) -{ - Object request; - request.push_back(Pair("method", strMethod)); - request.push_back(Pair("params", params)); - request.push_back(Pair("id", id)); - return write_string(Value(request), false) + "\n"; -} - -string JSONRPCReply(const Value& result, const Value& error, const Value& id) -{ - Object reply; - if (error.type() != null_type) - reply.push_back(Pair("result", Value::null)); - else - reply.push_back(Pair("result", result)); - reply.push_back(Pair("error", error)); - reply.push_back(Pair("id", id)); - return write_string(Value(reply), false) + "\n"; -} - -void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) -{ - // Send error reply from json-rpc error object - int nStatus = 500; - int code = find_value(objError, "code").get_int(); - if (code == -32600) nStatus = 400; - else if (code == -32601) nStatus = 404; - string strReply = JSONRPCReply(Value::null, objError, id); - stream << HTTPReply(nStatus, strReply) << std::flush; -} - -bool ClientAllowed(const string& strAddress) -{ - if (strAddress == asio::ip::address_v4::loopback().to_string()) - return true; - const vector& vAllow = mapMultiArgs["-rpcallowip"]; - BOOST_FOREACH(string strAllow, vAllow) - if (WildcardMatch(strAddress, strAllow)) - return true; - return false; -} - -#ifdef USE_SSL -// -// IOStream device that speaks SSL but can also speak non-SSL -// -class SSLIOStreamDevice : public iostreams::device { -public: - SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn) - { - fUseSSL = fUseSSLIn; - fNeedHandshake = fUseSSLIn; - } - - void handshake(ssl::stream_base::handshake_type role) - { - if (!fNeedHandshake) return; - fNeedHandshake = false; - stream.handshake(role); - } - std::streamsize read(char* s, std::streamsize n) - { - handshake(ssl::stream_base::server); // HTTPS servers read first - if (fUseSSL) return stream.read_some(asio::buffer(s, n)); - return stream.next_layer().read_some(asio::buffer(s, n)); - } - std::streamsize write(const char* s, std::streamsize n) - { - handshake(ssl::stream_base::client); // HTTPS clients write first - if (fUseSSL) return asio::write(stream, asio::buffer(s, n)); - return asio::write(stream.next_layer(), asio::buffer(s, n)); - } - bool connect(const std::string& server, const std::string& port) - { - ip::tcp::resolver resolver(stream.get_io_service()); - ip::tcp::resolver::query query(server.c_str(), port.c_str()); - ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); - ip::tcp::resolver::iterator end; - boost::system::error_code error = asio::error::host_not_found; - while (error && endpoint_iterator != end) - { - stream.lowest_layer().close(); - stream.lowest_layer().connect(*endpoint_iterator++, error); - } - if (error) - return false; - return true; - } - -private: - bool fNeedHandshake; - bool fUseSSL; - SSLStream& stream; -}; -#endif - -void ThreadRPCServer(void* parg) -{ - IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg)); - try - { - vnThreadsRunning[4]++; - ThreadRPCServer2(parg); - vnThreadsRunning[4]--; - } - catch (std::exception& e) { - vnThreadsRunning[4]--; - PrintException(&e, "ThreadRPCServer()"); - } catch (...) { - vnThreadsRunning[4]--; - PrintException(NULL, "ThreadRPCServer()"); - } - printf("ThreadRPCServer exiting\n"); -} - -void ThreadRPCServer2(void* parg) -{ - printf("ThreadRPCServer started\n"); - - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") - { - string strWhatAmI = "To use bitcoind"; - if (mapArgs.count("-server")) - strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); - else if (mapArgs.count("-daemon")) - strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); - PrintConsole( - _("Warning: %s, you must set rpcpassword=\nin the configuration file: %s\n" - "If the file does not exist, create it with owner-readable-only file permissions.\n"), - strWhatAmI.c_str(), - GetConfigFile().c_str()); - CreateThread(Shutdown, NULL); - return; - } - - bool fUseSSL = GetBoolArg("-rpcssl"); - asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); - - asio::io_service io_service; - ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332)); - ip::tcp::acceptor acceptor(io_service, endpoint); - - acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); - -#ifdef USE_SSL - ssl::context context(io_service, ssl::context::sslv23); - if (fUseSSL) - { - context.set_options(ssl::context::no_sslv2); - filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert"); - if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile; - if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str()); - else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str()); - filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem"); - if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile; - if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem); - else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str()); - - string ciphers = GetArg("-rpcsslciphers", - "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); - SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str()); - } -#else - if (fUseSSL) - throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); -#endif - - loop - { - // Accept connection -#ifdef USE_SSL - SSLStream sslStream(io_service, context); - SSLIOStreamDevice d(sslStream, fUseSSL); - iostreams::stream stream(d); -#else - ip::tcp::iostream stream; -#endif - - ip::tcp::endpoint peer; - vnThreadsRunning[4]--; -#ifdef USE_SSL - acceptor.accept(sslStream.lowest_layer(), peer); -#else - acceptor.accept(*stream.rdbuf(), peer); -#endif - vnThreadsRunning[4]++; - if (fShutdown) - return; - - // Restrict callers by IP - if (!ClientAllowed(peer.address().to_string())) - continue; - - map mapHeaders; - string strRequest; - - boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest)); - if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30)))) - { // Timed out: - acceptor.cancel(); - printf("ThreadRPCServer ReadHTTP timeout\n"); - continue; - } - - // Check authorization - if (mapHeaders.count("authorization") == 0) - { - stream << HTTPReply(401, "") << std::flush; - continue; - } - if (!HTTPAuthorized(mapHeaders)) - { - // Deter brute-forcing short passwords - if (mapArgs["-rpcpassword"].size() < 15) - Sleep(50); - - stream << HTTPReply(401, "") << std::flush; - printf("ThreadRPCServer incorrect password attempt\n"); - continue; - } - - Value id = Value::null; - try - { - // Parse request - Value valRequest; - if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type) - throw JSONRPCError(-32700, "Parse error"); - const Object& request = valRequest.get_obj(); - - // Parse id now so errors from here on will have the id - id = find_value(request, "id"); - - // Parse method - Value valMethod = find_value(request, "method"); - if (valMethod.type() == null_type) - throw JSONRPCError(-32600, "Missing method"); - if (valMethod.type() != str_type) - throw JSONRPCError(-32600, "Method must be a string"); - string strMethod = valMethod.get_str(); - if (strMethod != "getwork") - printf("ThreadRPCServer method=%s\n", strMethod.c_str()); - - // Parse params - Value valParams = find_value(request, "params"); - Array params; - if (valParams.type() == array_type) - params = valParams.get_array(); - else if (valParams.type() == null_type) - params = Array(); - else - throw JSONRPCError(-32600, "Params must be an array"); - - // Find method - map::iterator mi = mapCallTable.find(strMethod); - if (mi == mapCallTable.end()) - throw JSONRPCError(-32601, "Method not found"); - - // Observe safe mode - string strWarning = GetWarnings("rpc"); - if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod)) - throw JSONRPCError(-2, string("Safe mode: ") + strWarning); - - try - { - // Execute - Value result = (*(*mi).second)(params, false); - - // Send reply - string strReply = JSONRPCReply(result, Value::null, id); - stream << HTTPReply(200, strReply) << std::flush; - } - catch (std::exception& e) - { - ErrorReply(stream, JSONRPCError(-1, e.what()), id); - } - } - catch (Object& objError) - { - ErrorReply(stream, objError, id); - } - catch (std::exception& e) - { - ErrorReply(stream, JSONRPCError(-32700, e.what()), id); - } - } -} - - - - -Object CallRPC(const string& strMethod, const Array& params) -{ - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") - throw runtime_error(strprintf( - _("You must set rpcpassword= in the configuration file:\n%s\n" - "If the file does not exist, create it with owner-readable-only file permissions."), - GetConfigFile().c_str())); - - // Connect to localhost - bool fUseSSL = GetBoolArg("-rpcssl"); -#ifdef USE_SSL - asio::io_service io_service; - ssl::context context(io_service, ssl::context::sslv23); - context.set_options(ssl::context::no_sslv2); - SSLStream sslStream(io_service, context); - SSLIOStreamDevice d(sslStream, fUseSSL); - iostreams::stream stream(d); - if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"))) - throw runtime_error("couldn't connect to server"); -#else - if (fUseSSL) - throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); - - ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")); - if (stream.fail()) - throw runtime_error("couldn't connect to server"); -#endif - - - // HTTP basic authentication - string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); - map mapRequestHeaders; - mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; - - // Send request - string strRequest = JSONRPCRequest(strMethod, params, 1); - string strPost = HTTPPost(strRequest, mapRequestHeaders); - stream << strPost << std::flush; - - // Receive reply - map mapHeaders; - string strReply; - int nStatus = ReadHTTP(stream, mapHeaders, strReply); - if (nStatus == 401) - throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); - else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500) - throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); - else if (strReply.empty()) - throw runtime_error("no response from server"); - - // Parse reply - Value valReply; - if (!read_string(strReply, valReply)) - throw runtime_error("couldn't parse reply from server"); - const Object& reply = valReply.get_obj(); - if (reply.empty()) - throw runtime_error("expected reply to have result, error and id properties"); - - return reply; -} - - - - -template -void ConvertTo(Value& value) -{ - if (value.type() == str_type) - { - // reinterpret string as unquoted json value - Value value2; - if (!read_string(value.get_str(), value2)) - throw runtime_error("type mismatch"); - value = value2.get_value(); - } - else - { - value = value.get_value(); - } -} - -int CommandLineRPC(int argc, char *argv[]) -{ - string strPrint; - int nRet = 0; - try - { - // Skip switches - while (argc > 1 && IsSwitchChar(argv[1][0])) - { - argc--; - argv++; - } - - // Method - if (argc < 2) - throw runtime_error("too few parameters"); - string strMethod = argv[1]; - - // Parameters default to strings - Array params; - for (int i = 2; i < argc; i++) - params.push_back(argv[i]); - int n = params.size(); - - // - // Special case non-string parameter types - // - if (strMethod == "setgenerate" && n > 0) ConvertTo(params[0]); - if (strMethod == "setgenerate" && n > 1) ConvertTo(params[1]); - if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); - if (strMethod == "getamountreceived" && n > 1) ConvertTo(params[1]); // deprecated - if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo(params[1]); - if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated - if (strMethod == "getallreceived" && n > 0) ConvertTo(params[0]); // deprecated - if (strMethod == "getallreceived" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo(params[0]); - if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo(params[0]); - if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo(params[0]); // deprecated - if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated - if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); - if (strMethod == "move" && n > 2) ConvertTo(params[2]); - if (strMethod == "move" && n > 3) ConvertTo(params[3]); - if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]); - if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); - if (strMethod == "listtransactions" && n > 1) ConvertTo(params[1]); - if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); - if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); - if (strMethod == "sendmany" && n > 1) - { - string s = params[1].get_str(); - Value v; - if (!read_string(s, v) || v.type() != obj_type) - throw runtime_error("type mismatch"); - params[1] = v.get_obj(); - } - if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); - - // Execute - Object reply = CallRPC(strMethod, params); - - // Parse reply - const Value& result = find_value(reply, "result"); - const Value& error = find_value(reply, "error"); - const Value& id = find_value(reply, "id"); - - if (error.type() != null_type) - { - // Error - strPrint = "error: " + write_string(error, false); - int code = find_value(error.get_obj(), "code").get_int(); - nRet = abs(code); - } - else - { - // Result - if (result.type() == null_type) - strPrint = ""; - else if (result.type() == str_type) - strPrint = result.get_str(); - else - strPrint = write_string(result, true); - } - } - catch (std::exception& e) - { - strPrint = string("error: ") + e.what(); - nRet = 87; - } - catch (...) - { - PrintException(NULL, "CommandLineRPC()"); - } - - if (strPrint != "") - { -#if defined(__WXMSW__) && defined(GUI) - // Windows GUI apps can't print to command line, - // so settle for a message box yuck - MyMessageBox(strPrint, "Bitcoin", wxOK); -#else - fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); -#endif - } - return nRet; -} - - - - -#ifdef TEST -int main(int argc, char *argv[]) -{ -#ifdef _MSC_VER - // Turn off microsoft heap dump noise - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); -#endif - setbuf(stdin, NULL); - setbuf(stdout, NULL); - setbuf(stderr, NULL); - - try - { - if (argc >= 2 && string(argv[1]) == "-server") - { - printf("server ready\n"); - ThreadRPCServer(NULL); - } - else - { - return CommandLineRPC(argc, argv); - } - } - catch (std::exception& e) { - PrintException(&e, "main()"); - } catch (...) { - PrintException(NULL, "main()"); - } - return 0; -} -#endif diff --git a/src/rpc.h b/src/rpc.h deleted file mode 100644 index 48a7b8a8a6..0000000000 --- a/src/rpc.h +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. - -void ThreadRPCServer(void* parg); -int CommandLineRPC(int argc, char *argv[]); -- cgit v1.2.3 From 3913c387c98ae791db52703c19d41a9fecab9461 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 3 Jul 2011 20:59:56 +0200 Subject: Eliminate useless padding --- src/qt/transactionview.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 7bbe55001d..d024400fff 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -23,8 +23,10 @@ TransactionView::TransactionView(QWidget *parent) : transactionView(0) { // Build filter row + setContentsMargins(0,0,0,0); + QHBoxLayout *hlayout = new QHBoxLayout(); - hlayout->setContentsMargins(QMargins(0,0,0,0)); + hlayout->setContentsMargins(0,0,0,0); hlayout->setSpacing(0); hlayout->addSpacing(23); @@ -72,6 +74,8 @@ TransactionView::TransactionView(QWidget *parent) : hlayout->addWidget(amountWidget); QVBoxLayout *vlayout = new QVBoxLayout(this); + vlayout->setContentsMargins(0,0,0,0); + vlayout->setSpacing(0); QTableView *view = new QTableView(this); vlayout->addLayout(hlayout); -- cgit v1.2.3 From 9dfb2d28141dab60b5db59161e7f498ee19f9e3e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 4 Jul 2011 07:45:28 +0200 Subject: add windows build instructions --- README.rst | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9c1db1e6d2..458b28f8a8 100644 --- a/README.rst +++ b/README.rst @@ -39,9 +39,12 @@ This has to be done: - Build on Windows -Build instructions +Build instructions =================== +Debian +------- + First, make sure that the required packages for Qt4 development of your distribution are installed, for Debian and Ubuntu these are: @@ -62,6 +65,25 @@ Alternatively, install Qt Creator and open the `bitcoin-qt.pro` file. An executable named `bitcoin-qt` will be built. + +Windows +-------- + +Windows build instructions: + +- Download the `QT Windows SDK`_ and install it. You don't need the Symbian stuff, just the desktop Qt. + +- Download and extract the `dependencies archive`_ [#]_, or compile openssl, boost and dbcxx yourself. + +- Copy the contents of the folder "deps" to "X:\QtSDK\mingw", replace X:\ with the location where you installed the Qt SDK. Make sure that the contents of "deps/include" end up in the current "include" directory and such. + +- Open the .pro file in QT creator and build as normal (ctrl-B) + +.. _`QT Windows SDK`: http://qt.nokia.com/downloads/sdk-windows-cpp +.. _`dependencies archive`: http://download.visucore.com/bitcoin/qtgui_deps_1.zip +.. [#] PGP signature: http://download.visucore.com/bitcoin/qtgui_deps_1.zip.sig (signed with RSA key ID `610945D0`_) +.. _`610945D0`: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x610945D0 + Berkely DB version warning ========================== -- cgit v1.2.3 From b8f174a5ce24d9a5dc6742bd68bd93d0a4544c2a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 4 Jul 2011 20:12:58 +0200 Subject: as there is no "default receiving address" in this GUI, don't autogenerate new addresses on receiving --- src/wallet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet.cpp b/src/wallet.cpp index eab58aaafb..f896336773 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -91,7 +91,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) if (fInsertedNew || fUpdated) if (!wtx.WriteToDisk()) return false; - +#ifndef QT_GUI // If default receiving address gets used, replace it with a new one CScript scriptDefaultKey; scriptDefaultKey.SetBitcoinAddress(vchDefaultKey); @@ -107,7 +107,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); } } - +#endif // Notify UI vWalletUpdated.push_back(hash); -- cgit v1.2.3 From 825aa7d8d88251f89474e410e8631d0d75e8e898 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 5 Jul 2011 20:21:33 +0200 Subject: make balance selectable / copyable --- src/qt/bitcoingui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index f6b87c5459..cd9291ac60 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -78,6 +78,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): labelBalance = new QLabel(); labelBalance->setFont(QFont("Monospace", -1, QFont::Bold)); labelBalance->setToolTip(tr("Your current balance")); + labelBalance->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard); hbox_balance->addWidget(labelBalance); hbox_balance->addStretch(1); -- cgit v1.2.3 From 64c8b6994881ba2715cb08f48b406aaf1f28603c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 5 Jul 2011 22:09:39 +0200 Subject: tab reorg phase 1: split main gui into "overview" and "history" --- bitcoin-qt.pro | 9 ++++-- doc/assets-attribution.txt | 8 +++++- src/qt/bitcoin.qrc | 2 ++ src/qt/bitcoingui.cpp | 57 ++++++++++++++++++++++++++------------ src/qt/bitcoingui.h | 12 +++++++- src/qt/forms/overviewpage.ui | 63 ++++++++++++++++++++++++++++++++++++++++++ src/qt/overviewpage.cpp | 32 +++++++++++++++++++++ src/qt/overviewpage.h | 26 +++++++++++++++++ src/qt/res/icons/history.png | Bin 0 -> 746 bytes src/qt/res/icons/overview.png | Bin 0 -> 7015 bytes 10 files changed, 187 insertions(+), 22 deletions(-) create mode 100644 src/qt/forms/overviewpage.ui create mode 100644 src/qt/overviewpage.cpp create mode 100644 src/qt/overviewpage.h create mode 100644 src/qt/res/icons/history.png create mode 100644 src/qt/res/icons/overview.png diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 40c0ded6ce..a8705c369f 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -76,7 +76,8 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/transactionfilterproxy.h \ src/qt/transactionview.h \ src/qt/walletmodel.h \ - src/bitcoinrpc.h + src/bitcoinrpc.h \ + src/qt/overviewpage.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -112,7 +113,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactionfilterproxy.cpp \ src/qt/transactionview.cpp \ src/qt/walletmodel.cpp \ - src/bitcoinrpc.cpp + src/bitcoinrpc.cpp \ + src/qt/overviewpage.cpp RESOURCES += \ src/qt/bitcoin.qrc @@ -122,7 +124,8 @@ FORMS += \ src/qt/forms/addressbookdialog.ui \ src/qt/forms/aboutdialog.ui \ src/qt/forms/editaddressdialog.ui \ - src/qt/forms/transactiondescdialog.ui + src/qt/forms/transactiondescdialog.ui \ + src/qt/forms/overviewpage.ui CODECFORTR = UTF-8 TRANSLATIONS = src/qt/locale/bitcoin_nl.ts diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index f34c2c91b1..d4eb848820 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -34,7 +34,7 @@ Designer: http://www.everaldo.com Icon Pack: Crystal SVG License: LGPL -Icon: src/qt/res/icons/receive.png +Icon: src/qt/res/icons/receive.png, src/qt/res/icons/history.png Designer: Oxygen team Icon Pack: Oxygen License: Creative Common Attribution-ShareAlike 3.0 License or LGPL @@ -45,3 +45,9 @@ Designer: Bitboy (optimized for 16x16 by Wladimir van der Laan) License: Public Domain Site: http://forum.bitcoin.org/?topic=1756.0 +Icon: src/qt/res/icons/overview.png +Icon Pack: Primo +Designer: Jack Cai +License: Creative Commons Attribution No Derivatives (by-nd) +Site: http://findicons.com/icon/175944/home?id=176221# + diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 2dfc7dd987..20ecd9f302 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -26,6 +26,8 @@ res/icons/toolbar_testnet.png res/icons/edit.png res/icons/editdelete.png + res/icons/history.png + res/icons/overview.png res/images/about.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index cd9291ac60..bf4aa320f4 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -17,6 +17,7 @@ #include "transactiondescdialog.h" #include "addresstablemodel.h" #include "transactionview.h" +#include "overviewpage.h" #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include @@ -66,32 +68,28 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Toolbar QToolBar *toolbar = addToolBar("Main toolbar"); toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolbar->addAction(overviewAction); + toolbar->addAction(historyAction); + toolbar->addSeparator(); toolbar->addAction(sendCoins); toolbar->addAction(receiveCoins); toolbar->addAction(addressbook); - // Balance: - QHBoxLayout *hbox_balance = new QHBoxLayout(); - hbox_balance->addWidget(new QLabel(tr("Balance:"))); - hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */ - - labelBalance = new QLabel(); - labelBalance->setFont(QFont("Monospace", -1, QFont::Bold)); - labelBalance->setToolTip(tr("Your current balance")); - labelBalance->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard); - hbox_balance->addWidget(labelBalance); - hbox_balance->addStretch(1); - + overviewPage = new OverviewPage(); + QVBoxLayout *vbox = new QVBoxLayout(); - vbox->addLayout(hbox_balance); transactionView = new TransactionView(this); connect(transactionView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(transactionDetails(const QModelIndex&))); vbox->addWidget(transactionView); - QWidget *centralwidget = new QWidget(this); - centralwidget->setLayout(vbox); - setCentralWidget(centralwidget); + transactionsPage = new QWidget(this); + transactionsPage->setLayout(vbox); + + centralWidget = new QStackedWidget(this); + centralWidget->addWidget(overviewPage); + centralWidget->addWidget(transactionsPage); + setCentralWidget(centralWidget); // Create status bar statusBar(); @@ -125,10 +123,23 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): statusBar()->addPermanentWidget(labelTransactions); createTrayIcon(); + + gotoOverviewTab(); } void BitcoinGUI::createActions() { + QActionGroup *tabGroup = new QActionGroup(this); + overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Overview"), this); + overviewAction->setCheckable(true); + tabGroup->addAction(overviewAction); + historyAction = new QAction(QIcon(":/icons/history"), tr("&History"), this); + historyAction->setCheckable(true); + tabGroup->addAction(historyAction); + + connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewTab())); + connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryTab())); + quit = new QAction(QIcon(":/icons/quit"), tr("&Exit"), this); quit->setToolTip(tr("Quit application")); sendCoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); @@ -267,7 +278,7 @@ void BitcoinGUI::aboutClicked() void BitcoinGUI::setBalance(qint64 balance) { - labelBalance->setText(GUIUtil::formatMoney(balance) + QString(" BTC")); + overviewPage->setBalance(balance); } void BitcoinGUI::setNumConnections(int count) @@ -399,3 +410,15 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int QSystemTrayIcon::Information); } } + +void BitcoinGUI::gotoOverviewTab() +{ + overviewAction->setChecked(true); + centralWidget->setCurrentWidget(overviewPage); +} + +void BitcoinGUI::gotoHistoryTab() +{ + historyAction->setChecked(true); + centralWidget->setCurrentWidget(transactionsPage); +} diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 046186c562..432a9e3b04 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -8,6 +8,7 @@ class TransactionTableModel; class ClientModel; class WalletModel; class TransactionView; +class OverviewPage; QT_BEGIN_NAMESPACE class QLabel; @@ -16,6 +17,7 @@ class QTableView; class QAbstractItemModel; class QModelIndex; class QProgressBar; +class QStackedWidget; QT_END_NAMESPACE class BitcoinGUI : public QMainWindow @@ -42,7 +44,10 @@ private: ClientModel *clientModel; WalletModel *walletModel; - QLabel *labelBalance; + QStackedWidget *centralWidget; + OverviewPage *overviewPage; + QWidget *transactionsPage; + QLabel *labelConnections; QLabel *labelConnectionsIcon; QLabel *labelBlocks; @@ -50,6 +55,8 @@ private: QLabel *progressBarLabel; QProgressBar *progressBar; + QAction *overviewAction; + QAction *historyAction; QAction *quit; QAction *sendCoins; QAction *addressbook; @@ -86,6 +93,9 @@ private slots: void trayIconActivated(QSystemTrayIcon::ActivationReason reason); void transactionDetails(const QModelIndex& idx); void incomingTransaction(const QModelIndex & parent, int start, int end); + + void gotoOverviewTab(); + void gotoHistoryTab(); }; #endif diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui new file mode 100644 index 0000000000..ef9c07306f --- /dev/null +++ b/src/qt/forms/overviewpage.ui @@ -0,0 +1,63 @@ + + + OverviewPage + + + + 0 + 0 + 552 + 342 + + + + Form + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Balance + + + + + + + 123.456 BTC + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp new file mode 100644 index 0000000000..faed29866a --- /dev/null +++ b/src/qt/overviewpage.cpp @@ -0,0 +1,32 @@ +#include "overviewpage.h" +#include "ui_overviewpage.h" + +#include "guiutil.h" + +OverviewPage::OverviewPage(QWidget *parent) : + QWidget(parent), + ui(new Ui::OverviewPage) +{ + ui->setupUi(this); + + // Balance: + ui->labelBalance->setFont(QFont("Monospace", -1, QFont::Bold)); + ui->labelBalance->setToolTip(tr("Your current balance")); + ui->labelBalance->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard); + + // Overview page should show: + // Balance + // Unconfirmed balance + // Last received transaction(s) + // Last sent transaction(s) +} + +OverviewPage::~OverviewPage() +{ + delete ui; +} + +void OverviewPage::setBalance(qint64 balance) +{ + ui->labelBalance->setText(GUIUtil::formatMoney(balance) + QString(" BTC")); +} diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h new file mode 100644 index 0000000000..85b927b83f --- /dev/null +++ b/src/qt/overviewpage.h @@ -0,0 +1,26 @@ +#ifndef OVERVIEWPAGE_H +#define OVERVIEWPAGE_H + +#include + +namespace Ui { + class OverviewPage; +} + +class OverviewPage : public QWidget +{ + Q_OBJECT + +public: + explicit OverviewPage(QWidget *parent = 0); + ~OverviewPage(); + +public slots: + void setBalance(qint64 balance); + +private: + Ui::OverviewPage *ui; + +}; + +#endif // OVERVIEWPAGE_H diff --git a/src/qt/res/icons/history.png b/src/qt/res/icons/history.png new file mode 100644 index 0000000000..60f1351783 Binary files /dev/null and b/src/qt/res/icons/history.png differ diff --git a/src/qt/res/icons/overview.png b/src/qt/res/icons/overview.png new file mode 100644 index 0000000000..6b94b43a2c Binary files /dev/null and b/src/qt/res/icons/overview.png differ -- cgit v1.2.3 From e1f3d64c4a95893ec623fd082c82f443ae868fcd Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 6 Jul 2011 20:41:13 +0200 Subject: Add "BTC" to all amount widgets, to make clear what the unit is --- src/qt/bitcoinamountfield.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index a4cbbe0a7e..f16c0c51e5 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -25,6 +25,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): layout->addWidget(amount); layout->addWidget(new QLabel(QString("."))); layout->addWidget(decimals); + layout->addWidget(new QLabel(QString(" BTC"))); layout->addStretch(1); layout->setContentsMargins(0,0,0,0); -- cgit v1.2.3 From 8bca4099c712b816ec859b39571b7c555b33be49 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 6 Jul 2011 20:52:31 +0200 Subject: Remove code for no longer existing edit button in address book dialog --- src/qt/addressbookdialog.cpp | 21 --------------------- src/qt/addressbookdialog.h | 1 - 2 files changed, 22 deletions(-) diff --git a/src/qt/addressbookdialog.cpp b/src/qt/addressbookdialog.cpp index 5a744aec48..220679c9e6 100644 --- a/src/qt/addressbookdialog.cpp +++ b/src/qt/addressbookdialog.cpp @@ -104,27 +104,6 @@ void AddressBookDialog::on_copyToClipboard_clicked() } } -void AddressBookDialog::on_editButton_clicked() -{ - QModelIndexList indexes = getCurrentTable()->selectionModel()->selectedRows(); - if(indexes.isEmpty()) - { - return; - } - // Map selected index to source address book model - QAbstractProxyModel *proxy_model = static_cast(getCurrentTable()->model()); - QModelIndex selected = proxy_model->mapToSource(indexes.at(0)); - - // Double click also triggers edit button - EditAddressDialog dlg( - ui->tabWidget->currentIndex() == SendingTab ? - EditAddressDialog::EditSendingAddress : - EditAddressDialog::EditReceivingAddress); - dlg.setModel(model); - dlg.loadRow(selected.row()); - dlg.exec(); -} - void AddressBookDialog::on_newAddressButton_clicked() { EditAddressDialog dlg( diff --git a/src/qt/addressbookdialog.h b/src/qt/addressbookdialog.h index b032e554d6..fe243a62ca 100644 --- a/src/qt/addressbookdialog.h +++ b/src/qt/addressbookdialog.h @@ -46,7 +46,6 @@ private slots: void on_deleteButton_clicked(); void on_tabWidget_currentChanged(int index); void on_newAddressButton_clicked(); - void on_editButton_clicked(); void on_copyToClipboard_clicked(); }; -- cgit v1.2.3 From 393adf7acd8bfacaccb8b9543ce4d2b442608124 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 6 Jul 2011 21:52:23 +0200 Subject: Address book: Disable "copy to clipboard" and "Delete" buttons when nothing selected --- src/qt/addressbookdialog.cpp | 47 ++++++++++++++++++++++++++++++-------------- src/qt/addressbookdialog.h | 3 ++- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/qt/addressbookdialog.cpp b/src/qt/addressbookdialog.cpp index 220679c9e6..6d53ee8657 100644 --- a/src/qt/addressbookdialog.cpp +++ b/src/qt/addressbookdialog.cpp @@ -23,6 +23,8 @@ AddressBookDialog::AddressBookDialog(Mode mode, QWidget *parent) : ui->sendTableView->setFocus(); break; } + + connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(selectionChanged())); } AddressBookDialog::~AddressBookDialog() @@ -64,6 +66,11 @@ void AddressBookDialog::setModel(AddressTableModel *model) ui->sendTableView->horizontalHeader()->setResizeMode( AddressTableModel::Label, QHeaderView::Stretch); + connect(ui->receiveTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(selectionChanged())); + connect(ui->sendTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(selectionChanged())); + if(mode == ForSending) { // Auto-select first row when in sending mode @@ -74,7 +81,7 @@ void AddressBookDialog::setModel(AddressTableModel *model) void AddressBookDialog::setTab(int tab) { ui->tabWidget->setCurrentIndex(tab); - on_tabWidget_currentChanged(tab); + selectionChanged(); } QTableView *AddressBookDialog::getCurrentTable() @@ -114,20 +121,6 @@ void AddressBookDialog::on_newAddressButton_clicked() dlg.exec(); } -void AddressBookDialog::on_tabWidget_currentChanged(int index) -{ - // Enable/disable buttons based on selected tab - switch(index) - { - case SendingTab: - ui->deleteButton->setEnabled(true); - break; - case ReceivingTab: - ui->deleteButton->setEnabled(false); - break; - } -} - void AddressBookDialog::on_deleteButton_clicked() { QTableView *table = getCurrentTable(); @@ -158,3 +151,27 @@ void AddressBookDialog::on_buttonBox_accepted() } } +void AddressBookDialog::selectionChanged() +{ + // Set button states based on selected tab and selection + QTableView *table = getCurrentTable(); + + if(table->selectionModel()->hasSelection()) + { + switch(ui->tabWidget->currentIndex()) + { + case SendingTab: + ui->deleteButton->setEnabled(true); + break; + case ReceivingTab: + ui->deleteButton->setEnabled(false); + break; + } + ui->copyToClipboard->setEnabled(true); + } + else + { + ui->deleteButton->setEnabled(false); + ui->copyToClipboard->setEnabled(false); + } +} diff --git a/src/qt/addressbookdialog.h b/src/qt/addressbookdialog.h index fe243a62ca..befa8b7659 100644 --- a/src/qt/addressbookdialog.h +++ b/src/qt/addressbookdialog.h @@ -10,6 +10,7 @@ class AddressTableModel; QT_BEGIN_NAMESPACE class QTableView; +class QItemSelection; QT_END_NAMESPACE class AddressBookDialog : public QDialog @@ -44,9 +45,9 @@ private: private slots: void on_buttonBox_accepted(); void on_deleteButton_clicked(); - void on_tabWidget_currentChanged(int index); void on_newAddressButton_clicked(); void on_copyToClipboard_clicked(); + void selectionChanged(); }; #endif // ADDRESSBOOKDIALOG_H -- cgit v1.2.3 From e59924680340427b658d1bb37dc38028f6fa6317 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 10:29:07 +0200 Subject: Improve view of generated transactions (show clock icon when still maturing) --- src/qt/transactiontablemodel.cpp | 97 +++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 37 deletions(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 0d0d97bbde..65e1632399 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -275,15 +275,34 @@ QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) con status = tr("Open until %1").arg(GUIUtil::DateTimeStr(wtx->status.open_for)); break; case TransactionStatus::Offline: - status = tr("Offline (%1)").arg(wtx->status.depth); + status = tr("Offline (%1 confirmations)").arg(wtx->status.depth); break; case TransactionStatus::Unconfirmed: - status = tr("Unconfirmed (%1/%2)").arg(wtx->status.depth).arg(TransactionRecord::NumConfirmations); + status = tr("Unconfirmed (%1/%2 confirmations)").arg(wtx->status.depth).arg(TransactionRecord::NumConfirmations); break; case TransactionStatus::HaveConfirmations: - status = tr("Confirmed (%1)").arg(wtx->status.depth); + status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth); break; } + if(wtx->type == TransactionRecord::Generated) + { + status += "\n"; + switch(wtx->status.maturity) + { + case TransactionStatus::Immature: + status += tr("Generation matures in %n more blocks", "", + wtx->status.matures_in); + break; + case TransactionStatus::Mature: + break; + case TransactionStatus::MaturesWarning: + status += tr("This block was not received by any other nodes and will probably not be accepted!"); + break; + case TransactionStatus::NotAccepted: + status += tr("Generated but not accepted"); + break; + } + } return QVariant(status); } @@ -369,22 +388,7 @@ QVariant TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx) description = QString(); break; case TransactionRecord::Generated: - switch(wtx->status.maturity) - { - case TransactionStatus::Immature: - description = tr("(matures in %n more blocks)", "", - wtx->status.matures_in); - break; - case TransactionStatus::Mature: - description = QString(); - break; - case TransactionStatus::MaturesWarning: - description = tr("(Warning: This block was not received by any other nodes and will probably not be accepted!)"); - break; - case TransactionStatus::NotAccepted: - description = tr("(not accepted)"); - break; - } + description = QString(); break; } return QVariant(description); @@ -402,26 +406,45 @@ QVariant TransactionTableModel::formatTxAmount(const TransactionRecord *wtx) con QVariant TransactionTableModel::formatTxDecoration(const TransactionRecord *wtx) const { - switch(wtx->status.status) + if(wtx->type == TransactionRecord::Generated) { - case TransactionStatus::OpenUntilBlock: - case TransactionStatus::OpenUntilDate: - return QColor(64,64,255); - break; - case TransactionStatus::Offline: - return QColor(192,192,192); - case TransactionStatus::Unconfirmed: - switch(wtx->status.depth) + switch(wtx->status.maturity) { - case 0: return QIcon(":/icons/transaction_0"); - case 1: return QIcon(":/icons/transaction_1"); - case 2: return QIcon(":/icons/transaction_2"); - case 3: return QIcon(":/icons/transaction_3"); - case 4: return QIcon(":/icons/transaction_4"); - default: return QIcon(":/icons/transaction_5"); - }; - case TransactionStatus::HaveConfirmations: - return QIcon(":/icons/transaction_confirmed"); + case TransactionStatus::Immature: { + int total = wtx->status.depth + wtx->status.matures_in; + int part = (wtx->status.depth * 4 / total) + 1; + return QIcon(QString(":/icons/transaction_%1").arg(part)); + } + case TransactionStatus::Mature: + return QIcon(":/icons/transaction_confirmed"); + case TransactionStatus::MaturesWarning: + case TransactionStatus::NotAccepted: + return QIcon(":/icons/transaction_0"); + } + } + else + { + switch(wtx->status.status) + { + case TransactionStatus::OpenUntilBlock: + case TransactionStatus::OpenUntilDate: + return QColor(64,64,255); + break; + case TransactionStatus::Offline: + return QColor(192,192,192); + case TransactionStatus::Unconfirmed: + switch(wtx->status.depth) + { + case 0: return QIcon(":/icons/transaction_0"); + case 1: return QIcon(":/icons/transaction_1"); + case 2: return QIcon(":/icons/transaction_2"); + case 3: return QIcon(":/icons/transaction_3"); + case 4: return QIcon(":/icons/transaction_4"); + default: return QIcon(":/icons/transaction_5"); + }; + case TransactionStatus::HaveConfirmations: + return QIcon(":/icons/transaction_confirmed"); + } } return QColor(0,0,0); } -- cgit v1.2.3 From fac047480df29f1bde3b73899019d43e854b4c4e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 10:43:04 +0200 Subject: minor language/text updates --- src/qt/bitcoingui.cpp | 7 ++++--- src/qt/transactiontablemodel.cpp | 6 +++--- src/qt/transactionview.cpp | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bf4aa320f4..3ac87262aa 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -47,7 +47,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): trayIcon(0) { resize(850, 550); - setWindowTitle(tr("Bitcoin")); + setWindowTitle(tr("Bitcoin Wallet")); setWindowIcon(QIcon(":icons/bitcoin")); createActions(); @@ -170,11 +170,12 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) if(clientModel->isTestNet()) { - setWindowTitle(tr("Bitcoin [testnet]")); + QString title_testnet = tr("Bitcoin Wallet [testnet]"); + setWindowTitle(title_testnet); setWindowIcon(QIcon(":icons/bitcoin_testnet")); if(trayIcon) { - trayIcon->setToolTip(tr("Bitcoin [testnet]")); + trayIcon->setToolTip(title_testnet); trayIcon->setIcon(QIcon(":/icons/toolbar_testnet")); } } diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 65e1632399..70dbd0ffec 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -286,11 +286,11 @@ QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) con } if(wtx->type == TransactionRecord::Generated) { - status += "\n"; + status += "\n\n"; switch(wtx->status.maturity) { case TransactionStatus::Immature: - status += tr("Generation matures in %n more blocks", "", + status += tr("Mined balance will be available in %n more blocks", "", wtx->status.matures_in); break; case TransactionStatus::Mature: @@ -360,7 +360,7 @@ QVariant TransactionTableModel::formatTxType(const TransactionRecord *wtx) const description = tr("Payment to yourself"); break; case TransactionRecord::Generated: - description = tr("Generated"); + description = tr("Mined"); break; } return QVariant(description); diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index d024400fff..a3407e85de 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -53,7 +53,7 @@ TransactionView::TransactionView(QWidget *parent) : typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) | TransactionFilterProxy::TYPE(TransactionRecord::SendToIP)); typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf)); - typeWidget->addItem(tr("Generated"), TransactionFilterProxy::TYPE(TransactionRecord::Generated)); + typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated)); typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other)); hlayout->addWidget(typeWidget); -- cgit v1.2.3 From d52a0f3bca2c8df8360308b062185d803e34f0d9 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 10:59:00 +0200 Subject: Rename "History" tab to more logical "Transactions", move "Number of transactions" from status bar to overview page --- src/qt/bitcoingui.cpp | 10 ++-------- src/qt/bitcoingui.h | 1 - src/qt/forms/overviewpage.ui | 22 +++++++++++++++++++++- src/qt/overviewpage.cpp | 5 +++++ src/qt/overviewpage.h | 1 + 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 3ac87262aa..fbab51bd6b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -104,11 +104,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): labelBlocks->setMinimumWidth(130); labelBlocks->setToolTip(tr("Number of blocks in the block chain")); - labelTransactions = new QLabel(); - labelTransactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelTransactions->setMinimumWidth(130); - labelTransactions->setToolTip(tr("Number of transactions in your wallet")); - // Progress bar for blocks download progressBarLabel = new QLabel(tr("Synchronizing with network...")); progressBarLabel->setVisible(false); @@ -120,7 +115,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): statusBar()->addWidget(progressBar); statusBar()->addPermanentWidget(labelConnections); statusBar()->addPermanentWidget(labelBlocks); - statusBar()->addPermanentWidget(labelTransactions); createTrayIcon(); @@ -133,7 +127,7 @@ void BitcoinGUI::createActions() overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Overview"), this); overviewAction->setCheckable(true); tabGroup->addAction(overviewAction); - historyAction = new QAction(QIcon(":/icons/history"), tr("&History"), this); + historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this); historyAction->setCheckable(true); tabGroup->addAction(historyAction); @@ -318,7 +312,7 @@ void BitcoinGUI::setNumBlocks(int count) void BitcoinGUI::setNumTransactions(int count) { - labelTransactions->setText(tr("%n transaction(s)", "", count)); + overviewPage->setNumTransactions(count); } void BitcoinGUI::error(const QString &title, const QString &message) diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 432a9e3b04..e04bcf915c 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -51,7 +51,6 @@ private: QLabel *labelConnections; QLabel *labelConnectionsIcon; QLabel *labelBlocks; - QLabel *labelTransactions; QLabel *progressBarLabel; QProgressBar *progressBar; diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index ef9c07306f..4cb28d5bf7 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -26,10 +26,16 @@ QFormLayout::AllNonFixedFieldsGrow + + 12 + + + 12 + - Balance + Balance: @@ -40,6 +46,20 @@
+ + + + Number of transactions: + + + + + + + 0 + + +
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index faed29866a..87f95fddf3 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -30,3 +30,8 @@ void OverviewPage::setBalance(qint64 balance) { ui->labelBalance->setText(GUIUtil::formatMoney(balance) + QString(" BTC")); } + +void OverviewPage::setNumTransactions(int count) +{ + ui->labelNumTransactions->setText(QLocale::system().toString(count)); +} diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 85b927b83f..fbd6853b37 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -17,6 +17,7 @@ public: public slots: void setBalance(qint64 balance); + void setNumTransactions(int count); private: Ui::OverviewPage *ui; -- cgit v1.2.3 From fbaee7a8533b23d846ee16837320f709c4e83d47 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 14:27:16 +0200 Subject: Export functionality for transaction list --- bitcoin-qt.pro | 6 ++- doc/assets-attribution.txt | 3 +- src/qt/bitcoin.qrc | 1 + src/qt/bitcoingui.cpp | 21 +++++++++- src/qt/bitcoingui.h | 2 + src/qt/csvmodelwriter.cpp | 83 ++++++++++++++++++++++++++++++++++++++++ src/qt/csvmodelwriter.h | 43 +++++++++++++++++++++ src/qt/transactionrecord.cpp | 6 +++ src/qt/transactionrecord.h | 3 ++ src/qt/transactiontablemodel.cpp | 21 ++++++++-- src/qt/transactiontablemodel.h | 13 +++++-- src/qt/transactionview.cpp | 40 ++++++++++++++++++- src/qt/transactionview.h | 1 + 13 files changed, 230 insertions(+), 13 deletions(-) create mode 100644 src/qt/csvmodelwriter.cpp create mode 100644 src/qt/csvmodelwriter.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index a8705c369f..77d70b7ba7 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -77,7 +77,8 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/transactionview.h \ src/qt/walletmodel.h \ src/bitcoinrpc.h \ - src/qt/overviewpage.h + src/qt/overviewpage.h \ + src/qt/csvmodelwriter.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -114,7 +115,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactionview.cpp \ src/qt/walletmodel.cpp \ src/bitcoinrpc.cpp \ - src/qt/overviewpage.cpp + src/qt/overviewpage.cpp \ + src/qt/csvmodelwriter.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index d4eb848820..786427f202 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -34,7 +34,8 @@ Designer: http://www.everaldo.com Icon Pack: Crystal SVG License: LGPL -Icon: src/qt/res/icons/receive.png, src/qt/res/icons/history.png +Icon: src/qt/res/icons/receive.png, src/qt/res/icons/history.png, + src/qt/res/icons/export.png Designer: Oxygen team Icon Pack: Oxygen License: Creative Common Attribution-ShareAlike 3.0 License or LGPL diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 20ecd9f302..e64744cda8 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -28,6 +28,7 @@ res/icons/editdelete.png res/icons/history.png res/icons/overview.png + res/icons/export.png res/images/about.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index fbab51bd6b..34da1350cd 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -75,8 +75,12 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): toolbar->addAction(receiveCoins); toolbar->addAction(addressbook); - overviewPage = new OverviewPage(); + QToolBar *toolbar2 = addToolBar("Transactions toolbar"); + toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolbar2->addAction(exportAction); + // Overview page + overviewPage = new OverviewPage(); QVBoxLayout *vbox = new QVBoxLayout(); transactionView = new TransactionView(this); @@ -146,8 +150,10 @@ void BitcoinGUI::createActions() receiveCoins->setToolTip(tr("Show the list of addresses for receiving payments")); options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); options->setToolTip(tr("Modify configuration options for bitcoin")); - openBitcoin = new QAction(QIcon(":/icons/bitcoin"), "Open &Bitcoin", this); + openBitcoin = new QAction(QIcon(":/icons/bitcoin"), tr("Open &Bitcoin"), this); openBitcoin->setToolTip(tr("Show the Bitcoin window")); + exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this); + exportAction->setToolTip(tr("Export data in current view to a file")); connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); connect(sendCoins, SIGNAL(triggered()), this, SLOT(sendCoinsClicked())); @@ -156,6 +162,7 @@ void BitcoinGUI::createActions() connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); connect(openBitcoin, SIGNAL(triggered()), this, SLOT(show())); + connect(exportAction, SIGNAL(triggered()), this, SLOT(exportClicked())); } void BitcoinGUI::setClientModel(ClientModel *clientModel) @@ -410,10 +417,20 @@ void BitcoinGUI::gotoOverviewTab() { overviewAction->setChecked(true); centralWidget->setCurrentWidget(overviewPage); + exportAction->setEnabled(false); } void BitcoinGUI::gotoHistoryTab() { historyAction->setChecked(true); centralWidget->setCurrentWidget(transactionsPage); + exportAction->setEnabled(true); +} + +void BitcoinGUI::exportClicked() +{ + // Redirect to the right view, as soon as export for other views + // (such as address book) is implemented. + transactionView->exportClicked(); } + diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index e04bcf915c..a5fcc8a83a 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -63,6 +63,7 @@ private: QAction *receiveCoins; QAction *options; QAction *openBitcoin; + QAction *exportAction; QSystemTrayIcon *trayIcon; TransactionView *transactionView; @@ -92,6 +93,7 @@ private slots: void trayIconActivated(QSystemTrayIcon::ActivationReason reason); void transactionDetails(const QModelIndex& idx); void incomingTransaction(const QModelIndex & parent, int start, int end); + void exportClicked(); void gotoOverviewTab(); void gotoHistoryTab(); diff --git a/src/qt/csvmodelwriter.cpp b/src/qt/csvmodelwriter.cpp new file mode 100644 index 0000000000..62c0b949aa --- /dev/null +++ b/src/qt/csvmodelwriter.cpp @@ -0,0 +1,83 @@ +#include "csvmodelwriter.h" + +#include +#include +#include + +CSVModelWriter::CSVModelWriter(const QString &filename, QObject *parent) : + QObject(parent), + filename(filename) +{ +} + +void CSVModelWriter::setModel(const QAbstractItemModel *model) +{ + this->model = model; +} + +void CSVModelWriter::addColumn(const QString &title, int column, int role) +{ + Column col; + col.title = title; + col.column = column; + col.role = role; + + columns.append(col); +} + +static void writeValue(QTextStream &f, const QString &value) +{ + // TODO: quoting if " or \n in string + f << "\"" << value << "\""; +} + +static void writeSep(QTextStream &f) +{ + f << ","; +} + +static void writeNewline(QTextStream &f) +{ + f << "\n"; +} + +bool CSVModelWriter::write() +{ + QFile file(filename); + if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) + return false; + QTextStream out(&file); + + int numRows = model->rowCount(); + + // Header row + for(int i=0; iindex(j, columns[i].column).data(columns[i].role); + writeValue(out, data.toString()); + } + writeNewline(out); + } + + file.close(); + + return file.error() == QFile::NoError; +} + diff --git a/src/qt/csvmodelwriter.h b/src/qt/csvmodelwriter.h new file mode 100644 index 0000000000..7367f3a6a9 --- /dev/null +++ b/src/qt/csvmodelwriter.h @@ -0,0 +1,43 @@ +#ifndef CSVMODELWRITER_H +#define CSVMODELWRITER_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QAbstractItemModel; +QT_END_NAMESPACE + +// Export TableModel to CSV file +class CSVModelWriter : public QObject +{ + Q_OBJECT +public: + explicit CSVModelWriter(const QString &filename, QObject *parent = 0); + + void setModel(const QAbstractItemModel *model); + void addColumn(const QString &title, int column, int role=Qt::EditRole); + + // Perform write operation + // Returns true on success, false otherwise + bool write(); + +private: + QString filename; + const QAbstractItemModel *model; + + struct Column + { + QString title; + int column; + int role; + }; + QList columns; + +signals: + +public slots: + +}; + +#endif // CSVMODELWRITER_H diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 864dffa99e..1b527bc7d3 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -254,3 +254,9 @@ bool TransactionRecord::statusUpdateNeeded() { return status.cur_num_blocks != nBestHeight; } + +std::string TransactionRecord::getTxID() +{ + return hash.ToString() + strprintf("-%03d", idx); +} + diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index c196be2a9e..0050c878ee 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -103,6 +103,9 @@ public: /* Status: can change with block chain update */ TransactionStatus status; + /* Return the unique identifier for this transaction (part) */ + std::string getTxID(); + /* Update status from wallet tx. */ void updateStatus(const CWalletTx &wtx); diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 70dbd0ffec..52c9b0ce84 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -394,12 +394,15 @@ QVariant TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx) return QVariant(description); } -QVariant TransactionTableModel::formatTxAmount(const TransactionRecord *wtx) const +QVariant TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const { QString str = QString::fromStdString(FormatMoney(wtx->credit + wtx->debit)); - if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) + if(showUnconfirmed) { - str = QString("[") + str + QString("]"); + if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) + { + str = QString("[") + str + QString("]"); + } } return QVariant(str); } @@ -541,6 +544,18 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { return llabs(rec->credit + rec->debit); } + else if (role == TxIDRole) + { + return QString::fromStdString(rec->getTxID()); + } + else if (role == ConfirmedRole) + { + return rec->status.status == TransactionStatus::HaveConfirmations; + } + else if (role == FormattedAmountRole) + { + return formatTxAmount(rec, false); + } return QVariant(); } diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index f75f414d4c..85bfeebcb3 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -25,6 +25,7 @@ public: } ColumnIndex; // Roles to get specific information from a transaction row + // These are independent of column enum { // Type of transaction TypeRole = Qt::UserRole, @@ -36,8 +37,14 @@ public: AddressRole, // Label of address related to transaction LabelRole, - // Absolute net amount of transaction - AbsoluteAmountRole + // Absolute net amount of transaction, for filtering + AbsoluteAmountRole, + // Unique identifier + TxIDRole, + // Is transaction confirmed? + ConfirmedRole, + // Formatted amount, without brackets when unconfirmed + FormattedAmountRole } RoleIndex; int rowCount(const QModelIndex &parent) const; @@ -57,7 +64,7 @@ private: QVariant formatTxDate(const TransactionRecord *wtx) const; QVariant formatTxType(const TransactionRecord *wtx) const; QVariant formatTxToAddress(const TransactionRecord *wtx) const; - QVariant formatTxAmount(const TransactionRecord *wtx) const; + QVariant formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true) const; QVariant formatTxDecoration(const TransactionRecord *wtx) const; private slots: diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index a3407e85de..037dfbb3ed 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -1,11 +1,10 @@ #include "transactionview.h" -// Temp includes for filtering prototype -// Move to TransactionFilterRow class #include "transactionfilterproxy.h" #include "transactionrecord.h" #include "transactiontablemodel.h" #include "guiutil.h" +#include "csvmodelwriter.h" #include #include @@ -15,6 +14,9 @@ #include #include #include +#include +#include +#include #include @@ -76,6 +78,7 @@ TransactionView::TransactionView(QWidget *parent) : QVBoxLayout *vlayout = new QVBoxLayout(this); vlayout->setContentsMargins(0,0,0,0); vlayout->setSpacing(0); + //vlayout->addLayout(hlayout2); QTableView *view = new QTableView(this); vlayout->addLayout(hlayout); @@ -196,3 +199,36 @@ void TransactionView::changedAmount(const QString &amount) transactionProxyModel->setMinAmount(0); } } + +void TransactionView::exportClicked() +{ + // CSV is currently the only supported format + QString filename = QFileDialog::getSaveFileName( + this, + tr("Export Transaction Data"), + QDir::currentPath(), + tr("Comma separated file (*.csv)")); + if(!filename.endsWith(".csv")) + { + filename += ".csv"; + } + + CSVModelWriter writer(filename); + + // name, column, role + writer.setModel(transactionProxyModel); + writer.addColumn("Confirmed", 0, TransactionTableModel::ConfirmedRole); + writer.addColumn("Date", 0, TransactionTableModel::DateRole); + writer.addColumn("Type", TransactionTableModel::Type, Qt::EditRole); + writer.addColumn("Label", 0, TransactionTableModel::LabelRole); + writer.addColumn("Address", 0, TransactionTableModel::AddressRole); + writer.addColumn("Amount", 0, TransactionTableModel::FormattedAmountRole); + writer.addColumn("ID", 0, TransactionTableModel::TxIDRole); + + 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/transactionview.h b/src/qt/transactionview.h index fe0f154b8e..25212c976e 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -50,6 +50,7 @@ public slots: void chooseType(int idx); void changedPrefix(const QString &prefix); void changedAmount(const QString &amount); + void exportClicked(); }; -- cgit v1.2.3 From 33c75fd9aaafe12a518900d72685b5360f567638 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 15:16:26 +0200 Subject: CKeyStore::AddKey must return a boolean --- src/keystore.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/keystore.cpp b/src/keystore.cpp index 7dd045fe5f..bfad27c6d3 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -29,5 +29,6 @@ bool CKeyStore::AddKey(const CKey& key) mapKeys[key.GetPubKey()] = key.GetPrivKey(); mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey(); } + return true; } -- cgit v1.2.3 From 42c405ad2340c11c769643ab8aee5e6ab118d2a1 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 18 Jun 2011 18:42:13 +0200 Subject: temp patch for qtui --- src/bitcoinrpc.cpp | 2226 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/bitcoinrpc.h | 6 + src/headers.h | 4 + src/init.cpp | 9 +- src/net.h | 1 + src/qtui.h | 48 ++ src/rpc.cpp | 2226 ---------------------------------------------------- src/rpc.h | 6 - src/util.cpp | 10 +- src/util.h | 10 +- src/wallet.cpp | 4 +- 11 files changed, 2299 insertions(+), 2251 deletions(-) create mode 100644 src/bitcoinrpc.cpp create mode 100644 src/bitcoinrpc.h create mode 100644 src/qtui.h delete mode 100644 src/rpc.cpp delete mode 100644 src/rpc.h diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp new file mode 100644 index 0000000000..0493c6a08f --- /dev/null +++ b/src/bitcoinrpc.cpp @@ -0,0 +1,2226 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" +#include "cryptopp/sha.h" +#include "db.h" +#include "net.h" +#include "init.h" +#undef printf +#include +#include +#include +#include +#ifdef USE_SSL +#include +#include +#include +typedef boost::asio::ssl::stream SSLStream; +#endif +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_writer_template.h" +#include "json/json_spirit_utils.h" +#define printf OutputDebugStringF +// MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are +// precompiled in headers.h. The problem might be when the pch file goes over +// a certain size around 145MB. If we need access to json_spirit outside this +// file, we could use the compiled json_spirit option. + +using namespace std; +using namespace boost; +using namespace boost::asio; +using namespace json_spirit; + +void ThreadRPCServer2(void* parg); +typedef Value(*rpcfn_type)(const Array& params, bool fHelp); +extern map mapCallTable; + + +Object JSONRPCError(int code, const string& message) +{ + Object error; + error.push_back(Pair("code", code)); + error.push_back(Pair("message", message)); + return error; +} + + +void PrintConsole(const std::string &format, ...) +{ + char buffer[50000]; + int limit = sizeof(buffer); + va_list arg_ptr; + va_start(arg_ptr, format); + int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + ret = limit - 1; + buffer[limit-1] = 0; + } + printf("%s", buffer); +#if defined(__WXMSW__) && defined(GUI) + MyMessageBox(buffer, "Bitcoin", wxOK | wxICON_EXCLAMATION); +#else + fprintf(stdout, "%s", buffer); +#endif +} + + +int64 AmountFromValue(const Value& value) +{ + double dAmount = value.get_real(); + if (dAmount <= 0.0 || dAmount > 21000000.0) + throw JSONRPCError(-3, "Invalid amount"); + int64 nAmount = roundint64(dAmount * COIN); + if (!MoneyRange(nAmount)) + throw JSONRPCError(-3, "Invalid amount"); + return nAmount; +} + +Value ValueFromAmount(int64 amount) +{ + return (double)amount / (double)COIN; +} + +void WalletTxToJSON(const CWalletTx& wtx, Object& entry) +{ + entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain())); + entry.push_back(Pair("txid", wtx.GetHash().GetHex())); + entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); + BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) + entry.push_back(Pair(item.first, item.second)); +} + +string AccountFromValue(const Value& value) +{ + string strAccount = value.get_str(); + if (strAccount == "*") + throw JSONRPCError(-11, "Invalid account name"); + return strAccount; +} + + + +/// +/// Note: This interface may still be subject to change. +/// + + +Value help(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "help [command]\n" + "List commands, or get help for a command."); + + string strCommand; + if (params.size() > 0) + strCommand = params[0].get_str(); + + string strRet; + set setDone; + for (map::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi) + { + string strMethod = (*mi).first; + // We already filter duplicates, but these deprecated screw up the sort order + if (strMethod == "getamountreceived" || + strMethod == "getallreceived" || + (strMethod.find("label") != string::npos)) + continue; + if (strCommand != "" && strMethod != strCommand) + continue; + try + { + Array params; + rpcfn_type pfn = (*mi).second; + if (setDone.insert(pfn).second) + (*pfn)(params, true); + } + catch (std::exception& e) + { + // Help text is returned in an exception + string strHelp = string(e.what()); + if (strCommand == "") + if (strHelp.find('\n') != -1) + strHelp = strHelp.substr(0, strHelp.find('\n')); + strRet += strHelp + "\n"; + } + } + if (strRet == "") + strRet = strprintf("help: unknown command: %s\n", strCommand.c_str()); + strRet = strRet.substr(0,strRet.size()-1); + return strRet; +} + + +Value stop(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "stop\n" + "Stop bitcoin server."); + + // Shutdown will take long enough that the response should get back + CreateThread(Shutdown, NULL); + return "bitcoin server stopping"; +} + + +Value getblockcount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getblockcount\n" + "Returns the number of blocks in the longest block chain."); + + return nBestHeight; +} + + +Value getblocknumber(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getblocknumber\n" + "Returns the block number of the latest block in the longest block chain."); + + return nBestHeight; +} + + +Value getconnectioncount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getconnectioncount\n" + "Returns the number of connections to other nodes."); + + return (int)vNodes.size(); +} + + +double GetDifficulty() +{ + // Floating point number that is a multiple of the minimum difficulty, + // minimum difficulty = 1.0. + + if (pindexBest == NULL) + return 1.0; + int nShift = (pindexBest->nBits >> 24) & 0xff; + + double dDiff = + (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff); + + while (nShift < 29) + { + dDiff *= 256.0; + nShift++; + } + while (nShift > 29) + { + dDiff /= 256.0; + nShift--; + } + + return dDiff; +} + +Value getdifficulty(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getdifficulty\n" + "Returns the proof-of-work difficulty as a multiple of the minimum difficulty."); + + return GetDifficulty(); +} + + +Value getgenerate(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getgenerate\n" + "Returns true or false."); + + return (bool)fGenerateBitcoins; +} + + +Value setgenerate(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "setgenerate [genproclimit]\n" + " is true or false to turn generation on or off.\n" + "Generation is limited to [genproclimit] processors, -1 is unlimited."); + + bool fGenerate = true; + if (params.size() > 0) + fGenerate = params[0].get_bool(); + + if (params.size() > 1) + { + int nGenProcLimit = params[1].get_int(); + fLimitProcessors = (nGenProcLimit != -1); + WriteSetting("fLimitProcessors", fLimitProcessors); + if (nGenProcLimit != -1) + WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit); + if (nGenProcLimit == 0) + fGenerate = false; + } + + GenerateBitcoins(fGenerate, pwalletMain); + return Value::null; +} + + +Value gethashespersec(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "gethashespersec\n" + "Returns a recent hashes per second performance measurement while generating."); + + if (GetTimeMillis() - nHPSTimerStart > 8000) + return (boost::int64_t)0; + return (boost::int64_t)dHashesPerSec; +} + + +Value getinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getinfo\n" + "Returns an object containing various state info."); + + Object obj; + obj.push_back(Pair("version", (int)VERSION)); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("connections", (int)vNodes.size())); + obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string()))); + obj.push_back(Pair("generate", (bool)fGenerateBitcoins)); + obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1))); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("hashespersec", gethashespersec(params, false))); + obj.push_back(Pair("testnet", fTestNet)); + obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + return obj; +} + + +Value getnewaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getnewaddress [account]\n" + "Returns a new bitcoin address for receiving payments. " + "If [account] is specified (recommended), it is added to the address book " + "so payments received with the address will be credited to [account]."); + + // Parse the account first so we don't generate a key if there's an error + string strAccount; + if (params.size() > 0) + strAccount = AccountFromValue(params[0]); + + // Generate a new key that is added to wallet + string strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool()); + + // This could be done in the same main CS as GetKeyFromKeyPool. + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + pwalletMain->SetAddressBookName(strAddress, strAccount); + + return strAddress; +} + + +// requires cs_main, cs_mapWallet, cs_mapAddressBook locks +string GetAccountAddress(string strAccount, bool bForceNew=false) +{ + string strAddress; + + CWalletDB walletdb(pwalletMain->strWalletFile); + walletdb.TxnBegin(); + + CAccount account; + walletdb.ReadAccount(strAccount, account); + + // Check if the current key has been used + if (!account.vchPubKey.empty()) + { + CScript scriptPubKey; + scriptPubKey.SetBitcoinAddress(account.vchPubKey); + for (map::iterator it = pwalletMain->mapWallet.begin(); + it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty(); + ++it) + { + const CWalletTx& wtx = (*it).second; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + account.vchPubKey.clear(); + } + } + + // Generate a new key + if (account.vchPubKey.empty() || bForceNew) + { + account.vchPubKey = pwalletMain->GetKeyFromKeyPool(); + string strAddress = PubKeyToAddress(account.vchPubKey); + pwalletMain->SetAddressBookName(strAddress, strAccount); + walletdb.WriteAccount(strAccount, account); + } + + walletdb.TxnCommit(); + strAddress = PubKeyToAddress(account.vchPubKey); + + return strAddress; +} + +Value getaccountaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaccountaddress \n" + "Returns the current bitcoin address for receiving payments to this account."); + + // Parse the account first so we don't generate a key if there's an error + string strAccount = AccountFromValue(params[0]); + + Value ret; + + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + ret = GetAccountAddress(strAccount); + } + + return ret; +} + + + +Value setaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "setaccount \n" + "Sets the account associated with the given address."); + + string strAddress = params[0].get_str(); + uint160 hash160; + bool isValid = AddressToHash160(strAddress, hash160); + if (!isValid) + throw JSONRPCError(-5, "Invalid bitcoin address"); + + + string strAccount; + if (params.size() > 1) + strAccount = AccountFromValue(params[1]); + + // Detect when changing the account of an address that is the 'unused current key' of another account: + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + if (pwalletMain->mapAddressBook.count(strAddress)) + { + string strOldAccount = pwalletMain->mapAddressBook[strAddress]; + if (strAddress == GetAccountAddress(strOldAccount)) + GetAccountAddress(strOldAccount, true); + } + + pwalletMain->SetAddressBookName(strAddress, strAccount); + } + + return Value::null; +} + + +Value getaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaccount \n" + "Returns the account associated with the given address."); + + string strAddress = params[0].get_str(); + + string strAccount; + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + map::iterator mi = pwalletMain->mapAddressBook.find(strAddress); + if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty()) + strAccount = (*mi).second; + } + return strAccount; +} + + +Value getaddressesbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaddressesbyaccount \n" + "Returns the list of addresses for the given account."); + + string strAccount = AccountFromValue(params[0]); + + // Find all addresses that have the given account + Array ret; + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) + { + const string& strAddress = item.first; + const string& strName = item.second; + if (strName == strAccount) + { + // We're only adding valid bitcoin addresses and not ip addresses + CScript scriptPubKey; + if (scriptPubKey.SetBitcoinAddress(strAddress)) + ret.push_back(strAddress); + } + } + } + return ret; +} + +Value settxfee(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "settxfee \n" + " is a real and is rounded to the nearest 0.00000001"); + + // Amount + int64 nAmount = 0; + if (params[0].get_real() != 0.0) + nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts + + nTransactionFee = nAmount; + return true; +} + +Value sendtoaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "sendtoaddress [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.00000001"); + + string strAddress = params[0].get_str(); + + // Amount + int64 nAmount = AmountFromValue(params[1]); + + // Wallet comments + CWalletTx wtx; + if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty()) + wtx.mapValue["comment"] = params[2].get_str(); + if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) + wtx.mapValue["to"] = params[3].get_str(); + + CRITICAL_BLOCK(cs_main) + { + string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); + if (strError != "") + throw JSONRPCError(-4, strError); + } + + return wtx.GetHash().GetHex(); +} + + +Value getreceivedbyaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getreceivedbyaddress [minconf=1]\n" + "Returns the total amount received by in transactions with at least [minconf] confirmations."); + + // Bitcoin address + string strAddress = params[0].get_str(); + CScript scriptPubKey; + if (!scriptPubKey.SetBitcoinAddress(strAddress)) + throw JSONRPCError(-5, "Invalid bitcoin address"); + if (!IsMine(*pwalletMain,scriptPubKey)) + return (double)0.0; + + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + // Tally + int64 nAmount = 0; + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + } + + return ValueFromAmount(nAmount); +} + + +void GetAccountPubKeys(string strAccount, set& setPubKey) +{ + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) + { + const string& strAddress = item.first; + const string& strName = item.second; + if (strName == strAccount) + { + // We're only counting our own valid bitcoin addresses and not ip addresses + CScript scriptPubKey; + if (scriptPubKey.SetBitcoinAddress(strAddress)) + if (IsMine(*pwalletMain,scriptPubKey)) + setPubKey.insert(scriptPubKey); + } + } + } +} + + +Value getreceivedbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getreceivedbyaccount [minconf=1]\n" + "Returns the total amount received by addresses with in transactions with at least [minconf] confirmations."); + + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + // Get the set of pub keys that have the label + string strAccount = AccountFromValue(params[0]); + set setPubKey; + GetAccountPubKeys(strAccount, setPubKey); + + // Tally + int64 nAmount = 0; + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (setPubKey.count(txout.scriptPubKey)) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + } + + return (double)nAmount / (double)COIN; +} + + +int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) +{ + int64 nBalance = 0; + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + // Tally wallet transactions + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!wtx.IsFinal()) + continue; + + int64 nGenerated, nReceived, nSent, nFee; + wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee); + + if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) + nBalance += nReceived; + nBalance += nGenerated - nSent - nFee; + } + + // Tally internal accounting entries + nBalance += walletdb.GetAccountCreditDebit(strAccount); + } + + return nBalance; +} + +int64 GetAccountBalance(const string& strAccount, int nMinDepth) +{ + CWalletDB walletdb(pwalletMain->strWalletFile); + return GetAccountBalance(walletdb, strAccount, nMinDepth); +} + + +Value getbalance(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 0 || params.size() > 2) + throw runtime_error( + "getbalance [account] [minconf=1]\n" + "If [account] is not specified, returns the server's total available balance.\n" + "If [account] is specified, returns the balance in the account."); + + if (params.size() == 0) + return ValueFromAmount(pwalletMain->GetBalance()); + + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + if (params[0].get_str() == "*") { + // Calculate total balance a different way from GetBalance() + // (GetBalance() sums up all unspent TxOuts) + // getbalance and getbalance '*' should always return the same number. + int64 nBalance = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!wtx.IsFinal()) + continue; + + int64 allGeneratedImmature, allGeneratedMature, allFee; + allGeneratedImmature = allGeneratedMature = allFee = 0; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); + if (wtx.GetDepthInMainChain() >= nMinDepth) + BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) + nBalance += r.second; + BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listSent) + nBalance -= r.second; + nBalance -= allFee; + nBalance += allGeneratedMature; + } + return ValueFromAmount(nBalance); + } + + string strAccount = AccountFromValue(params[0]); + + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + + return ValueFromAmount(nBalance); +} + + +Value movecmd(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 5) + throw runtime_error( + "move [minconf=1] [comment]\n" + "Move from one account in your wallet to another."); + + string strFrom = AccountFromValue(params[0]); + string strTo = AccountFromValue(params[1]); + int64 nAmount = AmountFromValue(params[2]); + int nMinDepth = 1; + if (params.size() > 3) + nMinDepth = params[3].get_int(); + string strComment; + if (params.size() > 4) + strComment = params[4].get_str(); + + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + CWalletDB walletdb(pwalletMain->strWalletFile); + walletdb.TxnBegin(); + + int64 nNow = GetAdjustedTime(); + + // Debit + CAccountingEntry debit; + debit.strAccount = strFrom; + debit.nCreditDebit = -nAmount; + debit.nTime = nNow; + debit.strOtherAccount = strTo; + debit.strComment = strComment; + walletdb.WriteAccountingEntry(debit); + + // Credit + CAccountingEntry credit; + credit.strAccount = strTo; + credit.nCreditDebit = nAmount; + credit.nTime = nNow; + credit.strOtherAccount = strFrom; + credit.strComment = strComment; + walletdb.WriteAccountingEntry(credit); + + walletdb.TxnCommit(); + } + return true; +} + + +Value sendfrom(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 6) + throw runtime_error( + "sendfrom [minconf=1] [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.00000001"); + + string strAccount = AccountFromValue(params[0]); + string strAddress = params[1].get_str(); + int64 nAmount = AmountFromValue(params[2]); + int nMinDepth = 1; + if (params.size() > 3) + nMinDepth = params[3].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) + wtx.mapValue["comment"] = params[4].get_str(); + if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) + wtx.mapValue["to"] = params[5].get_str(); + + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + // Check funds + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + if (nAmount > nBalance) + throw JSONRPCError(-6, "Account has insufficient funds"); + + // Send + string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); + if (strError != "") + throw JSONRPCError(-4, strError); + } + + return wtx.GetHash().GetHex(); +} + +Value sendmany(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "sendmany {address:amount,...} [minconf=1] [comment]\n" + "amounts are double-precision floating point numbers"); + + string strAccount = AccountFromValue(params[0]); + Object sendTo = params[1].get_obj(); + int nMinDepth = 1; + if (params.size() > 2) + nMinDepth = params[2].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) + wtx.mapValue["comment"] = params[3].get_str(); + + set setAddress; + vector > vecSend; + + int64 totalAmount = 0; + BOOST_FOREACH(const Pair& s, sendTo) + { + uint160 hash160; + string strAddress = s.name_; + + if (setAddress.count(strAddress)) + throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+strAddress); + setAddress.insert(strAddress); + + CScript scriptPubKey; + if (!scriptPubKey.SetBitcoinAddress(strAddress)) + throw JSONRPCError(-5, string("Invalid bitcoin address:")+strAddress); + int64 nAmount = AmountFromValue(s.value_); + totalAmount += nAmount; + + vecSend.push_back(make_pair(scriptPubKey, nAmount)); + } + + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + // Check funds + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + if (totalAmount > nBalance) + throw JSONRPCError(-6, "Account has insufficient funds"); + + // Send + CReserveKey keyChange(pwalletMain); + int64 nFeeRequired = 0; + bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); + if (!fCreated) + { + if (totalAmount + nFeeRequired > pwalletMain->GetBalance()) + throw JSONRPCError(-6, "Insufficient funds"); + throw JSONRPCError(-4, "Transaction creation failed"); + } + if (!pwalletMain->CommitTransaction(wtx, keyChange)) + throw JSONRPCError(-4, "Transaction commit failed"); + } + + return wtx.GetHash().GetHex(); +} + + +struct tallyitem +{ + int64 nAmount; + int nConf; + tallyitem() + { + nAmount = 0; + nConf = INT_MAX; + } +}; + +Value ListReceived(const Array& params, bool fByAccounts) +{ + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + // Whether to include empty accounts + bool fIncludeEmpty = false; + if (params.size() > 1) + fIncludeEmpty = params[1].get_bool(); + + // Tally + map mapTally; + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + int nDepth = wtx.GetDepthInMainChain(); + if (nDepth < nMinDepth) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + // Only counting our own bitcoin addresses and not ip addresses + uint160 hash160 = txout.scriptPubKey.GetBitcoinAddressHash160(); + if (hash160 == 0 || !mapPubKeys.count(hash160)) // IsMine + continue; + + tallyitem& item = mapTally[hash160]; + item.nAmount += txout.nValue; + item.nConf = min(item.nConf, nDepth); + } + } + } + + // Reply + Array ret; + map mapAccountTally; + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) + { + const string& strAddress = item.first; + const string& strAccount = item.second; + uint160 hash160; + if (!AddressToHash160(strAddress, hash160)) + continue; + map::iterator it = mapTally.find(hash160); + if (it == mapTally.end() && !fIncludeEmpty) + continue; + + int64 nAmount = 0; + int nConf = INT_MAX; + if (it != mapTally.end()) + { + nAmount = (*it).second.nAmount; + nConf = (*it).second.nConf; + } + + if (fByAccounts) + { + tallyitem& item = mapAccountTally[strAccount]; + item.nAmount += nAmount; + item.nConf = min(item.nConf, nConf); + } + else + { + Object obj; + obj.push_back(Pair("address", strAddress)); + obj.push_back(Pair("account", strAccount)); + obj.push_back(Pair("label", strAccount)); // deprecated + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); + ret.push_back(obj); + } + } + } + + if (fByAccounts) + { + for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) + { + int64 nAmount = (*it).second.nAmount; + int nConf = (*it).second.nConf; + Object obj; + obj.push_back(Pair("account", (*it).first)); + obj.push_back(Pair("label", (*it).first)); // deprecated + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); + ret.push_back(obj); + } + } + + return ret; +} + +Value listreceivedbyaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "listreceivedbyaddress [minconf=1] [includeempty=false]\n" + "[minconf] is the minimum number of confirmations before payments are included.\n" + "[includeempty] whether to include addresses that haven't received any payments.\n" + "Returns an array of objects containing:\n" + " \"address\" : receiving address\n" + " \"account\" : the account of the receiving address\n" + " \"amount\" : total amount received by the address\n" + " \"confirmations\" : number of confirmations of the most recent transaction included"); + + return ListReceived(params, false); +} + +Value listreceivedbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "listreceivedbyaccount [minconf=1] [includeempty=false]\n" + "[minconf] is the minimum number of confirmations before payments are included.\n" + "[includeempty] whether to include accounts that haven't received any payments.\n" + "Returns an array of objects containing:\n" + " \"account\" : the account of the receiving addresses\n" + " \"amount\" : total amount received by addresses with this account\n" + " \"confirmations\" : number of confirmations of the most recent transaction included"); + + return ListReceived(params, true); +} + +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret) +{ + int64 nGeneratedImmature, nGeneratedMature, nFee; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); + + bool fAllAccounts = (strAccount == string("*")); + + // Generated blocks assigned to account "" + if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == "")) + { + Object entry; + entry.push_back(Pair("account", string(""))); + if (nGeneratedImmature) + { + entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan")); + entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature))); + } + else + { + entry.push_back(Pair("category", "generate")); + entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature))); + } + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + + // Sent + if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) + { + BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) + { + Object entry; + entry.push_back(Pair("account", strSentAccount)); + entry.push_back(Pair("address", s.first)); + entry.push_back(Pair("category", "send")); + entry.push_back(Pair("amount", ValueFromAmount(-s.second))); + entry.push_back(Pair("fee", ValueFromAmount(-nFee))); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + + // Received + if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) + { + string account; + if (pwalletMain->mapAddressBook.count(r.first)) + account = pwalletMain->mapAddressBook[r.first]; + if (fAllAccounts || (account == strAccount)) + { + Object entry; + entry.push_back(Pair("account", account)); + entry.push_back(Pair("address", r.first)); + entry.push_back(Pair("category", "receive")); + entry.push_back(Pair("amount", ValueFromAmount(r.second))); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + } + +} + +void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) +{ + bool fAllAccounts = (strAccount == string("*")); + + if (fAllAccounts || acentry.strAccount == strAccount) + { + Object entry; + entry.push_back(Pair("account", acentry.strAccount)); + entry.push_back(Pair("category", "move")); + entry.push_back(Pair("time", (boost::int64_t)acentry.nTime)); + entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); + entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); + entry.push_back(Pair("comment", acentry.strComment)); + ret.push_back(entry); + } +} + +Value listtransactions(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 3) + throw runtime_error( + "listtransactions [account] [count=10] [from=0]\n" + "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account]."); + + string strAccount = "*"; + if (params.size() > 0) + strAccount = params[0].get_str(); + int nCount = 10; + if (params.size() > 1) + nCount = params[1].get_int(); + int nFrom = 0; + if (params.size() > 2) + nFrom = params[2].get_int(); + + Array ret; + CWalletDB walletdb(pwalletMain->strWalletFile); + + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap: + typedef pair TxPair; + typedef multimap TxItems; + TxItems txByTime; + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0))); + } + list acentries; + walletdb.ListAccountCreditDebit(strAccount, acentries); + BOOST_FOREACH(CAccountingEntry& entry, acentries) + { + txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + } + + // Now: iterate backwards until we have nCount items to return: + TxItems::reverse_iterator it = txByTime.rbegin(); + for (std::advance(it, nFrom); it != txByTime.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx != 0) + ListTransactions(*pwtx, strAccount, 0, true, ret); + CAccountingEntry *const pacentry = (*it).second.second; + if (pacentry != 0) + AcentryToJSON(*pacentry, strAccount, ret); + + if (ret.size() >= nCount) break; + } + // ret is now newest to oldest + } + + // Make sure we return only last nCount items (sends-to-self might give us an extra): + if (ret.size() > nCount) + { + Array::iterator last = ret.begin(); + std::advance(last, nCount); + ret.erase(last, ret.end()); + } + std::reverse(ret.begin(), ret.end()); // oldest to newest + + return ret; +} + +Value listaccounts(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "listaccounts [minconf=1]\n" + "Returns Object that has account names as keys, account balances as values."); + + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + map mapAccountBalances; + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string, string)& entry, pwalletMain->mapAddressBook) { + uint160 hash160; + if(AddressToHash160(entry.first, hash160) && mapPubKeys.count(hash160)) // This address belongs to me + mapAccountBalances[entry.second] = 0; + } + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + int64 nGeneratedImmature, nGeneratedMature, nFee; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); + mapAccountBalances[strSentAccount] -= nFee; + BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) + mapAccountBalances[strSentAccount] -= s.second; + if (wtx.GetDepthInMainChain() >= nMinDepth) + { + mapAccountBalances[""] += nGeneratedMature; + BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) + if (pwalletMain->mapAddressBook.count(r.first)) + mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second; + else + mapAccountBalances[""] += r.second; + } + } + } + + list acentries; + CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); + BOOST_FOREACH(const CAccountingEntry& entry, acentries) + mapAccountBalances[entry.strAccount] += entry.nCreditDebit; + + Object ret; + BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) { + ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); + } + return ret; +} + +Value gettransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "gettransaction \n" + "Get detailed information about "); + + uint256 hash; + hash.SetHex(params[0].get_str()); + + Object entry; + CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + { + if (!pwalletMain->mapWallet.count(hash)) + throw JSONRPCError(-5, "Invalid or non-wallet transaction id"); + const CWalletTx& wtx = pwalletMain->mapWallet[hash]; + + int64 nCredit = wtx.GetCredit(); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); + + entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); + if (wtx.IsFromMe()) + entry.push_back(Pair("fee", ValueFromAmount(nFee))); + + WalletTxToJSON(pwalletMain->mapWallet[hash], entry); + + Array details; + ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details); + entry.push_back(Pair("details", details)); + } + + return entry; +} + + +Value backupwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "backupwallet \n" + "Safely copies wallet.dat to destination, which can be a directory or a path with filename."); + + string strDest = params[0].get_str(); + BackupWallet(*pwalletMain, strDest); + + return Value::null; +} + + +Value validateaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "validateaddress \n" + "Return information about ."); + + string strAddress = params[0].get_str(); + uint160 hash160; + bool isValid = AddressToHash160(strAddress, hash160); + + Object ret; + ret.push_back(Pair("isvalid", isValid)); + if (isValid) + { + // Call Hash160ToAddress() so we always return current ADDRESSVERSION + // version of the address: + string currentAddress = Hash160ToAddress(hash160); + ret.push_back(Pair("address", currentAddress)); + ret.push_back(Pair("ismine", (mapPubKeys.count(hash160) > 0))); + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + { + if (pwalletMain->mapAddressBook.count(currentAddress)) + ret.push_back(Pair("account", pwalletMain->mapAddressBook[currentAddress])); + } + } + return ret; +} + + +Value getwork(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getwork [data]\n" + "If [data] is not specified, returns formatted hash data to work on:\n" + " \"midstate\" : precomputed hash state after hashing the first half of the data\n" + " \"data\" : block data\n" + " \"hash1\" : formatted hash buffer for second hash\n" + " \"target\" : little endian hash target\n" + "If [data] is specified, tries to solve the block and returns true if it was successful."); + + if (vNodes.empty()) + throw JSONRPCError(-9, "Bitcoin is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(-10, "Bitcoin is downloading blocks..."); + + static map > mapNewBlock; + static vector vNewBlock; + static CReserveKey reservekey(pwalletMain); + + if (params.size() == 0) + { + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64 nStart; + static CBlock* pblock; + if (pindexPrev != pindexBest || + (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) + { + if (pindexPrev != pindexBest) + { + // Deallocate old blocks since they're obsolete now + mapNewBlock.clear(); + BOOST_FOREACH(CBlock* pblock, vNewBlock) + delete pblock; + vNewBlock.clear(); + } + nTransactionsUpdatedLast = nTransactionsUpdated; + pindexPrev = pindexBest; + nStart = GetTime(); + + // Create new block + pblock = CreateNewBlock(reservekey); + if (!pblock) + throw JSONRPCError(-7, "Out of memory"); + vNewBlock.push_back(pblock); + } + + // Update nTime + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->nNonce = 0; + + // Update nExtraNonce + static unsigned int nExtraNonce = 0; + static int64 nPrevTime = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, nPrevTime); + + // Save + mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, nExtraNonce); + + // Prebuild hash buffers + char pmidstate[32]; + char pdata[128]; + char phash1[64]; + FormatHashBuffers(pblock, pmidstate, pdata, phash1); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + Object result; + result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); + result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); + result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); + result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); + return result; + } + else + { + // Parse parameters + vector vchData = ParseHex(params[0].get_str()); + if (vchData.size() != 128) + throw JSONRPCError(-8, "Invalid parameter"); + CBlock* pdata = (CBlock*)&vchData[0]; + + // Byte reverse + for (int i = 0; i < 128/4; i++) + ((unsigned int*)pdata)[i] = CryptoPP::ByteReverse(((unsigned int*)pdata)[i]); + + // Get saved block + if (!mapNewBlock.count(pdata->hashMerkleRoot)) + return false; + CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; + unsigned int nExtraNonce = mapNewBlock[pdata->hashMerkleRoot].second; + + pblock->nTime = pdata->nTime; + pblock->nNonce = pdata->nNonce; + pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + + return CheckWork(pblock, *pwalletMain, reservekey); + } +} + + + + + + + + + + + +// +// Call Table +// + +pair pCallTable[] = +{ + make_pair("help", &help), + make_pair("stop", &stop), + make_pair("getblockcount", &getblockcount), + make_pair("getblocknumber", &getblocknumber), + make_pair("getconnectioncount", &getconnectioncount), + make_pair("getdifficulty", &getdifficulty), + make_pair("getgenerate", &getgenerate), + make_pair("setgenerate", &setgenerate), + make_pair("gethashespersec", &gethashespersec), + make_pair("getinfo", &getinfo), + make_pair("getnewaddress", &getnewaddress), + make_pair("getaccountaddress", &getaccountaddress), + make_pair("setaccount", &setaccount), + make_pair("setlabel", &setaccount), // deprecated + make_pair("getaccount", &getaccount), + make_pair("getlabel", &getaccount), // deprecated + make_pair("getaddressesbyaccount", &getaddressesbyaccount), + make_pair("getaddressesbylabel", &getaddressesbyaccount), // deprecated + make_pair("sendtoaddress", &sendtoaddress), + make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress + make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress + make_pair("getreceivedbyaddress", &getreceivedbyaddress), + make_pair("getreceivedbyaccount", &getreceivedbyaccount), + make_pair("getreceivedbylabel", &getreceivedbyaccount), // deprecated + make_pair("listreceivedbyaddress", &listreceivedbyaddress), + make_pair("listreceivedbyaccount", &listreceivedbyaccount), + make_pair("listreceivedbylabel", &listreceivedbyaccount), // deprecated + make_pair("backupwallet", &backupwallet), + make_pair("validateaddress", &validateaddress), + make_pair("getbalance", &getbalance), + make_pair("move", &movecmd), + make_pair("sendfrom", &sendfrom), + make_pair("sendmany", &sendmany), + make_pair("gettransaction", &gettransaction), + make_pair("listtransactions", &listtransactions), + make_pair("getwork", &getwork), + make_pair("listaccounts", &listaccounts), + make_pair("settxfee", &settxfee), +}; +map mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0])); + +string pAllowInSafeMode[] = +{ + "help", + "stop", + "getblockcount", + "getblocknumber", + "getconnectioncount", + "getdifficulty", + "getgenerate", + "setgenerate", + "gethashespersec", + "getinfo", + "getnewaddress", + "getaccountaddress", + "setlabel", + "getaccount", + "getlabel", // deprecated + "getaddressesbyaccount", + "getaddressesbylabel", // deprecated + "backupwallet", + "validateaddress", + "getwork", +}; +set setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0])); + + + + +// +// HTTP protocol +// +// This ain't Apache. We're just using HTTP header for the length field +// and to be compatible with other JSON-RPC implementations. +// + +string HTTPPost(const string& strMsg, const map& mapRequestHeaders) +{ + ostringstream s; + s << "POST / HTTP/1.1\r\n" + << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n" + << "Host: 127.0.0.1\r\n" + << "Content-Type: application/json\r\n" + << "Content-Length: " << strMsg.size() << "\r\n" + << "Accept: application/json\r\n"; + BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders) + s << item.first << ": " << item.second << "\r\n"; + s << "\r\n" << strMsg; + + return s.str(); +} + +string rfc1123Time() +{ + char buffer[64]; + time_t now; + time(&now); + struct tm* now_gmt = gmtime(&now); + string locale(setlocale(LC_TIME, NULL)); + setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings + strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); + setlocale(LC_TIME, locale.c_str()); + return string(buffer); +} + +static string HTTPReply(int nStatus, const string& strMsg) +{ + if (nStatus == 401) + return strprintf("HTTP/1.0 401 Authorization Required\r\n" + "Date: %s\r\n" + "Server: bitcoin-json-rpc/%s\r\n" + "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 296\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Error\r\n" + "\r\n" + "\r\n" + "

401 Unauthorized.

\r\n" + "\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); + string strStatus; + if (nStatus == 200) strStatus = "OK"; + else if (nStatus == 400) strStatus = "Bad Request"; + else if (nStatus == 403) strStatus = "Forbidden"; + else if (nStatus == 404) strStatus = "Not Found"; + else if (nStatus == 500) strStatus = "Internal Server Error"; + return strprintf( + "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Connection: close\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/json\r\n" + "Server: bitcoin-json-rpc/%s\r\n" + "\r\n" + "%s", + nStatus, + strStatus.c_str(), + rfc1123Time().c_str(), + strMsg.size(), + FormatFullVersion().c_str(), + strMsg.c_str()); +} + +int ReadHTTPStatus(std::basic_istream& stream) +{ + string str; + getline(stream, str); + vector vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return 500; + return atoi(vWords[1].c_str()); +} + +int ReadHTTPHeader(std::basic_istream& stream, map& mapHeadersRet) +{ + int nLen = 0; + loop + { + string str; + std::getline(stream, str); + if (str.empty() || str == "\r") + break; + string::size_type nColon = str.find(":"); + if (nColon != string::npos) + { + string strHeader = str.substr(0, nColon); + boost::trim(strHeader); + boost::to_lower(strHeader); + string strValue = str.substr(nColon+1); + boost::trim(strValue); + mapHeadersRet[strHeader] = strValue; + if (strHeader == "content-length") + nLen = atoi(strValue.c_str()); + } + } + return nLen; +} + +int ReadHTTP(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet) +{ + mapHeadersRet.clear(); + strMessageRet = ""; + + // Read status + int nStatus = ReadHTTPStatus(stream); + + // Read header + int nLen = ReadHTTPHeader(stream, mapHeadersRet); + if (nLen < 0 || nLen > MAX_SIZE) + return 500; + + // Read message + if (nLen > 0) + { + vector vch(nLen); + stream.read(&vch[0], nLen); + strMessageRet = string(vch.begin(), vch.end()); + } + + return nStatus; +} + +string EncodeBase64(string s) +{ + BIO *b64, *bmem; + BUF_MEM *bptr; + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_new(BIO_s_mem()); + b64 = BIO_push(b64, bmem); + BIO_write(b64, s.c_str(), s.size()); + BIO_flush(b64); + BIO_get_mem_ptr(b64, &bptr); + + string result(bptr->data, bptr->length); + BIO_free_all(b64); + + return result; +} + +string DecodeBase64(string s) +{ + BIO *b64, *bmem; + + char* buffer = static_cast(calloc(s.size(), sizeof(char))); + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_new_mem_buf(const_cast(s.c_str()), s.size()); + bmem = BIO_push(b64, bmem); + BIO_read(bmem, buffer, s.size()); + BIO_free_all(bmem); + + string result(buffer); + free(buffer); + return result; +} + +bool HTTPAuthorized(map& mapHeaders) +{ + string strAuth = mapHeaders["authorization"]; + if (strAuth.substr(0,6) != "Basic ") + return false; + string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); + string strUserPass = DecodeBase64(strUserPass64); + string::size_type nColon = strUserPass.find(":"); + if (nColon == string::npos) + return false; + string strUser = strUserPass.substr(0, nColon); + string strPassword = strUserPass.substr(nColon+1); + return (strUser == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]); +} + +// +// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, +// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were +// unspecified (HTTP errors and contents of 'error'). +// +// 1.0 spec: http://json-rpc.org/wiki/specification +// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http +// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx +// + +string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) +{ + Object request; + request.push_back(Pair("method", strMethod)); + request.push_back(Pair("params", params)); + request.push_back(Pair("id", id)); + return write_string(Value(request), false) + "\n"; +} + +string JSONRPCReply(const Value& result, const Value& error, const Value& id) +{ + Object reply; + if (error.type() != null_type) + reply.push_back(Pair("result", Value::null)); + else + reply.push_back(Pair("result", result)); + reply.push_back(Pair("error", error)); + reply.push_back(Pair("id", id)); + return write_string(Value(reply), false) + "\n"; +} + +void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) +{ + // Send error reply from json-rpc error object + int nStatus = 500; + int code = find_value(objError, "code").get_int(); + if (code == -32600) nStatus = 400; + else if (code == -32601) nStatus = 404; + string strReply = JSONRPCReply(Value::null, objError, id); + stream << HTTPReply(nStatus, strReply) << std::flush; +} + +bool ClientAllowed(const string& strAddress) +{ + if (strAddress == asio::ip::address_v4::loopback().to_string()) + return true; + const vector& vAllow = mapMultiArgs["-rpcallowip"]; + BOOST_FOREACH(string strAllow, vAllow) + if (WildcardMatch(strAddress, strAllow)) + return true; + return false; +} + +#ifdef USE_SSL +// +// IOStream device that speaks SSL but can also speak non-SSL +// +class SSLIOStreamDevice : public iostreams::device { +public: + SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn) + { + fUseSSL = fUseSSLIn; + fNeedHandshake = fUseSSLIn; + } + + void handshake(ssl::stream_base::handshake_type role) + { + if (!fNeedHandshake) return; + fNeedHandshake = false; + stream.handshake(role); + } + std::streamsize read(char* s, std::streamsize n) + { + handshake(ssl::stream_base::server); // HTTPS servers read first + if (fUseSSL) return stream.read_some(asio::buffer(s, n)); + return stream.next_layer().read_some(asio::buffer(s, n)); + } + std::streamsize write(const char* s, std::streamsize n) + { + handshake(ssl::stream_base::client); // HTTPS clients write first + if (fUseSSL) return asio::write(stream, asio::buffer(s, n)); + return asio::write(stream.next_layer(), asio::buffer(s, n)); + } + bool connect(const std::string& server, const std::string& port) + { + ip::tcp::resolver resolver(stream.get_io_service()); + ip::tcp::resolver::query query(server.c_str(), port.c_str()); + ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); + ip::tcp::resolver::iterator end; + boost::system::error_code error = asio::error::host_not_found; + while (error && endpoint_iterator != end) + { + stream.lowest_layer().close(); + stream.lowest_layer().connect(*endpoint_iterator++, error); + } + if (error) + return false; + return true; + } + +private: + bool fNeedHandshake; + bool fUseSSL; + SSLStream& stream; +}; +#endif + +void ThreadRPCServer(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg)); + try + { + vnThreadsRunning[4]++; + ThreadRPCServer2(parg); + vnThreadsRunning[4]--; + } + catch (std::exception& e) { + vnThreadsRunning[4]--; + PrintException(&e, "ThreadRPCServer()"); + } catch (...) { + vnThreadsRunning[4]--; + PrintException(NULL, "ThreadRPCServer()"); + } + printf("ThreadRPCServer exiting\n"); +} + +void ThreadRPCServer2(void* parg) +{ + printf("ThreadRPCServer started\n"); + + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + { + string strWhatAmI = "To use bitcoind"; + if (mapArgs.count("-server")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); + else if (mapArgs.count("-daemon")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); + PrintConsole( + _("Warning: %s, you must set rpcpassword=\nin the configuration file: %s\n" + "If the file does not exist, create it with owner-readable-only file permissions.\n"), + strWhatAmI.c_str(), + GetConfigFile().c_str()); + CreateThread(Shutdown, NULL); + return; + } + + bool fUseSSL = GetBoolArg("-rpcssl"); + asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); + + asio::io_service io_service; + ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332)); + ip::tcp::acceptor acceptor(io_service, endpoint); + + acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + +#ifdef USE_SSL + ssl::context context(io_service, ssl::context::sslv23); + if (fUseSSL) + { + context.set_options(ssl::context::no_sslv2); + filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert"); + if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile; + if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str()); + else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str()); + filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem"); + if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile; + if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem); + else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str()); + + string ciphers = GetArg("-rpcsslciphers", + "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); + SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str()); + } +#else + if (fUseSSL) + throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); +#endif + + loop + { + // Accept connection +#ifdef USE_SSL + SSLStream sslStream(io_service, context); + SSLIOStreamDevice d(sslStream, fUseSSL); + iostreams::stream stream(d); +#else + ip::tcp::iostream stream; +#endif + + ip::tcp::endpoint peer; + vnThreadsRunning[4]--; +#ifdef USE_SSL + acceptor.accept(sslStream.lowest_layer(), peer); +#else + acceptor.accept(*stream.rdbuf(), peer); +#endif + vnThreadsRunning[4]++; + if (fShutdown) + return; + + // Restrict callers by IP + if (!ClientAllowed(peer.address().to_string())) + { + // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. + if (!fUseSSL) + stream << HTTPReply(403, "") << std::flush; + continue; + } + + map mapHeaders; + string strRequest; + + boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest)); + if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30)))) + { // Timed out: + acceptor.cancel(); + printf("ThreadRPCServer ReadHTTP timeout\n"); + continue; + } + + // Check authorization + if (mapHeaders.count("authorization") == 0) + { + stream << HTTPReply(401, "") << std::flush; + continue; + } + if (!HTTPAuthorized(mapHeaders)) + { + // Deter brute-forcing short passwords + if (mapArgs["-rpcpassword"].size() < 15) + Sleep(50); + + stream << HTTPReply(401, "") << std::flush; + printf("ThreadRPCServer incorrect password attempt\n"); + continue; + } + + Value id = Value::null; + try + { + // Parse request + Value valRequest; + if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type) + throw JSONRPCError(-32700, "Parse error"); + const Object& request = valRequest.get_obj(); + + // Parse id now so errors from here on will have the id + id = find_value(request, "id"); + + // Parse method + Value valMethod = find_value(request, "method"); + if (valMethod.type() == null_type) + throw JSONRPCError(-32600, "Missing method"); + if (valMethod.type() != str_type) + throw JSONRPCError(-32600, "Method must be a string"); + string strMethod = valMethod.get_str(); + if (strMethod != "getwork") + printf("ThreadRPCServer method=%s\n", strMethod.c_str()); + + // Parse params + Value valParams = find_value(request, "params"); + Array params; + if (valParams.type() == array_type) + params = valParams.get_array(); + else if (valParams.type() == null_type) + params = Array(); + else + throw JSONRPCError(-32600, "Params must be an array"); + + // Find method + map::iterator mi = mapCallTable.find(strMethod); + if (mi == mapCallTable.end()) + throw JSONRPCError(-32601, "Method not found"); + + // Observe safe mode + string strWarning = GetWarnings("rpc"); + if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod)) + throw JSONRPCError(-2, string("Safe mode: ") + strWarning); + + try + { + // Execute + Value result = (*(*mi).second)(params, false); + + // Send reply + string strReply = JSONRPCReply(result, Value::null, id); + stream << HTTPReply(200, strReply) << std::flush; + } + catch (std::exception& e) + { + ErrorReply(stream, JSONRPCError(-1, e.what()), id); + } + } + catch (Object& objError) + { + ErrorReply(stream, objError, id); + } + catch (std::exception& e) + { + ErrorReply(stream, JSONRPCError(-32700, e.what()), id); + } + } +} + + + + +Object CallRPC(const string& strMethod, const Array& params) +{ + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + throw runtime_error(strprintf( + _("You must set rpcpassword= in the configuration file:\n%s\n" + "If the file does not exist, create it with owner-readable-only file permissions."), + GetConfigFile().c_str())); + + // Connect to localhost + bool fUseSSL = GetBoolArg("-rpcssl"); +#ifdef USE_SSL + asio::io_service io_service; + ssl::context context(io_service, ssl::context::sslv23); + context.set_options(ssl::context::no_sslv2); + SSLStream sslStream(io_service, context); + SSLIOStreamDevice d(sslStream, fUseSSL); + iostreams::stream stream(d); + if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"))) + throw runtime_error("couldn't connect to server"); +#else + if (fUseSSL) + throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); + + ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")); + if (stream.fail()) + throw runtime_error("couldn't connect to server"); +#endif + + + // HTTP basic authentication + string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); + map mapRequestHeaders; + mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; + + // Send request + string strRequest = JSONRPCRequest(strMethod, params, 1); + string strPost = HTTPPost(strRequest, mapRequestHeaders); + stream << strPost << std::flush; + + // Receive reply + map mapHeaders; + string strReply; + int nStatus = ReadHTTP(stream, mapHeaders, strReply); + if (nStatus == 401) + throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500) + throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); + else if (strReply.empty()) + throw runtime_error("no response from server"); + + // Parse reply + Value valReply; + if (!read_string(strReply, valReply)) + throw runtime_error("couldn't parse reply from server"); + const Object& reply = valReply.get_obj(); + if (reply.empty()) + throw runtime_error("expected reply to have result, error and id properties"); + + return reply; +} + + + + +template +void ConvertTo(Value& value) +{ + if (value.type() == str_type) + { + // reinterpret string as unquoted json value + Value value2; + if (!read_string(value.get_str(), value2)) + throw runtime_error("type mismatch"); + value = value2.get_value(); + } + else + { + value = value.get_value(); + } +} + +int CommandLineRPC(int argc, char *argv[]) +{ + string strPrint; + int nRet = 0; + try + { + // Skip switches + while (argc > 1 && IsSwitchChar(argv[1][0])) + { + argc--; + argv++; + } + + // Method + if (argc < 2) + throw runtime_error("too few parameters"); + string strMethod = argv[1]; + + // Parameters default to strings + Array params; + for (int i = 2; i < argc; i++) + params.push_back(argv[i]); + int n = params.size(); + + // + // Special case non-string parameter types + // + if (strMethod == "setgenerate" && n > 0) ConvertTo(params[0]); + if (strMethod == "setgenerate" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); + if (strMethod == "getamountreceived" && n > 1) ConvertTo(params[1]); // deprecated + if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo(params[1]); + if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated + if (strMethod == "getallreceived" && n > 0) ConvertTo(params[0]); // deprecated + if (strMethod == "getallreceived" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo(params[0]); + if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo(params[0]); // deprecated + if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated + if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); + if (strMethod == "move" && n > 2) ConvertTo(params[2]); + if (strMethod == "move" && n > 3) ConvertTo(params[3]); + if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]); + if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); + if (strMethod == "listtransactions" && n > 1) ConvertTo(params[1]); + if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); + if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); + if (strMethod == "sendmany" && n > 1) + { + string s = params[1].get_str(); + Value v; + if (!read_string(s, v) || v.type() != obj_type) + throw runtime_error("type mismatch"); + params[1] = v.get_obj(); + } + if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + + // Execute + Object reply = CallRPC(strMethod, params); + + // Parse reply + const Value& result = find_value(reply, "result"); + const Value& error = find_value(reply, "error"); + const Value& id = find_value(reply, "id"); + + if (error.type() != null_type) + { + // Error + strPrint = "error: " + write_string(error, false); + int code = find_value(error.get_obj(), "code").get_int(); + nRet = abs(code); + } + else + { + // Result + if (result.type() == null_type) + strPrint = ""; + else if (result.type() == str_type) + strPrint = result.get_str(); + else + strPrint = write_string(result, true); + } + } + catch (std::exception& e) + { + strPrint = string("error: ") + e.what(); + nRet = 87; + } + catch (...) + { + PrintException(NULL, "CommandLineRPC()"); + } + + if (strPrint != "") + { +#if defined(__WXMSW__) && defined(GUI) + // Windows GUI apps can't print to command line, + // so settle for a message box yuck + MyMessageBox(strPrint, "Bitcoin", wxOK); +#else + fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); +#endif + } + return nRet; +} + + + + +#ifdef TEST +int main(int argc, char *argv[]) +{ +#ifdef _MSC_VER + // Turn off microsoft heap dump noise + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); +#endif + setbuf(stdin, NULL); + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + try + { + if (argc >= 2 && string(argv[1]) == "-server") + { + printf("server ready\n"); + ThreadRPCServer(NULL); + } + else + { + return CommandLineRPC(argc, argv); + } + } + catch (std::exception& e) { + PrintException(&e, "main()"); + } catch (...) { + PrintException(NULL, "main()"); + } + return 0; +} +#endif diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h new file mode 100644 index 0000000000..48a7b8a8a6 --- /dev/null +++ b/src/bitcoinrpc.h @@ -0,0 +1,6 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +void ThreadRPCServer(void* parg); +int CommandLineRPC(int argc, char *argv[]); diff --git a/src/headers.h b/src/headers.h index d1844eb24e..02dba30ae2 100644 --- a/src/headers.h +++ b/src/headers.h @@ -98,8 +98,12 @@ #include "uibase.h" #include "ui.h" #else +#ifdef QT_GUI +#include "qtui.h" +#else #include "noui.h" #endif +#endif #ifdef GUI #include "xpm/addressbook16.xpm" diff --git a/src/init.cpp b/src/init.cpp index 635799ccf9..adbfa18c6a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -3,7 +3,7 @@ // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" #include "db.h" -#include "rpc.h" +#include "bitcoinrpc.h" #include "net.h" #include "init.h" #include "strlcpy.h" @@ -79,7 +79,7 @@ void HandleSIGTERM(int) // // Start // -#ifndef GUI +#if !defined(QT_GUI) && !defined(GUI) int main(int argc, char* argv[]) { bool fRet = false; @@ -239,10 +239,9 @@ bool AppInit2(int argc, char* argv[]) fServer = GetBoolArg("-server"); /* force fServer when running without GUI */ -#ifndef GUI +#if !defined(QT_GUI) && !defined(GUI) fServer = true; #endif - fPrintToConsole = GetBoolArg("-printtoconsole"); fPrintToDebugger = GetBoolArg("-printtodebugger"); @@ -545,7 +544,7 @@ bool AppInit2(int argc, char* argv[]) SetStartOnSystemStartup(true); #endif -#ifndef GUI +#if !defined(QT_GUI) && !defined(GUI) while (1) Sleep(5000); #endif diff --git a/src/net.h b/src/net.h index afa264b723..8f21de8dc9 100644 --- a/src/net.h +++ b/src/net.h @@ -6,6 +6,7 @@ #include #include +#include #include #ifndef __WXMSW__ diff --git a/src/qtui.h b/src/qtui.h new file mode 100644 index 0000000000..a3b9eb0148 --- /dev/null +++ b/src/qtui.h @@ -0,0 +1,48 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_EXTERNUI_H +#define BITCOIN_EXTERNUI_H + +#include +#include +#include "wallet.h" + +typedef void wxWindow; +#define wxYES 0x00000002 +#define wxOK 0x00000004 +#define wxNO 0x00000008 +#define wxYES_NO (wxYES|wxNO) +#define wxCANCEL 0x00000010 +#define wxAPPLY 0x00000020 +#define wxCLOSE 0x00000040 +#define wxOK_DEFAULT 0x00000000 +#define wxYES_DEFAULT 0x00000000 +#define wxNO_DEFAULT 0x00000080 +#define wxCANCEL_DEFAULT 0x80000000 +#define wxICON_EXCLAMATION 0x00000100 +#define wxICON_HAND 0x00000200 +#define wxICON_WARNING wxICON_EXCLAMATION +#define wxICON_ERROR wxICON_HAND +#define wxICON_QUESTION 0x00000400 +#define wxICON_INFORMATION 0x00000800 +#define wxICON_STOP wxICON_HAND +#define wxICON_ASTERISK wxICON_INFORMATION +#define wxICON_MASK (0x00000100|0x00000200|0x00000400|0x00000800) +#define wxFORWARD 0x00001000 +#define wxBACKWARD 0x00002000 +#define wxRESET 0x00004000 +#define wxHELP 0x00008000 +#define wxMORE 0x00010000 +#define wxSETUP 0x00020000 + +extern int MyMessageBox(const std::string& message, const std::string& caption="Message", int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1); +#define wxMessageBox MyMessageBox +extern int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1); +extern bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent); +extern void CalledSetStatusBar(const std::string& strText, int nField); +extern void UIThreadCall(boost::function0 fn); +extern void MainFrameRepaint(); +extern std::string _(const char* psz); + +#endif diff --git a/src/rpc.cpp b/src/rpc.cpp deleted file mode 100644 index 6f951b7431..0000000000 --- a/src/rpc.cpp +++ /dev/null @@ -1,2226 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. - -#include "headers.h" -#include "cryptopp/sha.h" -#include "db.h" -#include "net.h" -#include "init.h" -#undef printf -#include -#include -#include -#include -#ifdef USE_SSL -#include -#include -#include -typedef boost::asio::ssl::stream SSLStream; -#endif -#include "json/json_spirit_reader_template.h" -#include "json/json_spirit_writer_template.h" -#include "json/json_spirit_utils.h" -#define printf OutputDebugStringF -// MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are -// precompiled in headers.h. The problem might be when the pch file goes over -// a certain size around 145MB. If we need access to json_spirit outside this -// file, we could use the compiled json_spirit option. - -using namespace std; -using namespace boost; -using namespace boost::asio; -using namespace json_spirit; - -void ThreadRPCServer2(void* parg); -typedef Value(*rpcfn_type)(const Array& params, bool fHelp); -extern map mapCallTable; - - -Object JSONRPCError(int code, const string& message) -{ - Object error; - error.push_back(Pair("code", code)); - error.push_back(Pair("message", message)); - return error; -} - - -void PrintConsole(const char* format, ...) -{ - char buffer[50000]; - int limit = sizeof(buffer); - va_list arg_ptr; - va_start(arg_ptr, format); - int ret = _vsnprintf(buffer, limit, format, arg_ptr); - va_end(arg_ptr); - if (ret < 0 || ret >= limit) - { - ret = limit - 1; - buffer[limit-1] = 0; - } - printf("%s", buffer); -#if defined(__WXMSW__) && defined(GUI) - MyMessageBox(buffer, "Bitcoin", wxOK | wxICON_EXCLAMATION); -#else - fprintf(stdout, "%s", buffer); -#endif -} - - -int64 AmountFromValue(const Value& value) -{ - double dAmount = value.get_real(); - if (dAmount <= 0.0 || dAmount > 21000000.0) - throw JSONRPCError(-3, "Invalid amount"); - int64 nAmount = roundint64(dAmount * COIN); - if (!MoneyRange(nAmount)) - throw JSONRPCError(-3, "Invalid amount"); - return nAmount; -} - -Value ValueFromAmount(int64 amount) -{ - return (double)amount / (double)COIN; -} - -void WalletTxToJSON(const CWalletTx& wtx, Object& entry) -{ - entry.push_back(Pair("confirmations", wtx.GetDepthInMainChain())); - entry.push_back(Pair("txid", wtx.GetHash().GetHex())); - entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); - BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) - entry.push_back(Pair(item.first, item.second)); -} - -string AccountFromValue(const Value& value) -{ - string strAccount = value.get_str(); - if (strAccount == "*") - throw JSONRPCError(-11, "Invalid account name"); - return strAccount; -} - - - -/// -/// Note: This interface may still be subject to change. -/// - - -Value help(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "help [command]\n" - "List commands, or get help for a command."); - - string strCommand; - if (params.size() > 0) - strCommand = params[0].get_str(); - - string strRet; - set setDone; - for (map::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi) - { - string strMethod = (*mi).first; - // We already filter duplicates, but these deprecated screw up the sort order - if (strMethod == "getamountreceived" || - strMethod == "getallreceived" || - (strMethod.find("label") != string::npos)) - continue; - if (strCommand != "" && strMethod != strCommand) - continue; - try - { - Array params; - rpcfn_type pfn = (*mi).second; - if (setDone.insert(pfn).second) - (*pfn)(params, true); - } - catch (std::exception& e) - { - // Help text is returned in an exception - string strHelp = string(e.what()); - if (strCommand == "") - if (strHelp.find('\n') != -1) - strHelp = strHelp.substr(0, strHelp.find('\n')); - strRet += strHelp + "\n"; - } - } - if (strRet == "") - strRet = strprintf("help: unknown command: %s\n", strCommand.c_str()); - strRet = strRet.substr(0,strRet.size()-1); - return strRet; -} - - -Value stop(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "stop\n" - "Stop bitcoin server."); - - // Shutdown will take long enough that the response should get back - CreateThread(Shutdown, NULL); - return "bitcoin server stopping"; -} - - -Value getblockcount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblockcount\n" - "Returns the number of blocks in the longest block chain."); - - return nBestHeight; -} - - -Value getblocknumber(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getblocknumber\n" - "Returns the block number of the latest block in the longest block chain."); - - return nBestHeight; -} - - -Value getconnectioncount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getconnectioncount\n" - "Returns the number of connections to other nodes."); - - return (int)vNodes.size(); -} - - -double GetDifficulty() -{ - // Floating point number that is a multiple of the minimum difficulty, - // minimum difficulty = 1.0. - - if (pindexBest == NULL) - return 1.0; - int nShift = (pindexBest->nBits >> 24) & 0xff; - - double dDiff = - (double)0x0000ffff / (double)(pindexBest->nBits & 0x00ffffff); - - while (nShift < 29) - { - dDiff *= 256.0; - nShift++; - } - while (nShift > 29) - { - dDiff /= 256.0; - nShift--; - } - - return dDiff; -} - -Value getdifficulty(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getdifficulty\n" - "Returns the proof-of-work difficulty as a multiple of the minimum difficulty."); - - return GetDifficulty(); -} - - -Value getgenerate(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getgenerate\n" - "Returns true or false."); - - return (bool)fGenerateBitcoins; -} - - -Value setgenerate(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "setgenerate [genproclimit]\n" - " is true or false to turn generation on or off.\n" - "Generation is limited to [genproclimit] processors, -1 is unlimited."); - - bool fGenerate = true; - if (params.size() > 0) - fGenerate = params[0].get_bool(); - - if (params.size() > 1) - { - int nGenProcLimit = params[1].get_int(); - fLimitProcessors = (nGenProcLimit != -1); - WriteSetting("fLimitProcessors", fLimitProcessors); - if (nGenProcLimit != -1) - WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit); - if (nGenProcLimit == 0) - fGenerate = false; - } - - GenerateBitcoins(fGenerate, pwalletMain); - return Value::null; -} - - -Value gethashespersec(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "gethashespersec\n" - "Returns a recent hashes per second performance measurement while generating."); - - if (GetTimeMillis() - nHPSTimerStart > 8000) - return (boost::int64_t)0; - return (boost::int64_t)dHashesPerSec; -} - - -Value getinfo(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getinfo\n" - "Returns an object containing various state info."); - - Object obj; - obj.push_back(Pair("version", (int)VERSION)); - obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); - obj.push_back(Pair("blocks", (int)nBestHeight)); - obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string()))); - obj.push_back(Pair("generate", (bool)fGenerateBitcoins)); - obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1))); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("hashespersec", gethashespersec(params, false))); - obj.push_back(Pair("testnet", fTestNet)); - obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); - obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - return obj; -} - - -Value getnewaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getnewaddress [account]\n" - "Returns a new bitcoin address for receiving payments. " - "If [account] is specified (recommended), it is added to the address book " - "so payments received with the address will be credited to [account]."); - - // Parse the account first so we don't generate a key if there's an error - string strAccount; - if (params.size() > 0) - strAccount = AccountFromValue(params[0]); - - // Generate a new key that is added to wallet - string strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool()); - - // This could be done in the same main CS as GetKeyFromKeyPool. - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - pwalletMain->SetAddressBookName(strAddress, strAccount); - - return strAddress; -} - - -// requires cs_main, cs_mapWallet, cs_mapAddressBook locks -string GetAccountAddress(string strAccount, bool bForceNew=false) -{ - string strAddress; - - CWalletDB walletdb(pwalletMain->strWalletFile); - walletdb.TxnBegin(); - - CAccount account; - walletdb.ReadAccount(strAccount, account); - - // Check if the current key has been used - if (!account.vchPubKey.empty()) - { - CScript scriptPubKey; - scriptPubKey.SetBitcoinAddress(account.vchPubKey); - for (map::iterator it = pwalletMain->mapWallet.begin(); - it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty(); - ++it) - { - const CWalletTx& wtx = (*it).second; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - account.vchPubKey.clear(); - } - } - - // Generate a new key - if (account.vchPubKey.empty() || bForceNew) - { - account.vchPubKey = pwalletMain->GetKeyFromKeyPool(); - string strAddress = PubKeyToAddress(account.vchPubKey); - pwalletMain->SetAddressBookName(strAddress, strAccount); - walletdb.WriteAccount(strAccount, account); - } - - walletdb.TxnCommit(); - strAddress = PubKeyToAddress(account.vchPubKey); - - return strAddress; -} - -Value getaccountaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaccountaddress \n" - "Returns the current bitcoin address for receiving payments to this account."); - - // Parse the account first so we don't generate a key if there's an error - string strAccount = AccountFromValue(params[0]); - - Value ret; - - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - ret = GetAccountAddress(strAccount); - } - - return ret; -} - - - -Value setaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "setaccount \n" - "Sets the account associated with the given address."); - - string strAddress = params[0].get_str(); - uint160 hash160; - bool isValid = AddressToHash160(strAddress, hash160); - if (!isValid) - throw JSONRPCError(-5, "Invalid bitcoin address"); - - - string strAccount; - if (params.size() > 1) - strAccount = AccountFromValue(params[1]); - - // Detect when changing the account of an address that is the 'unused current key' of another account: - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - if (pwalletMain->mapAddressBook.count(strAddress)) - { - string strOldAccount = pwalletMain->mapAddressBook[strAddress]; - if (strAddress == GetAccountAddress(strOldAccount)) - GetAccountAddress(strOldAccount, true); - } - - pwalletMain->SetAddressBookName(strAddress, strAccount); - } - - return Value::null; -} - - -Value getaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaccount \n" - "Returns the account associated with the given address."); - - string strAddress = params[0].get_str(); - - string strAccount; - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - map::iterator mi = pwalletMain->mapAddressBook.find(strAddress); - if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty()) - strAccount = (*mi).second; - } - return strAccount; -} - - -Value getaddressesbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "getaddressesbyaccount \n" - "Returns the list of addresses for the given account."); - - string strAccount = AccountFromValue(params[0]); - - // Find all addresses that have the given account - Array ret; - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) - { - const string& strAddress = item.first; - const string& strName = item.second; - if (strName == strAccount) - { - // We're only adding valid bitcoin addresses and not ip addresses - CScript scriptPubKey; - if (scriptPubKey.SetBitcoinAddress(strAddress)) - ret.push_back(strAddress); - } - } - } - return ret; -} - -Value settxfee(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 1) - throw runtime_error( - "settxfee \n" - " is a real and is rounded to the nearest 0.00000001"); - - // Amount - int64 nAmount = 0; - if (params[0].get_real() != 0.0) - nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts - - nTransactionFee = nAmount; - return true; -} - -Value sendtoaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 4) - throw runtime_error( - "sendtoaddress [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001"); - - string strAddress = params[0].get_str(); - - // Amount - int64 nAmount = AmountFromValue(params[1]); - - // Wallet comments - CWalletTx wtx; - if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty()) - wtx.mapValue["comment"] = params[2].get_str(); - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) - wtx.mapValue["to"] = params[3].get_str(); - - CRITICAL_BLOCK(cs_main) - { - string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); - if (strError != "") - throw JSONRPCError(-4, strError); - } - - return wtx.GetHash().GetHex(); -} - - -Value getreceivedbyaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getreceivedbyaddress [minconf=1]\n" - "Returns the total amount received by in transactions with at least [minconf] confirmations."); - - // Bitcoin address - string strAddress = params[0].get_str(); - CScript scriptPubKey; - if (!scriptPubKey.SetBitcoinAddress(strAddress)) - throw JSONRPCError(-5, "Invalid bitcoin address"); - if (!IsMine(*pwalletMain,scriptPubKey)) - return (double)0.0; - - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - // Tally - int64 nAmount = 0; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; - } - } - - return ValueFromAmount(nAmount); -} - - -void GetAccountPubKeys(string strAccount, set& setPubKey) -{ - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) - { - const string& strAddress = item.first; - const string& strName = item.second; - if (strName == strAccount) - { - // We're only counting our own valid bitcoin addresses and not ip addresses - CScript scriptPubKey; - if (scriptPubKey.SetBitcoinAddress(strAddress)) - if (IsMine(*pwalletMain,scriptPubKey)) - setPubKey.insert(scriptPubKey); - } - } - } -} - - -Value getreceivedbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getreceivedbyaccount [minconf=1]\n" - "Returns the total amount received by addresses with in transactions with at least [minconf] confirmations."); - - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - // Get the set of pub keys that have the label - string strAccount = AccountFromValue(params[0]); - set setPubKey; - GetAccountPubKeys(strAccount, setPubKey); - - // Tally - int64 nAmount = 0; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (setPubKey.count(txout.scriptPubKey)) - if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; - } - } - - return (double)nAmount / (double)COIN; -} - - -int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) -{ - int64 nBalance = 0; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - // Tally wallet transactions - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!wtx.IsFinal()) - continue; - - int64 nGenerated, nReceived, nSent, nFee; - wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee); - - if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) - nBalance += nReceived; - nBalance += nGenerated - nSent - nFee; - } - - // Tally internal accounting entries - nBalance += walletdb.GetAccountCreditDebit(strAccount); - } - - return nBalance; -} - -int64 GetAccountBalance(const string& strAccount, int nMinDepth) -{ - CWalletDB walletdb(pwalletMain->strWalletFile); - return GetAccountBalance(walletdb, strAccount, nMinDepth); -} - - -Value getbalance(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 0 || params.size() > 2) - throw runtime_error( - "getbalance [account] [minconf=1]\n" - "If [account] is not specified, returns the server's total available balance.\n" - "If [account] is specified, returns the balance in the account."); - - if (params.size() == 0) - return ValueFromAmount(pwalletMain->GetBalance()); - - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); - - if (params[0].get_str() == "*") { - // Calculate total balance a different way from GetBalance() - // (GetBalance() sums up all unspent TxOuts) - // getbalance and getbalance '*' should always return the same number. - int64 nBalance = 0; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!wtx.IsFinal()) - continue; - - int64 allGeneratedImmature, allGeneratedMature, allFee; - allGeneratedImmature = allGeneratedMature = allFee = 0; - string strSentAccount; - list > listReceived; - list > listSent; - wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); - if (wtx.GetDepthInMainChain() >= nMinDepth) - BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) - nBalance += r.second; - BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listSent) - nBalance -= r.second; - nBalance -= allFee; - nBalance += allGeneratedMature; - } - return ValueFromAmount(nBalance); - } - - string strAccount = AccountFromValue(params[0]); - - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - - return ValueFromAmount(nBalance); -} - - -Value movecmd(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 3 || params.size() > 5) - throw runtime_error( - "move [minconf=1] [comment]\n" - "Move from one account in your wallet to another."); - - string strFrom = AccountFromValue(params[0]); - string strTo = AccountFromValue(params[1]); - int64 nAmount = AmountFromValue(params[2]); - int nMinDepth = 1; - if (params.size() > 3) - nMinDepth = params[3].get_int(); - string strComment; - if (params.size() > 4) - strComment = params[4].get_str(); - - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - CWalletDB walletdb(pwalletMain->strWalletFile); - walletdb.TxnBegin(); - - int64 nNow = GetAdjustedTime(); - - // Debit - CAccountingEntry debit; - debit.strAccount = strFrom; - debit.nCreditDebit = -nAmount; - debit.nTime = nNow; - debit.strOtherAccount = strTo; - debit.strComment = strComment; - walletdb.WriteAccountingEntry(debit); - - // Credit - CAccountingEntry credit; - credit.strAccount = strTo; - credit.nCreditDebit = nAmount; - credit.nTime = nNow; - credit.strOtherAccount = strFrom; - credit.strComment = strComment; - walletdb.WriteAccountingEntry(credit); - - walletdb.TxnCommit(); - } - return true; -} - - -Value sendfrom(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 3 || params.size() > 6) - throw runtime_error( - "sendfrom [minconf=1] [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001"); - - string strAccount = AccountFromValue(params[0]); - string strAddress = params[1].get_str(); - int64 nAmount = AmountFromValue(params[2]); - int nMinDepth = 1; - if (params.size() > 3) - nMinDepth = params[3].get_int(); - - CWalletTx wtx; - wtx.strFromAccount = strAccount; - if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) - wtx.mapValue["comment"] = params[4].get_str(); - if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) - wtx.mapValue["to"] = params[5].get_str(); - - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - // Check funds - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - if (nAmount > nBalance) - throw JSONRPCError(-6, "Account has insufficient funds"); - - // Send - string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); - if (strError != "") - throw JSONRPCError(-4, strError); - } - - return wtx.GetHash().GetHex(); -} - -Value sendmany(const Array& params, bool fHelp) -{ - if (fHelp || params.size() < 2 || params.size() > 4) - throw runtime_error( - "sendmany {address:amount,...} [minconf=1] [comment]\n" - "amounts are double-precision floating point numbers"); - - string strAccount = AccountFromValue(params[0]); - Object sendTo = params[1].get_obj(); - int nMinDepth = 1; - if (params.size() > 2) - nMinDepth = params[2].get_int(); - - CWalletTx wtx; - wtx.strFromAccount = strAccount; - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) - wtx.mapValue["comment"] = params[3].get_str(); - - set setAddress; - vector > vecSend; - - int64 totalAmount = 0; - BOOST_FOREACH(const Pair& s, sendTo) - { - uint160 hash160; - string strAddress = s.name_; - - if (setAddress.count(strAddress)) - throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+strAddress); - setAddress.insert(strAddress); - - CScript scriptPubKey; - if (!scriptPubKey.SetBitcoinAddress(strAddress)) - throw JSONRPCError(-5, string("Invalid bitcoin address:")+strAddress); - int64 nAmount = AmountFromValue(s.value_); - totalAmount += nAmount; - - vecSend.push_back(make_pair(scriptPubKey, nAmount)); - } - - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - // Check funds - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); - if (totalAmount > nBalance) - throw JSONRPCError(-6, "Account has insufficient funds"); - - // Send - CReserveKey keyChange(pwalletMain); - int64 nFeeRequired = 0; - bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); - if (!fCreated) - { - if (totalAmount + nFeeRequired > pwalletMain->GetBalance()) - throw JSONRPCError(-6, "Insufficient funds"); - throw JSONRPCError(-4, "Transaction creation failed"); - } - if (!pwalletMain->CommitTransaction(wtx, keyChange)) - throw JSONRPCError(-4, "Transaction commit failed"); - } - - return wtx.GetHash().GetHex(); -} - - -struct tallyitem -{ - int64 nAmount; - int nConf; - tallyitem() - { - nAmount = 0; - nConf = INT_MAX; - } -}; - -Value ListReceived(const Array& params, bool fByAccounts) -{ - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].get_int(); - - // Whether to include empty accounts - bool fIncludeEmpty = false; - if (params.size() > 1) - fIncludeEmpty = params[1].get_bool(); - - // Tally - map mapTally; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) - continue; - - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < nMinDepth) - continue; - - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - // Only counting our own bitcoin addresses and not ip addresses - uint160 hash160 = txout.scriptPubKey.GetBitcoinAddressHash160(); - if (hash160 == 0 || !mapPubKeys.count(hash160)) // IsMine - continue; - - tallyitem& item = mapTally[hash160]; - item.nAmount += txout.nValue; - item.nConf = min(item.nConf, nDepth); - } - } - } - - // Reply - Array ret; - map mapAccountTally; - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook) - { - const string& strAddress = item.first; - const string& strAccount = item.second; - uint160 hash160; - if (!AddressToHash160(strAddress, hash160)) - continue; - map::iterator it = mapTally.find(hash160); - if (it == mapTally.end() && !fIncludeEmpty) - continue; - - int64 nAmount = 0; - int nConf = INT_MAX; - if (it != mapTally.end()) - { - nAmount = (*it).second.nAmount; - nConf = (*it).second.nConf; - } - - if (fByAccounts) - { - tallyitem& item = mapAccountTally[strAccount]; - item.nAmount += nAmount; - item.nConf = min(item.nConf, nConf); - } - else - { - Object obj; - obj.push_back(Pair("address", strAddress)); - obj.push_back(Pair("account", strAccount)); - obj.push_back(Pair("label", strAccount)); // deprecated - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); - ret.push_back(obj); - } - } - } - - if (fByAccounts) - { - for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) - { - int64 nAmount = (*it).second.nAmount; - int nConf = (*it).second.nConf; - Object obj; - obj.push_back(Pair("account", (*it).first)); - obj.push_back(Pair("label", (*it).first)); // deprecated - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf))); - ret.push_back(obj); - } - } - - return ret; -} - -Value listreceivedbyaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "listreceivedbyaddress [minconf=1] [includeempty=false]\n" - "[minconf] is the minimum number of confirmations before payments are included.\n" - "[includeempty] whether to include addresses that haven't received any payments.\n" - "Returns an array of objects containing:\n" - " \"address\" : receiving address\n" - " \"account\" : the account of the receiving address\n" - " \"amount\" : total amount received by the address\n" - " \"confirmations\" : number of confirmations of the most recent transaction included"); - - return ListReceived(params, false); -} - -Value listreceivedbyaccount(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 2) - throw runtime_error( - "listreceivedbyaccount [minconf=1] [includeempty=false]\n" - "[minconf] is the minimum number of confirmations before payments are included.\n" - "[includeempty] whether to include accounts that haven't received any payments.\n" - "Returns an array of objects containing:\n" - " \"account\" : the account of the receiving addresses\n" - " \"amount\" : total amount received by addresses with this account\n" - " \"confirmations\" : number of confirmations of the most recent transaction included"); - - return ListReceived(params, true); -} - -void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret) -{ - int64 nGeneratedImmature, nGeneratedMature, nFee; - string strSentAccount; - list > listReceived; - list > listSent; - wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); - - bool fAllAccounts = (strAccount == string("*")); - - // Generated blocks assigned to account "" - if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == "")) - { - Object entry; - entry.push_back(Pair("account", string(""))); - if (nGeneratedImmature) - { - entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan")); - entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature))); - } - else - { - entry.push_back(Pair("category", "generate")); - entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature))); - } - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } - - // Sent - if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) - { - BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) - { - Object entry; - entry.push_back(Pair("account", strSentAccount)); - entry.push_back(Pair("address", s.first)); - entry.push_back(Pair("category", "send")); - entry.push_back(Pair("amount", ValueFromAmount(-s.second))); - entry.push_back(Pair("fee", ValueFromAmount(-nFee))); - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } - } - - // Received - if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) - { - string account; - if (pwalletMain->mapAddressBook.count(r.first)) - account = pwalletMain->mapAddressBook[r.first]; - if (fAllAccounts || (account == strAccount)) - { - Object entry; - entry.push_back(Pair("account", account)); - entry.push_back(Pair("address", r.first)); - entry.push_back(Pair("category", "receive")); - entry.push_back(Pair("amount", ValueFromAmount(r.second))); - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); - } - } - } - -} - -void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) -{ - bool fAllAccounts = (strAccount == string("*")); - - if (fAllAccounts || acentry.strAccount == strAccount) - { - Object entry; - entry.push_back(Pair("account", acentry.strAccount)); - entry.push_back(Pair("category", "move")); - entry.push_back(Pair("time", (boost::int64_t)acentry.nTime)); - entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); - entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); - entry.push_back(Pair("comment", acentry.strComment)); - ret.push_back(entry); - } -} - -Value listtransactions(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 3) - throw runtime_error( - "listtransactions [account] [count=10] [from=0]\n" - "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account]."); - - string strAccount = "*"; - if (params.size() > 0) - strAccount = params[0].get_str(); - int nCount = 10; - if (params.size() > 1) - nCount = params[1].get_int(); - int nFrom = 0; - if (params.size() > 2) - nFrom = params[2].get_int(); - - Array ret; - CWalletDB walletdb(pwalletMain->strWalletFile); - - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap: - typedef pair TxPair; - typedef multimap TxItems; - TxItems txByTime; - - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - CWalletTx* wtx = &((*it).second); - txByTime.insert(make_pair(wtx->GetTxTime(), TxPair(wtx, (CAccountingEntry*)0))); - } - list acentries; - walletdb.ListAccountCreditDebit(strAccount, acentries); - BOOST_FOREACH(CAccountingEntry& entry, acentries) - { - txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); - } - - // Now: iterate backwards until we have nCount items to return: - TxItems::reverse_iterator it = txByTime.rbegin(); - for (std::advance(it, nFrom); it != txByTime.rend(); ++it) - { - CWalletTx *const pwtx = (*it).second.first; - if (pwtx != 0) - ListTransactions(*pwtx, strAccount, 0, true, ret); - CAccountingEntry *const pacentry = (*it).second.second; - if (pacentry != 0) - AcentryToJSON(*pacentry, strAccount, ret); - - if (ret.size() >= nCount) break; - } - // ret is now newest to oldest - } - - // Make sure we return only last nCount items (sends-to-self might give us an extra): - if (ret.size() > nCount) - { - Array::iterator last = ret.begin(); - std::advance(last, nCount); - ret.erase(last, ret.end()); - } - std::reverse(ret.begin(), ret.end()); // oldest to newest - - return ret; -} - -Value listaccounts(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "listaccounts [minconf=1]\n" - "Returns Object that has account names as keys, account balances as values."); - - int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].get_int(); - - map mapAccountBalances; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string, string)& entry, pwalletMain->mapAddressBook) { - uint160 hash160; - if(AddressToHash160(entry.first, hash160) && mapPubKeys.count(hash160)) // This address belongs to me - mapAccountBalances[entry.second] = 0; - } - - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - int64 nGeneratedImmature, nGeneratedMature, nFee; - string strSentAccount; - list > listReceived; - list > listSent; - wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); - mapAccountBalances[strSentAccount] -= nFee; - BOOST_FOREACH(const PAIRTYPE(string, int64)& s, listSent) - mapAccountBalances[strSentAccount] -= s.second; - if (wtx.GetDepthInMainChain() >= nMinDepth) - { - mapAccountBalances[""] += nGeneratedMature; - BOOST_FOREACH(const PAIRTYPE(string, int64)& r, listReceived) - if (pwalletMain->mapAddressBook.count(r.first)) - mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second; - else - mapAccountBalances[""] += r.second; - } - } - } - - list acentries; - CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); - BOOST_FOREACH(const CAccountingEntry& entry, acentries) - mapAccountBalances[entry.strAccount] += entry.nCreditDebit; - - Object ret; - BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) { - ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); - } - return ret; -} - -Value gettransaction(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "gettransaction \n" - "Get detailed information about "); - - uint256 hash; - hash.SetHex(params[0].get_str()); - - Object entry; - CRITICAL_BLOCK(pwalletMain->cs_mapWallet) - { - if (!pwalletMain->mapWallet.count(hash)) - throw JSONRPCError(-5, "Invalid or non-wallet transaction id"); - const CWalletTx& wtx = pwalletMain->mapWallet[hash]; - - int64 nCredit = wtx.GetCredit(); - int64 nDebit = wtx.GetDebit(); - int64 nNet = nCredit - nDebit; - int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); - - entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); - if (wtx.IsFromMe()) - entry.push_back(Pair("fee", ValueFromAmount(nFee))); - - WalletTxToJSON(pwalletMain->mapWallet[hash], entry); - - Array details; - ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details); - entry.push_back(Pair("details", details)); - } - - return entry; -} - - -Value backupwallet(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "backupwallet \n" - "Safely copies wallet.dat to destination, which can be a directory or a path with filename."); - - string strDest = params[0].get_str(); - BackupWallet(*pwalletMain, strDest); - - return Value::null; -} - - -Value validateaddress(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "validateaddress \n" - "Return information about ."); - - string strAddress = params[0].get_str(); - uint160 hash160; - bool isValid = AddressToHash160(strAddress, hash160); - - Object ret; - ret.push_back(Pair("isvalid", isValid)); - if (isValid) - { - // Call Hash160ToAddress() so we always return current ADDRESSVERSION - // version of the address: - string currentAddress = Hash160ToAddress(hash160); - ret.push_back(Pair("address", currentAddress)); - ret.push_back(Pair("ismine", (mapPubKeys.count(hash160) > 0))); - CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) - { - if (pwalletMain->mapAddressBook.count(currentAddress)) - ret.push_back(Pair("account", pwalletMain->mapAddressBook[currentAddress])); - } - } - return ret; -} - - -Value getwork(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getwork [data]\n" - "If [data] is not specified, returns formatted hash data to work on:\n" - " \"midstate\" : precomputed hash state after hashing the first half of the data\n" - " \"data\" : block data\n" - " \"hash1\" : formatted hash buffer for second hash\n" - " \"target\" : little endian hash target\n" - "If [data] is specified, tries to solve the block and returns true if it was successful."); - - if (vNodes.empty()) - throw JSONRPCError(-9, "Bitcoin is not connected!"); - - if (IsInitialBlockDownload()) - throw JSONRPCError(-10, "Bitcoin is downloading blocks..."); - - static map > mapNewBlock; - static vector vNewBlock; - static CReserveKey reservekey(pwalletMain); - - if (params.size() == 0) - { - // Update block - static unsigned int nTransactionsUpdatedLast; - static CBlockIndex* pindexPrev; - static int64 nStart; - static CBlock* pblock; - if (pindexPrev != pindexBest || - (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) - { - if (pindexPrev != pindexBest) - { - // Deallocate old blocks since they're obsolete now - mapNewBlock.clear(); - BOOST_FOREACH(CBlock* pblock, vNewBlock) - delete pblock; - vNewBlock.clear(); - } - nTransactionsUpdatedLast = nTransactionsUpdated; - pindexPrev = pindexBest; - nStart = GetTime(); - - // Create new block - pblock = CreateNewBlock(reservekey); - if (!pblock) - throw JSONRPCError(-7, "Out of memory"); - vNewBlock.push_back(pblock); - } - - // Update nTime - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - pblock->nNonce = 0; - - // Update nExtraNonce - static unsigned int nExtraNonce = 0; - static int64 nPrevTime = 0; - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, nPrevTime); - - // Save - mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, nExtraNonce); - - // Prebuild hash buffers - char pmidstate[32]; - char pdata[128]; - char phash1[64]; - FormatHashBuffers(pblock, pmidstate, pdata, phash1); - - uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - - Object result; - result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); - result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); - result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); - result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); - return result; - } - else - { - // Parse parameters - vector vchData = ParseHex(params[0].get_str()); - if (vchData.size() != 128) - throw JSONRPCError(-8, "Invalid parameter"); - CBlock* pdata = (CBlock*)&vchData[0]; - - // Byte reverse - for (int i = 0; i < 128/4; i++) - ((unsigned int*)pdata)[i] = CryptoPP::ByteReverse(((unsigned int*)pdata)[i]); - - // Get saved block - if (!mapNewBlock.count(pdata->hashMerkleRoot)) - return false; - CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; - unsigned int nExtraNonce = mapNewBlock[pdata->hashMerkleRoot].second; - - pblock->nTime = pdata->nTime; - pblock->nNonce = pdata->nNonce; - pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce); - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - - return CheckWork(pblock, *pwalletMain, reservekey); - } -} - - - - - - - - - - - -// -// Call Table -// - -pair pCallTable[] = -{ - make_pair("help", &help), - make_pair("stop", &stop), - make_pair("getblockcount", &getblockcount), - make_pair("getblocknumber", &getblocknumber), - make_pair("getconnectioncount", &getconnectioncount), - make_pair("getdifficulty", &getdifficulty), - make_pair("getgenerate", &getgenerate), - make_pair("setgenerate", &setgenerate), - make_pair("gethashespersec", &gethashespersec), - make_pair("getinfo", &getinfo), - make_pair("getnewaddress", &getnewaddress), - make_pair("getaccountaddress", &getaccountaddress), - make_pair("setaccount", &setaccount), - make_pair("setlabel", &setaccount), // deprecated - make_pair("getaccount", &getaccount), - make_pair("getlabel", &getaccount), // deprecated - make_pair("getaddressesbyaccount", &getaddressesbyaccount), - make_pair("getaddressesbylabel", &getaddressesbyaccount), // deprecated - make_pair("sendtoaddress", &sendtoaddress), - make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress - make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress - make_pair("getreceivedbyaddress", &getreceivedbyaddress), - make_pair("getreceivedbyaccount", &getreceivedbyaccount), - make_pair("getreceivedbylabel", &getreceivedbyaccount), // deprecated - make_pair("listreceivedbyaddress", &listreceivedbyaddress), - make_pair("listreceivedbyaccount", &listreceivedbyaccount), - make_pair("listreceivedbylabel", &listreceivedbyaccount), // deprecated - make_pair("backupwallet", &backupwallet), - make_pair("validateaddress", &validateaddress), - make_pair("getbalance", &getbalance), - make_pair("move", &movecmd), - make_pair("sendfrom", &sendfrom), - make_pair("sendmany", &sendmany), - make_pair("gettransaction", &gettransaction), - make_pair("listtransactions", &listtransactions), - make_pair("getwork", &getwork), - make_pair("listaccounts", &listaccounts), - make_pair("settxfee", &settxfee), -}; -map mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0])); - -string pAllowInSafeMode[] = -{ - "help", - "stop", - "getblockcount", - "getblocknumber", - "getconnectioncount", - "getdifficulty", - "getgenerate", - "setgenerate", - "gethashespersec", - "getinfo", - "getnewaddress", - "getaccountaddress", - "setlabel", - "getaccount", - "getlabel", // deprecated - "getaddressesbyaccount", - "getaddressesbylabel", // deprecated - "backupwallet", - "validateaddress", - "getwork", -}; -set setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0])); - - - - -// -// HTTP protocol -// -// This ain't Apache. We're just using HTTP header for the length field -// and to be compatible with other JSON-RPC implementations. -// - -string HTTPPost(const string& strMsg, const map& mapRequestHeaders) -{ - ostringstream s; - s << "POST / HTTP/1.1\r\n" - << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n" - << "Host: 127.0.0.1\r\n" - << "Content-Type: application/json\r\n" - << "Content-Length: " << strMsg.size() << "\r\n" - << "Accept: application/json\r\n"; - BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders) - s << item.first << ": " << item.second << "\r\n"; - s << "\r\n" << strMsg; - - return s.str(); -} - -string rfc1123Time() -{ - char buffer[64]; - time_t now; - time(&now); - struct tm* now_gmt = gmtime(&now); - string locale(setlocale(LC_TIME, NULL)); - setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); - setlocale(LC_TIME, locale.c_str()); - return string(buffer); -} - -static string HTTPReply(int nStatus, const string& strMsg) -{ - if (nStatus == 401) - return strprintf("HTTP/1.0 401 Authorization Required\r\n" - "Date: %s\r\n" - "Server: bitcoin-json-rpc/%s\r\n" - "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" - "Content-Type: text/html\r\n" - "Content-Length: 296\r\n" - "\r\n" - "\r\n" - "\r\n" - "\r\n" - "Error\r\n" - "\r\n" - "\r\n" - "

401 Unauthorized.

\r\n" - "\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); - string strStatus; - if (nStatus == 200) strStatus = "OK"; - else if (nStatus == 400) strStatus = "Bad Request"; - else if (nStatus == 403) strStatus = "Forbidden"; - else if (nStatus == 404) strStatus = "Not Found"; - else if (nStatus == 500) strStatus = "Internal Server Error"; - return strprintf( - "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Connection: close\r\n" - "Content-Length: %d\r\n" - "Content-Type: application/json\r\n" - "Server: bitcoin-json-rpc/%s\r\n" - "\r\n" - "%s", - nStatus, - strStatus.c_str(), - rfc1123Time().c_str(), - strMsg.size(), - FormatFullVersion().c_str(), - strMsg.c_str()); -} - -int ReadHTTPStatus(std::basic_istream& stream) -{ - string str; - getline(stream, str); - vector vWords; - boost::split(vWords, str, boost::is_any_of(" ")); - if (vWords.size() < 2) - return 500; - return atoi(vWords[1].c_str()); -} - -int ReadHTTPHeader(std::basic_istream& stream, map& mapHeadersRet) -{ - int nLen = 0; - loop - { - string str; - std::getline(stream, str); - if (str.empty() || str == "\r") - break; - string::size_type nColon = str.find(":"); - if (nColon != string::npos) - { - string strHeader = str.substr(0, nColon); - boost::trim(strHeader); - boost::to_lower(strHeader); - string strValue = str.substr(nColon+1); - boost::trim(strValue); - mapHeadersRet[strHeader] = strValue; - if (strHeader == "content-length") - nLen = atoi(strValue.c_str()); - } - } - return nLen; -} - -int ReadHTTP(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet) -{ - mapHeadersRet.clear(); - strMessageRet = ""; - - // Read status - int nStatus = ReadHTTPStatus(stream); - - // Read header - int nLen = ReadHTTPHeader(stream, mapHeadersRet); - if (nLen < 0 || nLen > MAX_SIZE) - return 500; - - // Read message - if (nLen > 0) - { - vector vch(nLen); - stream.read(&vch[0], nLen); - strMessageRet = string(vch.begin(), vch.end()); - } - - return nStatus; -} - -string EncodeBase64(string s) -{ - BIO *b64, *bmem; - BUF_MEM *bptr; - - b64 = BIO_new(BIO_f_base64()); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_new(BIO_s_mem()); - b64 = BIO_push(b64, bmem); - BIO_write(b64, s.c_str(), s.size()); - BIO_flush(b64); - BIO_get_mem_ptr(b64, &bptr); - - string result(bptr->data, bptr->length); - BIO_free_all(b64); - - return result; -} - -string DecodeBase64(string s) -{ - BIO *b64, *bmem; - - char* buffer = static_cast(calloc(s.size(), sizeof(char))); - - b64 = BIO_new(BIO_f_base64()); - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_new_mem_buf(const_cast(s.c_str()), s.size()); - bmem = BIO_push(b64, bmem); - BIO_read(bmem, buffer, s.size()); - BIO_free_all(bmem); - - string result(buffer); - free(buffer); - return result; -} - -bool HTTPAuthorized(map& mapHeaders) -{ - string strAuth = mapHeaders["authorization"]; - if (strAuth.substr(0,6) != "Basic ") - return false; - string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); - string strUserPass = DecodeBase64(strUserPass64); - string::size_type nColon = strUserPass.find(":"); - if (nColon == string::npos) - return false; - string strUser = strUserPass.substr(0, nColon); - string strPassword = strUserPass.substr(nColon+1); - return (strUser == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]); -} - -// -// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, -// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were -// unspecified (HTTP errors and contents of 'error'). -// -// 1.0 spec: http://json-rpc.org/wiki/specification -// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http -// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx -// - -string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) -{ - Object request; - request.push_back(Pair("method", strMethod)); - request.push_back(Pair("params", params)); - request.push_back(Pair("id", id)); - return write_string(Value(request), false) + "\n"; -} - -string JSONRPCReply(const Value& result, const Value& error, const Value& id) -{ - Object reply; - if (error.type() != null_type) - reply.push_back(Pair("result", Value::null)); - else - reply.push_back(Pair("result", result)); - reply.push_back(Pair("error", error)); - reply.push_back(Pair("id", id)); - return write_string(Value(reply), false) + "\n"; -} - -void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) -{ - // Send error reply from json-rpc error object - int nStatus = 500; - int code = find_value(objError, "code").get_int(); - if (code == -32600) nStatus = 400; - else if (code == -32601) nStatus = 404; - string strReply = JSONRPCReply(Value::null, objError, id); - stream << HTTPReply(nStatus, strReply) << std::flush; -} - -bool ClientAllowed(const string& strAddress) -{ - if (strAddress == asio::ip::address_v4::loopback().to_string()) - return true; - const vector& vAllow = mapMultiArgs["-rpcallowip"]; - BOOST_FOREACH(string strAllow, vAllow) - if (WildcardMatch(strAddress, strAllow)) - return true; - return false; -} - -#ifdef USE_SSL -// -// IOStream device that speaks SSL but can also speak non-SSL -// -class SSLIOStreamDevice : public iostreams::device { -public: - SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn) - { - fUseSSL = fUseSSLIn; - fNeedHandshake = fUseSSLIn; - } - - void handshake(ssl::stream_base::handshake_type role) - { - if (!fNeedHandshake) return; - fNeedHandshake = false; - stream.handshake(role); - } - std::streamsize read(char* s, std::streamsize n) - { - handshake(ssl::stream_base::server); // HTTPS servers read first - if (fUseSSL) return stream.read_some(asio::buffer(s, n)); - return stream.next_layer().read_some(asio::buffer(s, n)); - } - std::streamsize write(const char* s, std::streamsize n) - { - handshake(ssl::stream_base::client); // HTTPS clients write first - if (fUseSSL) return asio::write(stream, asio::buffer(s, n)); - return asio::write(stream.next_layer(), asio::buffer(s, n)); - } - bool connect(const std::string& server, const std::string& port) - { - ip::tcp::resolver resolver(stream.get_io_service()); - ip::tcp::resolver::query query(server.c_str(), port.c_str()); - ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); - ip::tcp::resolver::iterator end; - boost::system::error_code error = asio::error::host_not_found; - while (error && endpoint_iterator != end) - { - stream.lowest_layer().close(); - stream.lowest_layer().connect(*endpoint_iterator++, error); - } - if (error) - return false; - return true; - } - -private: - bool fNeedHandshake; - bool fUseSSL; - SSLStream& stream; -}; -#endif - -void ThreadRPCServer(void* parg) -{ - IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg)); - try - { - vnThreadsRunning[4]++; - ThreadRPCServer2(parg); - vnThreadsRunning[4]--; - } - catch (std::exception& e) { - vnThreadsRunning[4]--; - PrintException(&e, "ThreadRPCServer()"); - } catch (...) { - vnThreadsRunning[4]--; - PrintException(NULL, "ThreadRPCServer()"); - } - printf("ThreadRPCServer exiting\n"); -} - -void ThreadRPCServer2(void* parg) -{ - printf("ThreadRPCServer started\n"); - - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") - { - string strWhatAmI = "To use bitcoind"; - if (mapArgs.count("-server")) - strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); - else if (mapArgs.count("-daemon")) - strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); - PrintConsole( - _("Warning: %s, you must set rpcpassword=\nin the configuration file: %s\n" - "If the file does not exist, create it with owner-readable-only file permissions.\n"), - strWhatAmI.c_str(), - GetConfigFile().c_str()); - CreateThread(Shutdown, NULL); - return; - } - - bool fUseSSL = GetBoolArg("-rpcssl"); - asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); - - asio::io_service io_service; - ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332)); - ip::tcp::acceptor acceptor(io_service, endpoint); - - acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); - -#ifdef USE_SSL - ssl::context context(io_service, ssl::context::sslv23); - if (fUseSSL) - { - context.set_options(ssl::context::no_sslv2); - filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert"); - if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile; - if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str()); - else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str()); - filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem"); - if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile; - if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem); - else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str()); - - string ciphers = GetArg("-rpcsslciphers", - "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); - SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str()); - } -#else - if (fUseSSL) - throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); -#endif - - loop - { - // Accept connection -#ifdef USE_SSL - SSLStream sslStream(io_service, context); - SSLIOStreamDevice d(sslStream, fUseSSL); - iostreams::stream stream(d); -#else - ip::tcp::iostream stream; -#endif - - ip::tcp::endpoint peer; - vnThreadsRunning[4]--; -#ifdef USE_SSL - acceptor.accept(sslStream.lowest_layer(), peer); -#else - acceptor.accept(*stream.rdbuf(), peer); -#endif - vnThreadsRunning[4]++; - if (fShutdown) - return; - - // Restrict callers by IP - if (!ClientAllowed(peer.address().to_string())) - { - // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. - if (!fUseSSL) - stream << HTTPReply(403, "") << std::flush; - continue; - } - - map mapHeaders; - string strRequest; - - boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest)); - if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30)))) - { // Timed out: - acceptor.cancel(); - printf("ThreadRPCServer ReadHTTP timeout\n"); - continue; - } - - // Check authorization - if (mapHeaders.count("authorization") == 0) - { - stream << HTTPReply(401, "") << std::flush; - continue; - } - if (!HTTPAuthorized(mapHeaders)) - { - // Deter brute-forcing short passwords - if (mapArgs["-rpcpassword"].size() < 15) - Sleep(50); - - stream << HTTPReply(401, "") << std::flush; - printf("ThreadRPCServer incorrect password attempt\n"); - continue; - } - - Value id = Value::null; - try - { - // Parse request - Value valRequest; - if (!read_string(strRequest, valRequest) || valRequest.type() != obj_type) - throw JSONRPCError(-32700, "Parse error"); - const Object& request = valRequest.get_obj(); - - // Parse id now so errors from here on will have the id - id = find_value(request, "id"); - - // Parse method - Value valMethod = find_value(request, "method"); - if (valMethod.type() == null_type) - throw JSONRPCError(-32600, "Missing method"); - if (valMethod.type() != str_type) - throw JSONRPCError(-32600, "Method must be a string"); - string strMethod = valMethod.get_str(); - if (strMethod != "getwork") - printf("ThreadRPCServer method=%s\n", strMethod.c_str()); - - // Parse params - Value valParams = find_value(request, "params"); - Array params; - if (valParams.type() == array_type) - params = valParams.get_array(); - else if (valParams.type() == null_type) - params = Array(); - else - throw JSONRPCError(-32600, "Params must be an array"); - - // Find method - map::iterator mi = mapCallTable.find(strMethod); - if (mi == mapCallTable.end()) - throw JSONRPCError(-32601, "Method not found"); - - // Observe safe mode - string strWarning = GetWarnings("rpc"); - if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod)) - throw JSONRPCError(-2, string("Safe mode: ") + strWarning); - - try - { - // Execute - Value result = (*(*mi).second)(params, false); - - // Send reply - string strReply = JSONRPCReply(result, Value::null, id); - stream << HTTPReply(200, strReply) << std::flush; - } - catch (std::exception& e) - { - ErrorReply(stream, JSONRPCError(-1, e.what()), id); - } - } - catch (Object& objError) - { - ErrorReply(stream, objError, id); - } - catch (std::exception& e) - { - ErrorReply(stream, JSONRPCError(-32700, e.what()), id); - } - } -} - - - - -Object CallRPC(const string& strMethod, const Array& params) -{ - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") - throw runtime_error(strprintf( - _("You must set rpcpassword= in the configuration file:\n%s\n" - "If the file does not exist, create it with owner-readable-only file permissions."), - GetConfigFile().c_str())); - - // Connect to localhost - bool fUseSSL = GetBoolArg("-rpcssl"); -#ifdef USE_SSL - asio::io_service io_service; - ssl::context context(io_service, ssl::context::sslv23); - context.set_options(ssl::context::no_sslv2); - SSLStream sslStream(io_service, context); - SSLIOStreamDevice d(sslStream, fUseSSL); - iostreams::stream stream(d); - if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"))) - throw runtime_error("couldn't connect to server"); -#else - if (fUseSSL) - throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); - - ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")); - if (stream.fail()) - throw runtime_error("couldn't connect to server"); -#endif - - - // HTTP basic authentication - string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); - map mapRequestHeaders; - mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; - - // Send request - string strRequest = JSONRPCRequest(strMethod, params, 1); - string strPost = HTTPPost(strRequest, mapRequestHeaders); - stream << strPost << std::flush; - - // Receive reply - map mapHeaders; - string strReply; - int nStatus = ReadHTTP(stream, mapHeaders, strReply); - if (nStatus == 401) - throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); - else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500) - throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); - else if (strReply.empty()) - throw runtime_error("no response from server"); - - // Parse reply - Value valReply; - if (!read_string(strReply, valReply)) - throw runtime_error("couldn't parse reply from server"); - const Object& reply = valReply.get_obj(); - if (reply.empty()) - throw runtime_error("expected reply to have result, error and id properties"); - - return reply; -} - - - - -template -void ConvertTo(Value& value) -{ - if (value.type() == str_type) - { - // reinterpret string as unquoted json value - Value value2; - if (!read_string(value.get_str(), value2)) - throw runtime_error("type mismatch"); - value = value2.get_value(); - } - else - { - value = value.get_value(); - } -} - -int CommandLineRPC(int argc, char *argv[]) -{ - string strPrint; - int nRet = 0; - try - { - // Skip switches - while (argc > 1 && IsSwitchChar(argv[1][0])) - { - argc--; - argv++; - } - - // Method - if (argc < 2) - throw runtime_error("too few parameters"); - string strMethod = argv[1]; - - // Parameters default to strings - Array params; - for (int i = 2; i < argc; i++) - params.push_back(argv[i]); - int n = params.size(); - - // - // Special case non-string parameter types - // - if (strMethod == "setgenerate" && n > 0) ConvertTo(params[0]); - if (strMethod == "setgenerate" && n > 1) ConvertTo(params[1]); - if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); - if (strMethod == "getamountreceived" && n > 1) ConvertTo(params[1]); // deprecated - if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo(params[1]); - if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated - if (strMethod == "getallreceived" && n > 0) ConvertTo(params[0]); // deprecated - if (strMethod == "getallreceived" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo(params[0]); - if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo(params[0]); - if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo(params[0]); // deprecated - if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo(params[1]); // deprecated - if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); - if (strMethod == "move" && n > 2) ConvertTo(params[2]); - if (strMethod == "move" && n > 3) ConvertTo(params[3]); - if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]); - if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); - if (strMethod == "listtransactions" && n > 1) ConvertTo(params[1]); - if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); - if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); - if (strMethod == "sendmany" && n > 1) - { - string s = params[1].get_str(); - Value v; - if (!read_string(s, v) || v.type() != obj_type) - throw runtime_error("type mismatch"); - params[1] = v.get_obj(); - } - if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); - - // Execute - Object reply = CallRPC(strMethod, params); - - // Parse reply - const Value& result = find_value(reply, "result"); - const Value& error = find_value(reply, "error"); - const Value& id = find_value(reply, "id"); - - if (error.type() != null_type) - { - // Error - strPrint = "error: " + write_string(error, false); - int code = find_value(error.get_obj(), "code").get_int(); - nRet = abs(code); - } - else - { - // Result - if (result.type() == null_type) - strPrint = ""; - else if (result.type() == str_type) - strPrint = result.get_str(); - else - strPrint = write_string(result, true); - } - } - catch (std::exception& e) - { - strPrint = string("error: ") + e.what(); - nRet = 87; - } - catch (...) - { - PrintException(NULL, "CommandLineRPC()"); - } - - if (strPrint != "") - { -#if defined(__WXMSW__) && defined(GUI) - // Windows GUI apps can't print to command line, - // so settle for a message box yuck - MyMessageBox(strPrint, "Bitcoin", wxOK); -#else - fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); -#endif - } - return nRet; -} - - - - -#ifdef TEST -int main(int argc, char *argv[]) -{ -#ifdef _MSC_VER - // Turn off microsoft heap dump noise - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); -#endif - setbuf(stdin, NULL); - setbuf(stdout, NULL); - setbuf(stderr, NULL); - - try - { - if (argc >= 2 && string(argv[1]) == "-server") - { - printf("server ready\n"); - ThreadRPCServer(NULL); - } - else - { - return CommandLineRPC(argc, argv); - } - } - catch (std::exception& e) { - PrintException(&e, "main()"); - } catch (...) { - PrintException(NULL, "main()"); - } - return 0; -} -#endif diff --git a/src/rpc.h b/src/rpc.h deleted file mode 100644 index 48a7b8a8a6..0000000000 --- a/src/rpc.h +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. - -void ThreadRPCServer(void* parg); -int CommandLineRPC(int argc, char *argv[]); diff --git a/src/util.cpp b/src/util.cpp index 479c601ee5..3d89f6a829 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -263,8 +263,7 @@ int my_snprintf(char* buffer, size_t limit, const char* format, ...) return ret; } - -string strprintf(const char* format, ...) +string strprintf(const std::string &format, ...) { char buffer[50000]; char* p = buffer; @@ -274,7 +273,7 @@ string strprintf(const char* format, ...) { va_list arg_ptr; va_start(arg_ptr, format); - ret = _vsnprintf(p, limit, format, arg_ptr); + ret = _vsnprintf(p, limit, format.c_str(), arg_ptr); va_end(arg_ptr); if (ret >= 0 && ret < limit) break; @@ -291,14 +290,13 @@ string strprintf(const char* format, ...) return str; } - -bool error(const char* format, ...) +bool error(const std::string &format, ...) { char buffer[50000]; int limit = sizeof(buffer); va_list arg_ptr; va_start(arg_ptr, format); - int ret = _vsnprintf(buffer, limit, format, arg_ptr); + int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr); va_end(arg_ptr); if (ret < 0 || ret >= limit) { diff --git a/src/util.h b/src/util.h index e7110570c6..4c1e74b7da 100644 --- a/src/util.h +++ b/src/util.h @@ -64,7 +64,7 @@ typedef unsigned long long uint64; #endif // This is needed because the foreach macro can't get over the comma in pair -#define PAIRTYPE(t1, t2) pair +#define PAIRTYPE(t1, t2) std::pair // Used to bypass the rule against non-const reference to temporary // where it makes sense with wrappers such as CFlatData or CTxDB @@ -139,8 +139,7 @@ inline int myclosesocket(SOCKET& hSocket) return ret; } #define closesocket(s) myclosesocket(s) - -#ifndef GUI +#if !defined(QT_GUI) && !defined(GUI) inline const char* _(const char* psz) { return psz; @@ -155,7 +154,6 @@ inline const char* _(const char* psz) - extern std::map mapArgs; extern std::map > mapMultiArgs; extern bool fDebug; @@ -176,8 +174,8 @@ void RandAddSeed(); void RandAddSeedPerfmon(); int OutputDebugStringF(const char* pszFormat, ...); int my_snprintf(char* buffer, size_t limit, const char* format, ...); -std::string strprintf(const char* format, ...); -bool error(const char* format, ...); +std::string strprintf(const std::string &format, ...); +bool error(const std::string &format, ...); void LogException(std::exception* pex, const char* pszThread); void PrintException(std::exception* pex, const char* pszThread); void PrintExceptionContinue(std::exception* pex, const char* pszThread); diff --git a/src/wallet.cpp b/src/wallet.cpp index 6ef75ef27f..5b88f387c7 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -91,7 +91,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) if (fInsertedNew || fUpdated) if (!wtx.WriteToDisk()) return false; - +#ifndef QT_GUI // If default receiving address gets used, replace it with a new one CScript scriptDefaultKey; scriptDefaultKey.SetBitcoinAddress(vchDefaultKey); @@ -100,7 +100,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) if (txout.scriptPubKey == scriptDefaultKey) SetDefaultKey(GetKeyFromKeyPool()); } - +#endif // Notify UI vWalletUpdated.push_back(hash); -- cgit v1.2.3 From ae3d0aba158d0a38c33d687e5473d688fbcb903d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 15:22:54 +0200 Subject: Sync to bitcoin git e94010b2395694d56dd6 --- src/bitcoinrpc.cpp | 25 ++++++++--- src/init.cpp | 4 +- src/keystore.cpp | 1 + src/main.cpp | 19 +++++--- src/net.cpp | 41 ++++++++++++------ src/net.h | 2 + src/qt/addresstablemodel.cpp | 16 ++++--- src/serialize.h | 2 +- src/util.cpp | 7 --- src/wallet.cpp | 101 ++++++++++++++----------------------------- src/wallet.h | 9 ++-- 11 files changed, 113 insertions(+), 114 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 644ad92297..0493c6a08f 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -13,7 +13,7 @@ #include #include #ifdef USE_SSL -#include +#include #include #include typedef boost::asio::ssl::stream SSLStream; @@ -332,12 +332,15 @@ Value getnewaddress(const Array& params, bool fHelp) // Generate a new key that is added to wallet string strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool()); - pwalletMain->SetAddressBookName(strAddress, strAccount); + // This could be done in the same main CS as GetKeyFromKeyPool. + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) + pwalletMain->SetAddressBookName(strAddress, strAccount); + return strAddress; } -// requires cs_main, cs_mapWallet locks +// requires cs_main, cs_mapWallet, cs_mapAddressBook locks string GetAccountAddress(string strAccount, bool bForceNew=false) { string strAddress; @@ -393,6 +396,7 @@ Value getaccountaddress(const Array& params, bool fHelp) CRITICAL_BLOCK(cs_main) CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) { ret = GetAccountAddress(strAccount); } @@ -431,9 +435,10 @@ Value setaccount(const Array& params, bool fHelp) if (strAddress == GetAccountAddress(strOldAccount)) GetAccountAddress(strOldAccount, true); } + + pwalletMain->SetAddressBookName(strAddress, strAccount); } - pwalletMain->SetAddressBookName(strAddress, strAccount); return Value::null; } @@ -838,7 +843,7 @@ Value sendmany(const Array& params, bool fHelp) CScript scriptPubKey; if (!scriptPubKey.SetBitcoinAddress(strAddress)) throw JSONRPCError(-5, string("Invalid bitcoin address:")+strAddress); - int64 nAmount = AmountFromValue(s.value_); + int64 nAmount = AmountFromValue(s.value_); totalAmount += nAmount; vecSend.push_back(make_pair(scriptPubKey, nAmount)); @@ -1156,7 +1161,7 @@ Value listtransactions(const Array& params, bool fHelp) } // ret is now newest to oldest } - + // Make sure we return only last nCount items (sends-to-self might give us an extra): if (ret.size() > nCount) { @@ -1532,7 +1537,7 @@ string rfc1123Time() return string(buffer); } -string HTTPReply(int nStatus, const string& strMsg) +static string HTTPReply(int nStatus, const string& strMsg) { if (nStatus == 401) return strprintf("HTTP/1.0 401 Authorization Required\r\n" @@ -1554,6 +1559,7 @@ string HTTPReply(int nStatus, const string& strMsg) string strStatus; if (nStatus == 200) strStatus = "OK"; else if (nStatus == 400) strStatus = "Bad Request"; + else if (nStatus == 403) strStatus = "Forbidden"; else if (nStatus == 404) strStatus = "Not Found"; else if (nStatus == 500) strStatus = "Internal Server Error"; return strprintf( @@ -1887,7 +1893,12 @@ void ThreadRPCServer2(void* parg) // Restrict callers by IP if (!ClientAllowed(peer.address().to_string())) + { + // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. + if (!fUseSSL) + stream << HTTPReply(403, "") << std::flush; continue; + } map mapHeaders; string strRequest; diff --git a/src/init.cpp b/src/init.cpp index cac921e2b2..adbfa18c6a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -493,7 +493,9 @@ bool AppInit2(int argc, char* argv[]) } } - if (mapArgs.count("-dnsseed")) + if (GetBoolArg("-nodnsseed")) + printf("DNS seeding disabled\n"); + else DNSAddressSeed(); if (mapArgs.count("-paytxfee")) diff --git a/src/keystore.cpp b/src/keystore.cpp index 7dd045fe5f..bfad27c6d3 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -29,5 +29,6 @@ bool CKeyStore::AddKey(const CKey& key) mapKeys[key.GetPubKey()] = key.GetPrivKey(); mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey(); } + return true; } diff --git a/src/main.cpp b/src/main.cpp index 54902e82eb..594f1d3bc4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,8 +32,8 @@ map mapNextTx; map mapBlockIndex; uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); -const int nTotalBlocksEstimate = 131000; // Conservative estimate of total nr of blocks on main chain -const int nInitialBlockThreshold = 10000; // Regard blocks up until N-threshold as "initial download" +const int nTotalBlocksEstimate = 134444; // Conservative estimate of total nr of blocks on main chain +const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download" CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; CBigNum bnBestChainWork = 0; @@ -1294,7 +1294,8 @@ bool CBlock::AcceptBlock() (nHeight == 70567 && hash != uint256("0x00000000006a49b14bcf27462068f1264c961f11fa2e0eddd2be0791e1d4124a")) || (nHeight == 74000 && hash != uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")) || (nHeight == 105000 && hash != uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")) || - (nHeight == 118000 && hash != uint256("0x000000000000774a7f8a7a12dc906ddb9e17e75d684f15e00f8767f9e8f36553"))) + (nHeight == 118000 && hash != uint256("0x000000000000774a7f8a7a12dc906ddb9e17e75d684f15e00f8767f9e8f36553")) || + (nHeight == 134444 && hash != uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe"))) return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight); // Write block to history file @@ -1311,7 +1312,7 @@ bool CBlock::AcceptBlock() if (hashBestChain == hash) CRITICAL_BLOCK(cs_vNodes) BOOST_FOREACH(CNode* pnode, vNodes) - if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 118000)) + if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 134444)) pnode->PushInventory(CInv(MSG_BLOCK, hash)); return true; @@ -2040,20 +2041,24 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (pindex) pindex = pindex->pnext; int nLimit = 500 + locator.GetDistanceBack(); + unsigned int nBytes = 0; printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit); for (; pindex; pindex = pindex->pnext) { if (pindex->GetBlockHash() == hashStop) { - printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); + printf(" getblocks stopping at %d %s (%u bytes)\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str(), nBytes); break; } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); - if (--nLimit <= 0) + CBlock block; + block.ReadFromDisk(pindex, true); + nBytes += block.GetSerializeSize(SER_NETWORK); + if (--nLimit <= 0 || nBytes >= SendBufferSize()/2) { // When this block is requested, we'll send an inv that'll make them // getblocks the next batch of inventory. - printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); + printf(" getblocks stopping at limit %d %s (%u bytes)\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str(), nBytes); pfrom->hashContinue = pindex->GetBlockHash(); break; } diff --git a/src/net.cpp b/src/net.cpp index 4b13726230..0d3348da72 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -9,6 +9,15 @@ #include "init.h" #include "strlcpy.h" +#ifdef __WXMSW__ +#include +// This file can be downloaded as a part of the Windows Platform SDK +// and is required for Bitcoin binaries to work properly on versions +// of Windows before XP. If you are doing builds of Bitcoin for +// public release, you should uncomment this line. +//#include +#endif + #ifdef USE_UPNP #include #include @@ -148,7 +157,7 @@ bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet, int nTimeout } if (nRet != 0) { - printf("connect() failed after select(): %i\n",nRet); + printf("connect() failed after select(): %s\n",strerror(nRet)); closesocket(hSocket); return false; } @@ -159,7 +168,7 @@ bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet, int nTimeout else #endif { - printf("connect() failed: %s\n",WSAGetLastError()); + printf("connect() failed: %i\n",WSAGetLastError()); closesocket(hSocket); return false; } @@ -915,7 +924,7 @@ void ThreadSocketHandler2(void* parg) CDataStream& vRecv = pnode->vRecv; unsigned int nPos = vRecv.size(); - if (nPos > 1000*GetArg("-maxreceivebuffer", 10*1000)) { + if (nPos > ReceiveBufferSize()) { if (!pnode->fDisconnect) printf("socket recv flood control disconnect (%d bytes)\n", vRecv.size()); pnode->CloseSocketDisconnect(); @@ -980,7 +989,7 @@ void ThreadSocketHandler2(void* parg) pnode->CloseSocketDisconnect(); } } - if (vSend.size() > 1000*GetArg("-maxsendbuffer", 10*1000)) { + if (vSend.size() > SendBufferSize()) { if (!pnode->fDisconnect) printf("socket send flood control disconnect (%d bytes)\n", vSend.size()); pnode->CloseSocketDisconnect(); @@ -1139,25 +1148,29 @@ void MapPort(bool fMapPort) static const char *strDNSSeed[] = { "bitseed.xf2.org", "bitseed.bitcoin.org.uk", + "dnsseed.bluematt.me", }; void DNSAddressSeed() { int found = 0; - printf("Loading addresses from DNS seeds (could take a while)\n"); + if (!fTestNet) + { + printf("Loading addresses from DNS seeds (could take a while)\n"); - for (int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) { - vector vaddr; - if (Lookup(strDNSSeed[seed_idx], vaddr, NODE_NETWORK, -1, true)) - { - BOOST_FOREACH (CAddress& addr, vaddr) + for (int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) { + vector vaddr; + if (Lookup(strDNSSeed[seed_idx], vaddr, NODE_NETWORK, -1, true)) { - if (addr.GetByte(3) != 127) + BOOST_FOREACH (CAddress& addr, vaddr) { - addr.nTime = 0; - AddAddress(addr); - found++; + if (addr.GetByte(3) != 127) + { + addr.nTime = 0; + AddAddress(addr); + found++; + } } } } diff --git a/src/net.h b/src/net.h index cafb175d94..8f21de8dc9 100644 --- a/src/net.h +++ b/src/net.h @@ -24,6 +24,8 @@ extern int nConnectTimeout; +inline unsigned int ReceiveBufferSize() { return 1000*GetArg("-maxreceivebuffer", 10*1000); } +inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 10*1000); } inline unsigned short GetDefaultPort() { return fTestNet ? 18333 : 8333; } static const unsigned int PUBLISH_HOPS = 5; enum diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index e375ff8cbc..d04989ea2e 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -165,10 +165,13 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu // Double-check that we're not overwriting receiving address if(rec->type == AddressTableEntry::Sending) { - // Remove old entry - wallet->EraseAddressBookName(rec->address.toStdString()); - // Add new entry with new address - wallet->SetAddressBookName(value.toString().toStdString(), rec->label.toStdString()); + CRITICAL_BLOCK(wallet->cs_mapAddressBook) + { + // Remove old entry + wallet->DelAddressBookName(rec->address.toStdString()); + // Add new entry with new address + wallet->SetAddressBookName(value.toString().toStdString(), rec->label.toStdString()); + } rec->address = value.toString(); } @@ -274,7 +277,10 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex & paren // Also refuse to remove receiving addresses. return false; } - wallet->EraseAddressBookName(rec->address.toStdString()); + CRITICAL_BLOCK(wallet->cs_mapAddressBook) + { + wallet->DelAddressBookName(rec->address.toStdString()); + } updateList(); return true; } diff --git a/src/serialize.h b/src/serialize.h index 0d66d6a956..31862a71a9 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -33,7 +33,7 @@ class CDataStream; class CAutoFile; static const unsigned int MAX_SIZE = 0x02000000; -static const int VERSION = 32300; +static const int VERSION = 32400; static const char* pszSubVer = ""; static const bool VERSION_IS_BETA = true; diff --git a/src/util.cpp b/src/util.cpp index 2bc2cdd599..3d89f6a829 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -344,11 +344,6 @@ string FormatMoney(int64 n, bool fPlus) if (nTrim) str.erase(str.size()-nTrim, nTrim); - // Insert thousands-separators: - size_t point = str.find("."); - for (int i = (str.size()-point)+3; i < str.size(); i += 4) - if (isdigit(str[str.size() - i - 1])) - str.insert(str.size() - i, 1, ','); if (n < 0) str.insert((unsigned int)0, 1, '-'); else if (fPlus && n > 0) @@ -371,8 +366,6 @@ bool ParseMoney(const char* pszIn, int64& nRet) p++; for (; *p; p++) { - if (*p == ',' && p > pszIn && isdigit(p[-1]) && isdigit(p[1]) && isdigit(p[2]) && isdigit(p[3]) && !isdigit(p[4])) - continue; if (*p == '.') { p++; diff --git a/src/wallet.cpp b/src/wallet.cpp index f896336773..5b88f387c7 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -98,14 +98,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (txout.scriptPubKey == scriptDefaultKey) - { - if (!fFileBacked) - continue; - CWalletDB walletdb(strWalletFile); - vchDefaultKey = GetKeyFromKeyPool(); - walletdb.WriteDefaultKey(vchDefaultKey); - walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); - } + SetDefaultKey(GetKeyFromKeyPool()); } #endif // Notify UI @@ -967,16 +960,33 @@ bool CWallet::LoadWallet(bool& fFirstRunRet) // Create new default key RandAddSeedPerfmon(); - vchDefaultKey = GetKeyFromKeyPool(); + SetDefaultKey(GetKeyFromKeyPool()); if (!SetAddressBookName(PubKeyToAddress(vchDefaultKey), "")) return false; - CWalletDB(strWalletFile).WriteDefaultKey(vchDefaultKey); } CreateThread(ThreadFlushWalletDB, &strWalletFile); return true; } + +bool CWallet::SetAddressBookName(const string& strAddress, const string& strName) +{ + mapAddressBook[strAddress] = strName; + if (!fFileBacked) + return false; + return CWalletDB(strWalletFile).WriteName(strAddress, strName); +} + +bool CWallet::DelAddressBookName(const string& strAddress) +{ + mapAddressBook.erase(strAddress); + if (!fFileBacked) + return false; + return CWalletDB(strWalletFile).EraseName(strAddress); +} + + void CWallet::PrintWallet(const CBlock& block) { CRITICAL_BLOCK(cs_mapWallet) @@ -1004,6 +1014,17 @@ bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) return false; } +bool CWallet::SetDefaultKey(const std::vector &vchPubKey) +{ + if (fFileBacked) + { + if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) + return false; + } + vchDefaultKey = vchPubKey; + return true; +} + bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) { if (!pwallet->fFileBacked) @@ -1070,65 +1091,6 @@ void CWallet::ReturnKey(int64 nIndex) printf("keypool return %"PRI64d"\n", nIndex); } -bool CWallet::SetAddressBookName(const std::string& strAddress, const std::string& strName) -{ - if (!fFileBacked) - return false; - if(CWalletDB(strWalletFile).WriteName(strAddress, strName)) - { - CRITICAL_BLOCK(cs_mapAddressBook) - mapAddressBook[strAddress] = strName; - return true; - } - else - { - return false; - } -} - -bool CWallet::EraseAddressBookName(const std::string& strAddress) -{ - if (!fFileBacked) - return false; - if(CWalletDB(strWalletFile).EraseName(strAddress)) - { - CRITICAL_BLOCK(cs_mapAddressBook) - mapAddressBook.erase(strAddress); - return true; - } - else - { - return false; - } -} - - -std::string CWallet::GetDefaultAddress() -{ - if (!fFileBacked) - return false; - std::vector vchPubKey; - if (CWalletDB(strWalletFile, "r").ReadDefaultKey(vchPubKey)) - { - return PubKeyToAddress(vchPubKey); - } - else - { - return ""; - } -} - -bool CWallet::SetDefaultAddress(const std::string& strAddress) -{ - uint160 hash160; - if (!AddressToHash160(strAddress, hash160)) - return false; - if (!mapPubKeys.count(hash160)) - return false; - return CWalletDB(strWalletFile).WriteDefaultKey(mapPubKeys[hash160]); -} - - vector CWallet::GetKeyFromKeyPool() { int64 nIndex = 0; @@ -1174,3 +1136,4 @@ void CReserveKey::ReturnKey() nIndex = -1; vchPubKey.clear(); } + diff --git a/src/wallet.h b/src/wallet.h index 69110a4afa..7d9db97267 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -149,10 +149,12 @@ public: bool LoadWallet(bool& fFirstRunRet); // bool BackupWallet(const std::string& strDest); + + // requires cs_mapAddressBook lock bool SetAddressBookName(const std::string& strAddress, const std::string& strName); - bool EraseAddressBookName(const std::string& strAddress); - std::string GetDefaultAddress(); - bool SetDefaultAddress(const std::string& strAddress); + + // requires cs_mapAddressBook lock + bool DelAddressBookName(const std::string& strAddress); void UpdatedTransaction(const uint256 &hashTx) { @@ -174,6 +176,7 @@ public: bool GetTransaction(const uint256 &hashTx, CWalletTx& wtx); + bool SetDefaultKey(const std::vector &vchPubKey); }; -- cgit v1.2.3 From 5eaa1b435c144d841c9a03fb9c478ef760f22d8c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 16:57:19 +0200 Subject: Qt handles the "..." for too long table rows. Remove this functionality from TransactionTableModel... --- src/qt/transactiontablemodel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 52c9b0ce84..6a7f7aab4a 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -320,7 +320,7 @@ QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const } /* Look up address in address book, if found return - address[0:12]... (label) + address (label) otherwise just return address */ QString TransactionTableModel::lookupAddress(const std::string &address) const @@ -333,7 +333,7 @@ QString TransactionTableModel::lookupAddress(const std::string &address) const } else { - description = label + QString(" (") + QString::fromStdString(address.substr(0,12)) + QString("...)"); + description = label + QString(" (") + QString::fromStdString(address) + QString(")"); } return description; } -- cgit v1.2.3 From 3479849dc47acd2fb1e191ea690a0c507a97bb73 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 17:33:15 +0200 Subject: convert to full tab-based ui --- bitcoin-qt.pro | 6 +- src/qt/addressbookdialog.cpp | 177 ------------------------------------- src/qt/addressbookdialog.h | 53 ----------- src/qt/addressbookpage.cpp | 181 ++++++++++++++++++++++++++++++++++++++ src/qt/addressbookpage.h | 57 ++++++++++++ src/qt/bitcoinamountfield.cpp | 5 ++ src/qt/bitcoingui.cpp | 151 +++++++++++++++++-------------- src/qt/bitcoingui.h | 34 ++++--- src/qt/forms/addressbookdialog.ui | 179 ------------------------------------- src/qt/forms/addressbookpage.ui | 130 +++++++++++++++++++++++++++ src/qt/res/icons/export.png | Bin 0 -> 1339 bytes src/qt/sendcoinsdialog.cpp | 34 ++++++- src/qt/sendcoinsdialog.h | 5 ++ 13 files changed, 519 insertions(+), 493 deletions(-) delete mode 100644 src/qt/addressbookdialog.cpp delete mode 100644 src/qt/addressbookdialog.h create mode 100644 src/qt/addressbookpage.cpp create mode 100644 src/qt/addressbookpage.h delete mode 100644 src/qt/forms/addressbookdialog.ui create mode 100644 src/qt/forms/addressbookpage.ui create mode 100644 src/qt/res/icons/export.png diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 77d70b7ba7..5e2646fe6f 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -22,7 +22,7 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/addresstablemodel.h \ src/qt/optionsdialog.h \ src/qt/sendcoinsdialog.h \ - src/qt/addressbookdialog.h \ + src/qt/addressbookpage.h \ src/qt/aboutdialog.h \ src/qt/editaddressdialog.h \ src/qt/bitcoinaddressvalidator.h \ @@ -84,7 +84,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/addresstablemodel.cpp \ src/qt/optionsdialog.cpp \ src/qt/sendcoinsdialog.cpp \ - src/qt/addressbookdialog.cpp \ + src/qt/addressbookpage.cpp \ src/qt/aboutdialog.cpp \ src/qt/editaddressdialog.cpp \ src/qt/bitcoinaddressvalidator.cpp \ @@ -123,7 +123,7 @@ RESOURCES += \ FORMS += \ src/qt/forms/sendcoinsdialog.ui \ - src/qt/forms/addressbookdialog.ui \ + src/qt/forms/addressbookpage.ui \ src/qt/forms/aboutdialog.ui \ src/qt/forms/editaddressdialog.ui \ src/qt/forms/transactiondescdialog.ui \ diff --git a/src/qt/addressbookdialog.cpp b/src/qt/addressbookdialog.cpp deleted file mode 100644 index 6d53ee8657..0000000000 --- a/src/qt/addressbookdialog.cpp +++ /dev/null @@ -1,177 +0,0 @@ -#include "addressbookdialog.h" -#include "ui_addressbookdialog.h" - -#include "addresstablemodel.h" -#include "editaddressdialog.h" - -#include -#include -#include - -AddressBookDialog::AddressBookDialog(Mode mode, QWidget *parent) : - QDialog(parent), - ui(new Ui::AddressBookDialog), - model(0), - mode(mode) -{ - ui->setupUi(this); - switch(mode) - { - case ForSending: - connect(ui->receiveTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_buttonBox_accepted())); - connect(ui->sendTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_buttonBox_accepted())); - ui->sendTableView->setFocus(); - break; - } - - connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(selectionChanged())); -} - -AddressBookDialog::~AddressBookDialog() -{ - delete ui; -} - -void AddressBookDialog::setModel(AddressTableModel *model) -{ - this->model = model; - // Refresh list from core - model->updateList(); - - // Receive filter - QSortFilterProxyModel *receive_model = new QSortFilterProxyModel(this); - receive_model->setSourceModel(model); - receive_model->setDynamicSortFilter(true); - receive_model->setFilterRole(AddressTableModel::TypeRole); - receive_model->setFilterFixedString(AddressTableModel::Receive); - ui->receiveTableView->setModel(receive_model); - ui->receiveTableView->sortByColumn(0, Qt::AscendingOrder); - - // Send filter - QSortFilterProxyModel *send_model = new QSortFilterProxyModel(this); - send_model->setSourceModel(model); - send_model->setDynamicSortFilter(true); - send_model->setFilterRole(AddressTableModel::TypeRole); - send_model->setFilterFixedString(AddressTableModel::Send); - ui->sendTableView->setModel(send_model); - ui->sendTableView->sortByColumn(0, Qt::AscendingOrder); - - // Set column widths - ui->receiveTableView->horizontalHeader()->resizeSection( - AddressTableModel::Address, 320); - ui->receiveTableView->horizontalHeader()->setResizeMode( - AddressTableModel::Label, QHeaderView::Stretch); - ui->sendTableView->horizontalHeader()->resizeSection( - AddressTableModel::Address, 320); - ui->sendTableView->horizontalHeader()->setResizeMode( - AddressTableModel::Label, QHeaderView::Stretch); - - connect(ui->receiveTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - this, SLOT(selectionChanged())); - connect(ui->sendTableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - this, SLOT(selectionChanged())); - - if(mode == ForSending) - { - // Auto-select first row when in sending mode - ui->sendTableView->selectRow(0); - } -} - -void AddressBookDialog::setTab(int tab) -{ - ui->tabWidget->setCurrentIndex(tab); - selectionChanged(); -} - -QTableView *AddressBookDialog::getCurrentTable() -{ - switch(ui->tabWidget->currentIndex()) - { - case SendingTab: - return ui->sendTableView; - case ReceivingTab: - return ui->receiveTableView; - default: - return 0; - } -} - -void AddressBookDialog::on_copyToClipboard_clicked() -{ - // Copy currently selected address to clipboard - // (or nothing, if nothing selected) - QTableView *table = getCurrentTable(); - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) - { - QVariant address = index.data(); - QApplication::clipboard()->setText(address.toString()); - } -} - -void AddressBookDialog::on_newAddressButton_clicked() -{ - EditAddressDialog dlg( - ui->tabWidget->currentIndex() == SendingTab ? - EditAddressDialog::NewSendingAddress : - EditAddressDialog::NewReceivingAddress); - dlg.setModel(model); - dlg.exec(); -} - -void AddressBookDialog::on_deleteButton_clicked() -{ - QTableView *table = getCurrentTable(); - QModelIndexList indexes = table->selectionModel()->selectedRows(); - if(!indexes.isEmpty()) - { - table->model()->removeRow(indexes.at(0).row()); - } -} - -void AddressBookDialog::on_buttonBox_accepted() -{ - QTableView *table = getCurrentTable(); - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) - { - QVariant address = table->model()->data(index); - returnValue = address.toString(); - } - if(!returnValue.isEmpty()) - { - accept(); - } - else - { - reject(); - } -} - -void AddressBookDialog::selectionChanged() -{ - // Set button states based on selected tab and selection - QTableView *table = getCurrentTable(); - - if(table->selectionModel()->hasSelection()) - { - switch(ui->tabWidget->currentIndex()) - { - case SendingTab: - ui->deleteButton->setEnabled(true); - break; - case ReceivingTab: - ui->deleteButton->setEnabled(false); - break; - } - ui->copyToClipboard->setEnabled(true); - } - else - { - ui->deleteButton->setEnabled(false); - ui->copyToClipboard->setEnabled(false); - } -} diff --git a/src/qt/addressbookdialog.h b/src/qt/addressbookdialog.h deleted file mode 100644 index befa8b7659..0000000000 --- a/src/qt/addressbookdialog.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef ADDRESSBOOKDIALOG_H -#define ADDRESSBOOKDIALOG_H - -#include - -namespace Ui { - class AddressBookDialog; -} -class AddressTableModel; - -QT_BEGIN_NAMESPACE -class QTableView; -class QItemSelection; -QT_END_NAMESPACE - -class AddressBookDialog : public QDialog -{ - Q_OBJECT - -public: - enum Tabs { - SendingTab = 0, - ReceivingTab = 1 - }; - - enum Mode { - ForSending, // Pick address for sending - ForEditing // Open address book for editing - }; - - explicit AddressBookDialog(Mode mode, QWidget *parent = 0); - ~AddressBookDialog(); - - void setModel(AddressTableModel *model); - void setTab(int tab); - const QString &getReturnValue() const { return returnValue; } -private: - Ui::AddressBookDialog *ui; - AddressTableModel *model; - Mode mode; - QString returnValue; - - QTableView *getCurrentTable(); - -private slots: - void on_buttonBox_accepted(); - void on_deleteButton_clicked(); - void on_newAddressButton_clicked(); - void on_copyToClipboard_clicked(); - void selectionChanged(); -}; - -#endif // ADDRESSBOOKDIALOG_H diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp new file mode 100644 index 0000000000..67ed0fecca --- /dev/null +++ b/src/qt/addressbookpage.cpp @@ -0,0 +1,181 @@ +#include "addressbookpage.h" +#include "ui_addressbookpage.h" + +#include "addresstablemodel.h" +#include "editaddressdialog.h" + +#include +#include +#include + +AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : + QDialog(parent), + ui(new Ui::AddressBookPage), + model(0), + mode(mode), + tab(tab) +{ + ui->setupUi(this); + switch(mode) + { + case ForSending: + connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_buttonBox_accepted())); + ui->tableView->setFocus(); + break; + case ForEditing: + ui->buttonBox->hide(); + break; + } + switch(tab) + { + case SendingTab: + ui->labelExplanation->hide(); + break; + case ReceivingTab: + break; + } +} + +AddressBookPage::~AddressBookPage() +{ + delete ui; +} + +void AddressBookPage::setModel(AddressTableModel *model) +{ + this->model = model; + // Refresh list from core + model->updateList(); + + switch(tab) + { + case ReceivingTab: { + // Receive filter + QSortFilterProxyModel *receive_model = new QSortFilterProxyModel(this); + receive_model->setSourceModel(model); + receive_model->setDynamicSortFilter(true); + receive_model->setFilterRole(AddressTableModel::TypeRole); + receive_model->setFilterFixedString(AddressTableModel::Receive); + ui->tableView->setModel(receive_model); + ui->tableView->sortByColumn(0, Qt::AscendingOrder); + } break; + case SendingTab: { + // Send filter + QSortFilterProxyModel *send_model = new QSortFilterProxyModel(this); + send_model->setSourceModel(model); + send_model->setDynamicSortFilter(true); + send_model->setFilterRole(AddressTableModel::TypeRole); + send_model->setFilterFixedString(AddressTableModel::Send); + ui->tableView->setModel(send_model); + ui->tableView->sortByColumn(0, Qt::AscendingOrder); + } break; + } + + // Set column widths + ui->tableView->horizontalHeader()->resizeSection( + AddressTableModel::Address, 320); + ui->tableView->horizontalHeader()->setResizeMode( + AddressTableModel::Label, QHeaderView::Stretch); + + connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(selectionChanged())); + + if(mode == ForSending) + { + // Auto-select first row when in sending mode + ui->tableView->selectRow(0); + } + selectionChanged(); +} + +QTableView *AddressBookPage::getCurrentTable() +{ + return ui->tableView; +} + +void AddressBookPage::on_copyToClipboard_clicked() +{ + // Copy currently selected address to clipboard + // (or nothing, if nothing selected) + QTableView *table = getCurrentTable(); + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QVariant address = index.data(); + QApplication::clipboard()->setText(address.toString()); + } +} + +void AddressBookPage::on_newAddressButton_clicked() +{ + EditAddressDialog dlg( + tab == SendingTab ? + EditAddressDialog::NewSendingAddress : + EditAddressDialog::NewReceivingAddress); + dlg.setModel(model); + dlg.exec(); +} + +void AddressBookPage::on_deleteButton_clicked() +{ + QTableView *table = getCurrentTable(); + QModelIndexList indexes = table->selectionModel()->selectedRows(); + if(!indexes.isEmpty()) + { + table->model()->removeRow(indexes.at(0).row()); + } +} + +void AddressBookPage::on_buttonBox_accepted() +{ + QTableView *table = getCurrentTable(); + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QVariant address = table->model()->data(index); + returnValue = address.toString(); + } + if(!returnValue.isEmpty()) + { + accept(); + } + else + { + reject(); + } +} + +void AddressBookPage::selectionChanged() +{ + // Set button states based on selected tab and selection + QTableView *table = getCurrentTable(); + + if(table->selectionModel()->hasSelection()) + { + switch(tab) + { + case SendingTab: + ui->deleteButton->setEnabled(true); + break; + case ReceivingTab: + ui->deleteButton->setEnabled(false); + break; + } + ui->copyToClipboard->setEnabled(true); + } + else + { + ui->deleteButton->setEnabled(false); + ui->copyToClipboard->setEnabled(false); + } +} + +void AddressBookPage::done(int retval) +{ + // When this is a tab/widget and not a model dialog, ignore "done" + if(mode == ForEditing) + return; + QDialog::done(retval); +} diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h new file mode 100644 index 0000000000..f25bbc990b --- /dev/null +++ b/src/qt/addressbookpage.h @@ -0,0 +1,57 @@ +#ifndef ADDRESSBOOKPAGE_H +#define ADDRESSBOOKPAGE_H + +#include + +namespace Ui { + class AddressBookPage; +} +class AddressTableModel; + +QT_BEGIN_NAMESPACE +class QTableView; +class QItemSelection; +QT_END_NAMESPACE + +class AddressBookPage : public QDialog +{ + Q_OBJECT + +public: + enum Tabs { + SendingTab = 0, + ReceivingTab = 1 + }; + + enum Mode { + ForSending, // Pick address for sending + ForEditing // Open address book for editing + }; + + explicit AddressBookPage(Mode mode, Tabs tab, QWidget *parent = 0); + ~AddressBookPage(); + + void setModel(AddressTableModel *model); + const QString &getReturnValue() const { return returnValue; } + +public slots: + void done(int retval); + +private: + Ui::AddressBookPage *ui; + AddressTableModel *model; + Mode mode; + Tabs tab; + QString returnValue; + + QTableView *getCurrentTable(); + +private slots: + void on_buttonBox_accepted(); + void on_deleteButton_clicked(); + void on_newAddressButton_clicked(); + void on_copyToClipboard_clicked(); + void selectionChanged(); +}; + +#endif // ADDRESSBOOKDIALOG_H diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index f16c0c51e5..1359a32b87 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -46,6 +46,11 @@ void BitcoinAmountField::setText(const QString &text) amount->setText(parts[0]); decimals->setText(parts[1]); } + else + { + amount->setText(QString()); + decimals->setText(QString()); + } } QString BitcoinAmountField::text() const diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 34da1350cd..99dbbc11ae 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -5,7 +5,7 @@ */ #include "bitcoingui.h" #include "transactiontablemodel.h" -#include "addressbookdialog.h" +#include "addressbookpage.h" #include "sendcoinsdialog.h" #include "optionsdialog.h" #include "aboutdialog.h" @@ -54,26 +54,25 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Menus QMenu *file = menuBar()->addMenu("&File"); - file->addAction(sendCoins); - file->addAction(receiveCoins); + file->addAction(sendCoinsAction); + file->addAction(receiveCoinsAction); file->addSeparator(); - file->addAction(quit); + file->addAction(quitAction); QMenu *settings = menuBar()->addMenu("&Settings"); - settings->addAction(options); + settings->addAction(optionsAction); QMenu *help = menuBar()->addMenu("&Help"); - help->addAction(about); + help->addAction(aboutAction); // Toolbar QToolBar *toolbar = addToolBar("Main toolbar"); toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolbar->addAction(overviewAction); + toolbar->addAction(sendCoinsAction); + toolbar->addAction(receiveCoinsAction); toolbar->addAction(historyAction); - toolbar->addSeparator(); - toolbar->addAction(sendCoins); - toolbar->addAction(receiveCoins); - toolbar->addAction(addressbook); + toolbar->addAction(addressBookAction); QToolBar *toolbar2 = addToolBar("Transactions toolbar"); toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); @@ -90,9 +89,18 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): transactionsPage = new QWidget(this); transactionsPage->setLayout(vbox); + addressBookPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab); + + receiveCoinsPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab); + + sendCoinsPage = new SendCoinsDialog(this); + centralWidget = new QStackedWidget(this); centralWidget->addWidget(overviewPage); centralWidget->addWidget(transactionsPage); + centralWidget->addWidget(addressBookPage); + centralWidget->addWidget(receiveCoinsPage); + centralWidget->addWidget(sendCoinsPage); setCentralWidget(centralWidget); // Create status bar @@ -122,46 +130,57 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): createTrayIcon(); - gotoOverviewTab(); + gotoOverviewPage(); } void BitcoinGUI::createActions() { QActionGroup *tabGroup = new QActionGroup(this); + overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Overview"), this); overviewAction->setCheckable(true); tabGroup->addAction(overviewAction); + historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this); historyAction->setCheckable(true); tabGroup->addAction(historyAction); - connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewTab())); - connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryTab())); - - quit = new QAction(QIcon(":/icons/quit"), tr("&Exit"), this); - quit->setToolTip(tr("Quit application")); - sendCoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); - sendCoins->setToolTip(tr("Send coins to a bitcoin address")); - addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); - addressbook->setToolTip(tr("Edit the list of stored addresses and labels")); - about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); - about->setToolTip(tr("Show information about Bitcoin")); - receiveCoins = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive coins"), this); - receiveCoins->setToolTip(tr("Show the list of addresses for receiving payments")); - options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); - options->setToolTip(tr("Modify configuration options for bitcoin")); - openBitcoin = new QAction(QIcon(":/icons/bitcoin"), tr("Open &Bitcoin"), this); - openBitcoin->setToolTip(tr("Show the Bitcoin window")); + addressBookAction = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); + addressBookAction->setToolTip(tr("Edit the list of stored addresses and labels")); + addressBookAction->setCheckable(true); + tabGroup->addAction(addressBookAction); + + receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive coins"), this); + receiveCoinsAction->setToolTip(tr("Show the list of addresses for receiving payments")); + receiveCoinsAction->setCheckable(true); + tabGroup->addAction(receiveCoinsAction); + + sendCoinsAction = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this); + sendCoinsAction->setToolTip(tr("Send coins to a bitcoin address")); + sendCoinsAction->setCheckable(true); + tabGroup->addAction(sendCoinsAction); + + connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage())); + connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage())); + connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage())); + connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); + connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage())); + + quitAction = new QAction(QIcon(":/icons/quit"), tr("&Exit"), this); + quitAction->setToolTip(tr("Quit application")); + aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this); + aboutAction->setToolTip(tr("Show information about Bitcoin")); + optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); + optionsAction->setToolTip(tr("Modify configuration options for bitcoin")); + openBitcoinAction = new QAction(QIcon(":/icons/bitcoin"), tr("Open &Bitcoin"), this); + openBitcoinAction->setToolTip(tr("Show the Bitcoin window")); exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this); exportAction->setToolTip(tr("Export data in current view to a file")); - connect(quit, SIGNAL(triggered()), qApp, SLOT(quit())); - connect(sendCoins, SIGNAL(triggered()), this, SLOT(sendCoinsClicked())); - connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked())); - connect(receiveCoins, SIGNAL(triggered()), this, SLOT(receiveCoinsClicked())); - connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked())); - connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); - connect(openBitcoin, SIGNAL(triggered()), this, SLOT(show())); + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked())); + connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); + connect(openBitcoinAction, SIGNAL(triggered()), this, SLOT(show())); connect(exportAction, SIGNAL(triggered()), this, SLOT(exportClicked())); } @@ -209,6 +228,10 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) // Put transaction list in tabs transactionView->setModel(walletModel->getTransactionTableModel()); + addressBookPage->setModel(walletModel->getAddressTableModel()); + receiveCoinsPage->setModel(walletModel->getAddressTableModel()); + sendCoinsPage->setModel(walletModel); + // Balloon popup for new transaction connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(incomingTransaction(const QModelIndex &, int, int))); @@ -217,11 +240,11 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) void BitcoinGUI::createTrayIcon() { QMenu *trayIconMenu = new QMenu(this); - trayIconMenu->addAction(openBitcoin); - trayIconMenu->addAction(sendCoins); - trayIconMenu->addAction(options); + trayIconMenu->addAction(openBitcoinAction); + trayIconMenu->addAction(sendCoinsAction); + trayIconMenu->addAction(optionsAction); trayIconMenu->addSeparator(); - trayIconMenu->addAction(quit); + trayIconMenu->addAction(quitAction); trayIcon = new QSystemTrayIcon(this); trayIcon->setContextMenu(trayIconMenu); @@ -237,33 +260,10 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) if(reason == QSystemTrayIcon::DoubleClick) { // Doubleclick on system tray icon triggers "open bitcoin" - openBitcoin->trigger(); + openBitcoinAction->trigger(); } } -void BitcoinGUI::sendCoinsClicked() -{ - SendCoinsDialog dlg; - dlg.setModel(walletModel); - dlg.exec(); -} - -void BitcoinGUI::addressbookClicked() -{ - AddressBookDialog dlg(AddressBookDialog::ForEditing); - dlg.setModel(walletModel->getAddressTableModel()); - dlg.setTab(AddressBookDialog::SendingTab); - dlg.exec(); -} - -void BitcoinGUI::receiveCoinsClicked() -{ - AddressBookDialog dlg(AddressBookDialog::ForEditing); - dlg.setModel(walletModel->getAddressTableModel()); - dlg.setTab(AddressBookDialog::ReceivingTab); - dlg.exec(); -} - void BitcoinGUI::optionsClicked() { OptionsDialog dlg; @@ -413,20 +413,41 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int } } -void BitcoinGUI::gotoOverviewTab() +void BitcoinGUI::gotoOverviewPage() { overviewAction->setChecked(true); centralWidget->setCurrentWidget(overviewPage); exportAction->setEnabled(false); } -void BitcoinGUI::gotoHistoryTab() +void BitcoinGUI::gotoHistoryPage() { historyAction->setChecked(true); centralWidget->setCurrentWidget(transactionsPage); exportAction->setEnabled(true); } +void BitcoinGUI::gotoAddressBookPage() +{ + addressBookAction->setChecked(true); + centralWidget->setCurrentWidget(addressBookPage); + exportAction->setEnabled(false); // TODO +} + +void BitcoinGUI::gotoReceiveCoinsPage() +{ + receiveCoinsAction->setChecked(true); + centralWidget->setCurrentWidget(receiveCoinsPage); + exportAction->setEnabled(false); // TODO +} + +void BitcoinGUI::gotoSendCoinsPage() +{ + sendCoinsAction->setChecked(true); + centralWidget->setCurrentWidget(sendCoinsPage); + exportAction->setEnabled(false); +} + void BitcoinGUI::exportClicked() { // Redirect to the right view, as soon as export for other views diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index a5fcc8a83a..8c3632a3a6 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -9,6 +9,8 @@ class ClientModel; class WalletModel; class TransactionView; class OverviewPage; +class AddressBookPage; +class SendCoinsDialog; QT_BEGIN_NAMESPACE class QLabel; @@ -45,8 +47,12 @@ private: WalletModel *walletModel; QStackedWidget *centralWidget; + OverviewPage *overviewPage; QWidget *transactionsPage; + AddressBookPage *addressBookPage; + AddressBookPage *receiveCoinsPage; + SendCoinsDialog *sendCoinsPage; QLabel *labelConnections; QLabel *labelConnectionsIcon; @@ -56,13 +62,13 @@ private: QAction *overviewAction; QAction *historyAction; - QAction *quit; - QAction *sendCoins; - QAction *addressbook; - QAction *about; - QAction *receiveCoins; - QAction *options; - QAction *openBitcoin; + QAction *quitAction; + QAction *sendCoinsAction; + QAction *addressBookAction; + QAction *aboutAction; + QAction *receiveCoinsAction; + QAction *optionsAction; + QAction *openBitcoinAction; QAction *exportAction; QSystemTrayIcon *trayIcon; @@ -85,18 +91,20 @@ public slots: void askFee(qint64 nFeeRequired, bool *payFee); private slots: - void sendCoinsClicked(); - void addressbookClicked(); + // UI pages + void gotoOverviewPage(); + void gotoHistoryPage(); + void gotoAddressBookPage(); + void gotoReceiveCoinsPage(); + void gotoSendCoinsPage(); + + // Misc actions void optionsClicked(); - void receiveCoinsClicked(); void aboutClicked(); void trayIconActivated(QSystemTrayIcon::ActivationReason reason); void transactionDetails(const QModelIndex& idx); void incomingTransaction(const QModelIndex & parent, int start, int end); void exportClicked(); - - void gotoOverviewTab(); - void gotoHistoryTab(); }; #endif diff --git a/src/qt/forms/addressbookdialog.ui b/src/qt/forms/addressbookdialog.ui deleted file mode 100644 index 66f1076afb..0000000000 --- a/src/qt/forms/addressbookdialog.ui +++ /dev/null @@ -1,179 +0,0 @@ - - - AddressBookDialog - - - - 0 - 0 - 627 - 347 - - - - Address Book - - - - - - 1 - - - - - - - Sending - - - - - - Double-click to edit address or label - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - true - - - false - - - - - - - - - - - Receiving - - - - - - These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. - - - Qt::AutoText - - - true - - - - - - - Double-click to edit address or label - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - true - - - false - - - - - - - - - - - - - Create a new address - - - &New Address... - - - - :/icons/add:/icons/add - - - - - - - Copy the currently selected address to the system clipboard - - - &Copy to Clipboard - - - - :/icons/editcopy:/icons/editcopy - - - - - - - Delete the currently selected address from the list. Only sending addresses can be deleted. - - - &Delete - - - - :/icons/editdelete:/icons/editdelete - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - QDialogButtonBox::Ok - - - - - - - - - - - - diff --git a/src/qt/forms/addressbookpage.ui b/src/qt/forms/addressbookpage.ui new file mode 100644 index 0000000000..d3feedb5e0 --- /dev/null +++ b/src/qt/forms/addressbookpage.ui @@ -0,0 +1,130 @@ + + + AddressBookPage + + + + 0 + 0 + 627 + 347 + + + + Address Book + + + + + + These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + Qt::AutoText + + + true + + + + + + + Double-click to edit address or label + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + + + + + + + Create a new address + + + &New Address... + + + + :/icons/add:/icons/add + + + + + + + Copy the currently selected address to the system clipboard + + + &Copy to Clipboard + + + + :/icons/editcopy:/icons/editcopy + + + + + + + Delete the currently selected address from the list. Only sending addresses can be deleted. + + + &Delete + + + + :/icons/editdelete:/icons/editdelete + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + QDialogButtonBox::Ok + + + + + + + + + + + + diff --git a/src/qt/res/icons/export.png b/src/qt/res/icons/export.png new file mode 100644 index 0000000000..69d59a38d2 Binary files /dev/null and b/src/qt/res/icons/export.png differ diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 4b974371fe..8050baff38 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -3,7 +3,7 @@ #include "walletmodel.h" #include "guiutil.h" -#include "addressbookdialog.h" +#include "addressbookpage.h" #include "optionsmodel.h" #include @@ -11,6 +11,7 @@ #include #include #include +#include SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : QDialog(parent), @@ -61,6 +62,16 @@ void SendCoinsDialog::on_sendButton_clicked() // Add address to address book under label, if specified label = ui->addAsLabel->text(); + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), + tr("Are you sure you want to send %1 BTC to %2 (%3)?").arg(GUIUtil::formatMoney(payAmountParsed), label, ui->payTo->text()), + QMessageBox::Yes|QMessageBox::Cancel, + QMessageBox::Cancel); + + if(retval != QMessageBox::Yes) + { + return; + } + switch(model->sendCoins(ui->payTo->text(), payAmountParsed, label)) { case WalletModel::InvalidAddress: @@ -102,9 +113,8 @@ void SendCoinsDialog::on_pasteButton_clicked() void SendCoinsDialog::on_addressBookButton_clicked() { - AddressBookDialog dlg(AddressBookDialog::ForSending); + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab); dlg.setModel(model->getAddressTableModel()); - dlg.setTab(AddressBookDialog::SendingTab); dlg.exec(); ui->payTo->setText(dlg.getReturnValue()); ui->payAmount->setFocus(); @@ -119,3 +129,21 @@ void SendCoinsDialog::on_payTo_textChanged(const QString &address) { ui->addAsLabel->setText(model->labelForAddress(address)); } + +void SendCoinsDialog::clear() +{ + ui->payTo->setText(QString()); + ui->addAsLabel->setText(QString()); + ui->payAmount->setText(QString()); + ui->payTo->setFocus(); +} + +void SendCoinsDialog::reject() +{ + clear(); +} + +void SendCoinsDialog::accept() +{ + clear(); +} diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 968cbe760f..4e019b7296 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -18,6 +18,11 @@ public: void setModel(WalletModel *model); +public slots: + void clear(); + void reject(); + void accept(); + private: Ui::SendCoinsDialog *ui; WalletModel *model; -- cgit v1.2.3 From 94fe42a945bc9a5e7091149eb43d90d77165d5a2 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 18:25:27 +0200 Subject: Selection/tab navigation fixes --- src/qt/addressbookpage.cpp | 42 +++++++++++++++++++++--------------------- src/qt/addressbookpage.h | 1 - src/qt/sendcoinsdialog.cpp | 10 ++++++---- src/qt/transactionview.cpp | 1 + 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 67ed0fecca..5127eb6009 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -19,7 +19,8 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : switch(mode) { case ForSending: - connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_buttonBox_accepted())); + connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(accept())); + ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); ui->tableView->setFocus(); break; case ForEditing: @@ -34,6 +35,9 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : case ReceivingTab: break; } + ui->tableView->setTabKeyNavigation(false); + + connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); } AddressBookPage::~AddressBookPage() @@ -127,26 +131,6 @@ void AddressBookPage::on_deleteButton_clicked() } } -void AddressBookPage::on_buttonBox_accepted() -{ - QTableView *table = getCurrentTable(); - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) - { - QVariant address = table->model()->data(index); - returnValue = address.toString(); - } - if(!returnValue.isEmpty()) - { - accept(); - } - else - { - reject(); - } -} - void AddressBookPage::selectionChanged() { // Set button states based on selected tab and selection @@ -177,5 +161,21 @@ void AddressBookPage::done(int retval) // When this is a tab/widget and not a model dialog, ignore "done" if(mode == ForEditing) return; + + // Figure out which address was selected, and return it + QTableView *table = getCurrentTable(); + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QVariant address = table->model()->data(index); + returnValue = address.toString(); + } + + if(returnValue.isEmpty()) + { + retval = Rejected; + } + QDialog::done(retval); } diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index f25bbc990b..c4039523af 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -47,7 +47,6 @@ private: QTableView *getCurrentTable(); private slots: - void on_buttonBox_accepted(); void on_deleteButton_clicked(); void on_newAddressButton_clicked(); void on_copyToClipboard_clicked(); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 8050baff38..2265f88cb0 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -113,11 +113,13 @@ void SendCoinsDialog::on_pasteButton_clicked() void SendCoinsDialog::on_addressBookButton_clicked() { - AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab); + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); dlg.setModel(model->getAddressTableModel()); - dlg.exec(); - ui->payTo->setText(dlg.getReturnValue()); - ui->payAmount->setFocus(); + if(dlg.exec()) + { + ui->payTo->setText(dlg.getReturnValue()); + ui->payAmount->setFocus(); + } } void SendCoinsDialog::on_buttonBox_rejected() diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 037dfbb3ed..5a383da2b8 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -89,6 +89,7 @@ TransactionView::TransactionView(QWidget *parent) : hlayout->addSpacing(width); // Always show scroll bar view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + view->setTabKeyNavigation(false); transactionView = view; -- cgit v1.2.3 From a7e506d6980d76001fe49c6c87262af183373c0c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 18:32:47 +0200 Subject: clarify todo/dones in readme --- README.rst | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/README.rst b/README.rst index 458b28f8a8..d9b470227b 100644 --- a/README.rst +++ b/README.rst @@ -3,41 +3,35 @@ Bitcoin-qt: Qt4 based GUI replacement for Bitcoin **Warning** **Warning** **Warning** -Pre-alpha stuff! I'm using this client myself on the production network, and I haven't noticed any glitches, but remember: always backup your wallet! Testing on the testnet is recommended. +Pre-alpha stuff! I'm using this client myself on the production network, and I haven't noticed any glitches, but remember: always backup your wallet. +Testing on the testnet is recommended. This has been implemented: - qmake / QtCreator project (.pro) -- All dialogs (main GUI, address book, send coins) and menus +- Compatibility with Linux (both GNOME and KDE), MacOSX and Windows -- Taskbar icon/menu +- All functionality of the original client, including taskbar icon/menu -- GUI only functionality (copy to clipboard, select address, address/transaction filter proxys) +- Tabbed interface -- Bitcoin core is made compatible with Qt4 +- Ask for confirmation before sending coins -- Send coins dialog: address and input validation +- CSV export of transactions -- Address book and transactions views and models +- User friendly transaction list with status icons -- Options dialog +- Show alternative icon when on testnet -- Sending coins (including ask for fee when needed) - -- Show error messages from core - -- Show details dialog for transactions (on double click) +- Progress bar on initial block download This has to be done: -- Integrate with main bitcoin tree - - Start at system start - Internationalization (convert WX language files) -- Build on Windows Build instructions =================== -- cgit v1.2.3 From 0002bdddfa81123d479d3c37a37e67f8334fd980 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 18:38:37 +0200 Subject: add [testnet] to whatever the current window title is --- src/qt/bitcoingui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 99dbbc11ae..cfdccbea6b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -190,7 +190,7 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) if(clientModel->isTestNet()) { - QString title_testnet = tr("Bitcoin Wallet [testnet]"); + QString title_testnet = windowTitle() + QString(" ") + tr("[testnet]"); setWindowTitle(title_testnet); setWindowIcon(QIcon(":icons/bitcoin_testnet")); if(trayIcon) -- cgit v1.2.3 From ba3d0255fcb0a661097a7d4b67cd16a1da26cddc Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 21:19:25 +0200 Subject: Add German translation by nico_w --- README.rst | 2 +- bitcoin-qt.pro | 3 +- src/qt/locale/bitcoin_de.ts | 1155 +++++++++++++++++++++++++++++++++++++++++++ src/qt/locale/bitcoin_nl.ts | 619 +++++++++++++++++------ 4 files changed, 1624 insertions(+), 155 deletions(-) create mode 100644 src/qt/locale/bitcoin_de.ts diff --git a/README.rst b/README.rst index d9b470227b..949bf17e8a 100644 --- a/README.rst +++ b/README.rst @@ -81,7 +81,7 @@ Windows build instructions: Berkely DB version warning ========================== -A warning for people using the *static binary* version of Bitcoin (tl;dr: **Berkely DB databases are not forward compatible**). +A warning for people using the *static binary* version of Bitcoin on a Linux/UNIX-ish system (tl;dr: **Berkely DB databases are not forward compatible**). The static binary version of Bitcoin is linked against libdb4.7 or libdb4.8 (see also `this Debian issue`_). diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 5e2646fe6f..2a389e6840 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -130,4 +130,5 @@ FORMS += \ src/qt/forms/overviewpage.ui CODECFORTR = UTF-8 -TRANSLATIONS = src/qt/locale/bitcoin_nl.ts +TRANSLATIONS = src/qt/locale/bitcoin_nl.ts src/qt/locale/bitcoin_de.ts + diff --git a/src/qt/locale/bitcoin_de.ts b/src/qt/locale/bitcoin_de.ts new file mode 100644 index 0000000000..87185155ac --- /dev/null +++ b/src/qt/locale/bitcoin_de.ts @@ -0,0 +1,1155 @@ + + + +UTF-8 + + AboutDialog + + + About Bitcoin + Über Bitcoin + + + + <b>Bitcoin</b> version + <b>Bitcoin</b> Version + + + + Copyright © 2009-2011 Bitcoin Developers + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file license.txt or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + Copyright © 2009-2011 Bitcoin Developers + +Dies ist experimentelle Software. + +Veröffentlicht unter der MIT/X11 Software-Lizenz. Sie können diese in der beiligenden Datei license.txt oder unter http://www.opensource.org/licenses/mit-license.php nachlesen. + +Dieses Produkt enthält Software, welche vom OpenSSL Projekt zur Verwendung im OpenSSL Toolkit (http://www.openssl.org/) entwickelt wurde, kryptographische Software von Eric Young (eay@cryptsoft.com) und UPnP Software von Thomas-Bernard. + + + + AddressBookPage + + + Address Book + Adressbuch + + + + These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Dies sind ihre Bitcoin-Adressen zum empfangen von Zahlungen. Sie können jedem Sender eine andere Adresse mitteilen, sodass sie ihre empfangenen Zahlungen zurückverfolgen können. + + + + Double-click to edit address or label + Doppelklick zum Ändern der Adresse oder Beschreibung + + + + Create a new address + Neue Adresse erstellen + + + + &New Address... + &Neue Adresse... + + + + Copy the currently selected address to the system clipboard + Ausgewählte Adresse in die Zwischenablage kopieren + + + + &Copy to Clipboard + &Kopieren + + + + Delete the currently selected address from the list. Only sending addresses can be deleted. + Ausgewählte Adresse aus der Liste entfernen. Sie können nur die Adressen von Empfängern löschen. + + + + &Delete + &Löschen + + + + AddressTableModel + + + Label + + + + + Address + + + + + (no label) + + + + + BitcoinGUI + + + Bitcoin Wallet + + + + + Number of connections to other clients + Anzahl der Verbindungen zu anderen Clients + + + + Number of blocks in the block chain + Anzahl der Blocks in der Block-Chain + + + + Synchronizing with network... + Mit Netzwerk synchronisieren... + + + + Block chain synchronization in progress + Synchronisierung der Block-Chain... + + + + &Overview + Übersicht + + + + &Transactions + Transaktionen + + + + &Address Book + Adressbuch + + + + Edit the list of stored addresses and labels + Adressliste bearbeiten + + + + &Receive coins + Bitcoins empfangen + + + + Show the list of addresses for receiving payments + Liste der Adressen für Überweisungen anzeigen + + + + &Send coins + Bitcoins senden + + + + Send coins to a bitcoin address + Bitcoins an eine Bitcoin-Adresse senden + + + + &Exit + Beenden + + + + Quit application + Anwendung beenden + + + + &About + Über + + + + Show information about Bitcoin + Informationen über Bitcoin + + + + &Options... + Einstellungen + + + + Modify configuration options for bitcoin + Einstellungen ändern + + + + Open &Bitcoin + Bitcoin öffnen + + + + Show the Bitcoin window + Bitcoin-Fenster öffnen + + + + &Export... + Exportieren... + + + + Export data in current view to a file + Angezeigte Daten als Datei exportieren + + + + Bitcoin Wallet [testnet] + Bitcoin Wallet [testnet] + + + + %n connection(s) + + %n Verbindung(en) + + + + + + %n block(s) + + %n Blöcke + + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Die Größe dieser Transaktion übersteigt das Limit. + Sie können die Coins jedoch senden, wenn sie einen zusätzlichen Betrag von %1 zahlen, +welcher an die Teilnehmer des Bitcoin-Netzwerkes ausgeschüttet wird und dieses unterstützt. +Möchten Sie die zusätzliche Gebühr zahlen? + + + + Sending... + Senden... + + + + Incoming transaction + Eingehende Transaktion + + + + Date: + Datum: + + + + Amount: + Betrag: + + + + Type: + Beschreibung: + + + + Address: + Adresse: + + + + EditAddressDialog + + + Edit Address + Adresse Bearbeiten + + + + &Label + Beschreibung + + + + The label associated with this address book entry + Name/Beschreibung für den Adressbucheintrag + + + + &Address + Adresse + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Adresse für den Adressbucheintrag + + + + New receiving address + Neue Empfangsadresse + + + + New sending address + Neue Zahlungsadresse + + + + Edit receiving address + Empfangsadresse bearbeiten + + + + Edit sending address + Zahlungsadresse bearbeiten + + + + The entered address "%1" is not a valid bitcoin address. + Die eingegebene Adresse ("%1") ist keine gültige Bitcoin-Adresse. + + + + The entered address "%1" is already in the address book. + Die eingegebene Adresse ("%1") befindet sich bereits in Ihrem Adressbuch. + + + + MainOptionsPage + + + &Start Bitcoin on window system startup + Bitcoin beim Start des Systems ausführen + + + + Automatically start Bitcoin after the computer is turned on + Bitcoin automatisch beim Systemstart ausführen + + + + &Minimize to the tray instead of the taskbar + In den Infobereich statt der Taskleiste minimieren + + + + Show only a tray icon after minimizing the window + Nur ein Icon im Infobereich anzeigen nach dem minimieren des Fensters + + + + Map port using &UPnP + Portweiterleitung via UPnP + + + + Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled. + Öffnet den Bitcoin Client-Port automatisch auf dem Router. Dies funktioniert nur, wenn Ihr Router UPnP unterstützt und dies aktiviert ist. + + + + M&inimize on close + Beim Schließen minimieren + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimiert die Anwendung, wenn das Fenster geschlossen wird. Wenn dies aktiviert ist, müssen Sie das Programm über "Beenden" im Menü schließen. + + + + &Connect through SOCKS4 proxy: + Über SOCKS4-Proxy verbinden: + + + + Connect to the Bitcon network through a SOCKS4 proxy (e.g. when connecting through Tor) + Über SOCKS4-Proxy zum Bitcoin-Netzwerk verbinden (bspw. für eine Verbindung über Tor) + + + + Proxy &IP: + Proxy-IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + IP-Adresse des Proxys (z.B. 127.0.0.1) + + + + &Port: + Port: + + + + Port of the proxy (e.g. 1234) + Port des Proxy-Servers (z.B. 1234) + + + + Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended. + Zusätzliche Transaktionsgebühr per KB, welche sicherstellt dass Ihre Transaktionen schnell bearbeitet werden. Die meisten Transaktionen sind 1KB groß. Eine Gebühr von 0.01 wird empfohlen. + + + + Pay transaction &fee + Transaktionsgebühr bezahlen + + + + Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended. + Zusätzliche Transaktionsgebühr per KB, welche sicherstellt dass Ihre Transaktionen schnell bearbeitet werden. Die meisten Transaktionen sind 1KB groß. Eine Gebühr von 0.01 wird empfohlen. + + + + OptionsDialog + + + Main + Haupt + + + + Options + Einstellungen + + + + OverviewPage + + + Form + + + + + Balance: + Kontostand: + + + + 123.456 BTC + + + + + Number of transactions: + Anzahl der Transaktionen: + + + + 0 + + + + + Your current balance + Kontostand + + + + SendCoinsDialog + + + + + + + + Send Coins + Bitcoins überweisen + + + + The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + Adresse für die Überweisung (z.B. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + + + + Look up adress in address book + Adressbuch + + + + Alt+A + Alt+A + + + + Paste address from system clipboard + Adresse aus der Zwischenanlage einfügen + + + + Alt+P + Alt+P + + + + A&mount: + Betrag: + + + + Pay &To: + Empfänger: + + + + Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + Bitcoin-Adresse eingeben (z.B. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + + + + + Enter a label for this address to add it to your address book + Beschreibung der Adresse im Adressbuch + + + + &Label: + Beschreibung: + + + + Confirm the send action + Überweisung bestätigen + + + + &Send + Überweisen + + + + Abort the send action + Überweisung abbrechen + + + + Must fill in an amount to pay. + Sie müssen einen Betrag angeben. + + + + Confirm send coins + Überweisung bestätigen + + + + Are you sure you want to send %1 BTC to %2 (%3)? + Sind Sie sich sicher, %1 BTC an %2 (%3) zahlen zu wollen? + + + + The recepient address is not valid, please recheck. + Die Adresse des Empfängers ist nicht gültig, bitte überprüfen Sie diese. + + + + The amount to pay must be larger than 0. + Der Transaktionsbetrag muss mehr als 0 betragen. + + + + Amount exceeds your balance + Der Betrag übersteigt ihren Kontostand + + + + Total exceeds your balance when the %1 transaction fee is included + Betrag übersteigt ihren Kontostand aufgrund der Transaktionsgebühren in Höhe von %1 + + + + TransactionDescDialog + + + Transaction details + Transaktionsübersicht + + + + This pane shows a detailed description of the transaction + Dieses Fenster zeigt ihnen eine detaillierte Übersicht der Transaktion an + + + + TransactionTableModel + + + Status + Status + + + + Date + Datum + + + + Type + Beschreibung + + + + Address + Adresse + + + + Amount + Betrag + + + + Open for %n block(s) + + Offen für %n Blöcke + + + + + + Open until %1 + Offen bis %1 + + + + Offline (%1 confirmations) + Offline (%1 Bestätigungen) + + + + Unconfirmed (%1/%2 confirmations) + Unbestätigt (%1/%2 Bestätigungen) + + + + Confirmed (%1 confirmations) + Bestätigt (%1 Bestätigungen) + + + + Mined balance will be available in %n more blocks + + Der geminte Betrag wird nach %n Blöcken verfügbar sein + + + + + + This block was not received by any other nodes and will probably not be accepted! + Dieser Block wurde vom Netzwerk nicht angenommen und wird wahrscheinlich nicht bestätigt werden! + + + + Generated but not accepted + Generiert, jedoch nicht angenommen + + + + Received with + Empfangen durch + + + + Received from IP + Empfangen durch IP + + + + Sent to + An + + + + Sent to IP + An IP + + + + Payment to yourself + Überweisung an Sie selbst + + + + Mined + Gemined + + + + Transaction status. Hover over this field to show number of confirmations. + Transaktionsstatus. Fahren Sie mit der Maus über dieses Feld um die Anzahl der Bestätigungen zu sehen. + + + + Date and time that the transaction was received. + Datum und Uhrzeit als die Transaktion empfangen wurde. + + + + Type of transaction. + Art der Transaktion. + + + + Destination address of transaction. + Empfangsadresse der Transaktion + + + + Amount removed from or added to balance. + Betrag vom Kontostand entfernt oder hinzugefügt + + + + TransactionView + + + + All + Alle + + + + Today + Heute + + + + This week + Diese Woche + + + + This month + Diesen Monat + + + + Last month + Letzten Monat + + + + This year + Dieses Jahr + + + + Range... + Bereich... + + + + Received with + Empfangen durch + + + + Sent to + An + + + + To yourself + Zu Ihnen selbst + + + + Mined + Gemined + + + + Other + Andere + + + + Export Transaction Data + Transaktionen exportieren + + + + Comma separated file (*.csv) + + + + + Error exporting + Fehler beim exportieren + + + + Could not write to file %1. + Konnte Datei %1 nicht öffnen + + + + WalletModel + + + Sending... + Senden... + + + + bitcoin-core + + + Bitcoin version + + + + + Usage: + + + + + Send command to -server or bitcoind + + + + + + List commands + + + + + + Get help for a command + + + + + + Options: + + + + + + Specify configuration file (default: bitcoin.conf) + + + + + + Specify pid file (default: bitcoind.pid) + + + + + + Generate coins + + + + + + Don't generate coins + + + + + + Start minimized + + + + + + Specify data directory + + + + + + Specify connection timeout (in milliseconds) + + + + + + Connect through socks4 proxy + + + + + + Allow DNS lookups for addnode and connect + + + + + + Add a node to connect to + + + + + + Connect only to the specified node + + + + + + Don't accept connections from outside + + + + + + Don't attempt to use UPnP to map the listening port + + + + + + Attempt to use UPnP to map the listening port + + + + + + Fee per KB to add to transactions you send + + + + + + Accept command line and JSON-RPC commands + + + + + + Run in the background as a daemon and accept commands + + + + + + Use the test network + + + + + + Username for JSON-RPC connections + + + + + + Password for JSON-RPC connections + + + + + + Listen for JSON-RPC connections on <port> (default: 8332) + + + + + + Allow JSON-RPC connections from specified IP address + + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + + Set key pool size to <n> (default: 100) + + + + + + Rescan the block chain for missing wallet transactions + + + + + + +SSL options: (see the Bitcoin Wiki for SSL setup instructions) + + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + + Server certificate file (default: server.cert) + + + + + + Server private key (default: server.pem) + + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + + This help message + + + + + + Cannot obtain a lock on data directory %s. Bitcoin is probably already running. + + + + + Error loading addr.dat + + + + + + Error loading blkindex.dat + + + + + + Error loading wallet.dat + + + + + + Invalid -proxy address + + + + + Invalid amount for -paytxfee=<amount> + + + + + Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Disk space is low + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds + + + + + Error: Transaction creation failed + + + + + Sending... + + + + + Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here. + + + + + Invalid amount + + + + + Insufficient funds + + + + + Invalid bitcoin address + + + + + Unable to bind to port %d on this computer. Bitcoin is probably already running. + + + + + To use the %s option + + + + + Warning: %s, you must set rpcpassword=<password> +in the configuration file: %s +If the file does not exist, create it with owner-readable-only file permissions. + + + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + + Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly. + + + + + -beta + + + + diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts index 935815c48a..afe0dc89e2 100644 --- a/src/qt/locale/bitcoin_nl.ts +++ b/src/qt/locale/bitcoin_nl.ts @@ -41,243 +41,332 @@ door Thomas Bernard. AddressBookDialog - Address Book - Adresboek + Adresboek - Sending - Versturen + Versturen - Receiving - Ontvangen + Ontvangen - These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. The highlighted address is displayed in the main window. - Dit zijn je bitcoin-adressen voor het ontvangen van betalingen. Het is een goed idee iedere afzender een ander adres te geven zodat je bij kunt houden wie je een betaling stuurt. Het geselecteerde adres is zichtbaar in het hoofdscherm. + Dit zijn je bitcoin-adressen voor het ontvangen van betalingen. Het is een goed idee iedere afzender een ander adres te geven zodat je bij kunt houden wie je een betaling stuurt. Het geselecteerde adres is zichtbaar in het hoofdscherm. - Create a new address - Voeg een nieuw adres toe + Voeg een nieuw adres toe - &New Address... - &Nieuw adres... + &Nieuw adres... - Copy the currently selected address to the system clipboard - Kopieer het geselecteerde adres naar het plakbord + Kopieer het geselecteerde adres naar het plakbord - &Copy to Clipboard - &Kopieer naar plakbord + &Kopieer naar plakbord - Edit the currently selected address - Bewerk het geselecteerde adres + Bewerk het geselecteerde adres - &Edit... - &Bewerken... + &Bewerken... - Delete the currently selected address from the list. Only sending addresses can be deleted. - Verwijder het geselecteerde adres. Alleen verstuur-adressen kunnen worden verwijderd. + Verwijder het geselecteerde adres. Alleen verstuur-adressen kunnen worden verwijderd. - &Delete - &Verwijderen + &Verwijderen + + + + AddressBookPage + + + Address Book + Adresboek + + + + These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + Double-click to edit address or label + + + + + Create a new address + Voeg een nieuw adres toe + + + + &New Address... + &Nieuw adres... + + + + Copy the currently selected address to the system clipboard + Kopieer het geselecteerde adres naar het plakbord + + + + &Copy to Clipboard + &Kopieer naar plakbord + + + + Delete the currently selected address from the list. Only sending addresses can be deleted. + Verwijder het geselecteerde adres. Alleen verstuur-adressen kunnen worden verwijderd. + + + + &Delete + &Verwijderen AddressTableModel - + Label Label - + Address Adres - + + (no label) + + + Default receiving address - Standaard ontvangst-adres + Standaard ontvangst-adres BitcoinGUI - Bitcoin - Bitcoin + Bitcoin - Your Bitcoin address: - Uw bitcoin-adres: + Uw bitcoin-adres: - Your current default receiving address - Uw huidig standaard ontvangst-adres + Uw huidig standaard ontvangst-adres - &New... - &Nieuw... + &Nieuw... - Create new receiving address - Maak nieuw ontvangst-adres + Maak nieuw ontvangst-adres - &Copy to clipboard - &Kopieer naar plakbord + &Kopieer naar plakbord - Copy current receiving address to the system clipboard - Kopieer huidig ontvangst-adres naar plakbord + Kopieer huidig ontvangst-adres naar plakbord - Balance: - Saldo: + Saldo: - Your current balance - Uw huidige saldo + Uw huidige saldo - + Number of connections to other clients Aantal verbindingen met andere clients - + Number of blocks in the block chain Aantal blokken in de block-chain - Number of transactions in your wallet - Aantal transacties in je portefeuille + Aantal transacties in je portefeuille - + Synchronizing with network... Synchroniseren met netwerk... - + Block chain synchronization in progress Bezig met blokken-database-synchronisatie - + &Exit A&fsluiten - + Quit application De applicatie afsluiten - + &Send coins &Verstuur coins - + Send coins to a bitcoin address Coins versturen naar een bitcoin-adres - + &Address Book &Adresboek - + + Bitcoin Wallet + + + + + &Overview + + + + + &Transactions + + + + Edit the list of stored addresses and labels Bewerk de lijst van adressen en labels - + + &Receive coins + + + + + Show the list of addresses for receiving payments + + + + &About &Over Bitcoin - + Show information about Bitcoin Laat informatie zien over Bitcoin - + + &Export... + + + + + Export data in current view to a file + + + + + [testnet] + + + + + Date: + + + + + Amount: + + + + + Type: + + + + + Address: + + + Your &Receiving Addresses... - &Uw ontvangstadressen... + &Uw ontvangstadressen... - Show the list of receiving addresses and edit their labels - Laat de lijst zien van ontvangst-adressen en bewerk de labels + Laat de lijst zien van ontvangst-adressen en bewerk de labels - + &Options... &Opties... - + Modify configuration options for bitcoin Verander instellingen van Bitcoin - + + Open &Bitcoin + + + + Show the Bitcoin window Laat het Bitcoin-venster zien - All transactions - Alle transacties + Alle transacties - Sent/Received - Verzonden/Ontvangen + Verzonden/Ontvangen - Sent - Verzonden + Verzonden - Received - Ontvangen + Ontvangen - + %n connection(s) %n verbinding @@ -285,7 +374,7 @@ door Thomas Bernard. - + %n block(s) %n blok @@ -293,25 +382,24 @@ door Thomas Bernard. - %n transaction(s) - + %n transactie %n transacties - + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? Deze transactie overschrijdt de limiet. Om de transactie alsnog te verwerken kun je een fooi betalen van %1. Deze zal betaald worden aan de node die uw transactie verwerkt. Wil je doorgaan en deze fooi betalen? - + Sending... Versturen... - + Incoming transaction Binnenkomende transactie @@ -319,9 +407,8 @@ door Thomas Bernard. ClientModel - Sending... - Versturen... + Versturen... @@ -337,12 +424,12 @@ door Thomas Bernard. &Label - + &Address &Adres - + The label associated with this address book entry Het label dat is geassocieerd met dit adres @@ -352,9 +439,8 @@ door Thomas Bernard. Het adres dat is geassocieerd met de label. Dit kan alleen worden veranderd voor verzend-adressen. - Set as default receiving address - Stel in as standaard ontvangst-adres + Stel in as standaard ontvangst-adres @@ -362,24 +448,33 @@ door Thomas Bernard. Nieuw ontvangst-adres - + New sending address Nieuw verzend-adres - + Edit receiving address Bewerk ontvangst-adres - + Edit sending address Bewerk ontvangst-adres - + + The entered address "%1" is not a valid bitcoin address. + + + + + The entered address "%1" is already in the address book. + + + The address %1 is already in the address book. - Het adres %1 staat al in het adresboek. + Het adres %1 staat al in het adresboek. @@ -483,15 +578,48 @@ door Thomas Bernard. Opties + + OverviewPage + + + Form + + + + + Balance: + Saldo: + + + + 123.456 BTC + + + + + Number of transactions: + + + + + 0 + + + + + Your current balance + Uw huidige saldo + + SendCoinsDialog - - - - + + + + Send Coins Verstuur coins @@ -519,6 +647,17 @@ door Thomas Bernard. Paste address from system clipboard Plak adres uit systeemplakbord + + + + Enter a label for this address to add it to your address book + + + + + &Label: + + &Paste &Plakken @@ -548,57 +687,68 @@ door Thomas Bernard. Voer een bitcoin-adres in (bijvoorbeeld: 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - Add specified destination address to address book - Voeg het ingevoerde doel-adres toe aan het adresboek + Voeg het ingevoerde doel-adres toe aan het adresboek - A&dd to address book as - &Voeg toe aan adresboek als + &Voeg toe aan adresboek als - Label to add address as - Label om toe te kennen aan adres in adresboek + Label om toe te kennen aan adres in adresboek - + Confirm the send action Bevestig de zend-transactie - + &Send &Versturen - + Abort the send action De transactie annuleren - The amount to pay must be a valid number. - Het ingevoerde bedrag is geen geldig getal. + Het ingevoerde bedrag is geen geldig getal. + + + + Must fill in an amount to pay. + + + + + Confirm send coins + - + + Are you sure you want to send %1 BTC to %2 (%3)? + + + + The recepient address is not valid, please recheck. Het verzendadres is niet geld. - + The amount to pay must be larger than 0. Het ingevoerde gedrag moet groter zijn dan 0. - + Amount exceeds your balance Hoeveelheid overschrijdt uw huidige balans - + Total exceeds your balance when the %1 transaction fee is included Totaal overschrijdt uw huidige balans wanneer de %1 transactiefooi is meegerekend @@ -619,32 +769,117 @@ door Thomas Bernard. TransactionTableModel - + Status Status - + Date Datum - + + Type + + + + + Address + Adres + + + + Amount + + + + + Offline (%1 confirmations) + + + + + Unconfirmed (%1/%2 confirmations) + + + + + Confirmed (%1 confirmations) + + + + + Mined balance will be available in %n more blocks + + + + + + + + This block was not received by any other nodes and will probably not be accepted! + + + + + Generated but not accepted + + + + + Received with + + + + + Received from IP + + + + + Sent to + + + + + Sent to IP + + + + + Mined + + + + + Type of transaction. + + + + + Destination address of transaction. + + + + + Amount removed from or added to balance. + + + Description - Beschrijving + Beschrijving - Debit - Debet + Debet - Credit - Credit + Credit - + Open for %n block(s) Open gedurende %n blok @@ -652,97 +887,175 @@ door Thomas Bernard. - + Open until %1 Open tot %1 - Offline (%1) - Offline (%1) + Offline (%1) - Unconfirmed (%1/%2) - Niet bevestigd (%1/%2) + Niet bevestigd (%1/%2) - Confirmed (%1) - Bevestigd (%1) + Bevestigd (%1) - Received with: - Ontvangen met: + Ontvangen met: - Received from IP: - Ontvangen van IP: + Ontvangen van IP: - Sent to: - Verzonden aan: + Verzonden aan: - Sent to IP: - Verzonden aan IP: + Verzonden aan IP: - + Payment to yourself Betaling aan uzelf - Generated (matures in %n more blocks) - + Gegenereerd (wordt volwassen na %n blok) Gegenereerd (wordt volwassen na %n blokken) - Generated - Gegenereerd + Gegenereerd - Generated - Warning: This block was not received by any other nodes and will probably not be accepted! - Gegenereerd - Waarschuwing: Dit blok is niet ontvangen door andere nodes en zal waarschijnlijk niet geaccepteerd worden! + Gegenereerd - Waarschuwing: Dit blok is niet ontvangen door andere nodes en zal waarschijnlijk niet geaccepteerd worden! - Generated (not accepted) - Gegenereerd (niet geaccepteerd) + Gegenereerd (niet geaccepteerd) - + Transaction status. Hover over this field to show number of confirmations. Transactiestatus. Hou de muiscursor boven dit veld om het aantal bevestigingen te laten zien. - + Date and time that the transaction was received. Datum en tijd waarop deze transactie is ontvangen. - Short description of the transaction. - Korte beschrijving van de transactie. + Korte beschrijving van de transactie. - Amount removed from balance. - Bedrag afgeschreven van saldo. + Bedrag afgeschreven van saldo. - Amount added to balance. - Bedrag bijgeschreven bij saldo. + Bedrag bijgeschreven bij saldo. + + + + TransactionView + + + + All + + + + + Today + + + + + This week + + + + + This month + + + + + Last month + + + + + This year + + + + + Range... + + + + + Received with + + + + + Sent to + + + + + To yourself + + + + + Mined + + + + + Other + + + + + Export Transaction Data + + + + + Comma separated file (*.csv) + + + + + Error exporting + + + + + Could not write to file %1. + + + + + WalletModel + + + Sending... + Versturen... -- cgit v1.2.3 From 610121480cc1cd66c08f54c7e67a5440892cc6a2 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 21:25:17 +0200 Subject: "Status" doesn't fit into narrow first column in transaction history, make the header empty --- src/qt/transactiontablemodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 6a7f7aab4a..a529dadb1a 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -208,7 +208,7 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren walletModel(parent), priv(new TransactionTablePriv(wallet, this)) { - columns << tr("Status") << tr("Date") << tr("Type") << tr("Address") << tr("Amount"); + columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount"); priv->refreshWallet(); -- cgit v1.2.3 From 8ffec99b071df945e4d6c59641a89a8bd409c4ec Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 7 Jul 2011 21:26:46 +0200 Subject: update README --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 949bf17e8a..da480adf1c 100644 --- a/README.rst +++ b/README.rst @@ -16,11 +16,11 @@ This has been implemented: - Tabbed interface -- Ask for confirmation before sending coins +- Asks for confirmation before sending coins - CSV export of transactions -- User friendly transaction list with status icons +- User friendly transaction list with status icons, and real-time filtering - Show alternative icon when on testnet -- cgit v1.2.3 From 84c8506e90c01b4ba38c19064389d8549593be2f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 8 Jul 2011 18:05:10 +0200 Subject: Display a "freshness" indicator instead of nr of blocks --- doc/assets-attribution.txt | 6 ++++++ src/qt/bitcoin.qrc | 2 ++ src/qt/bitcoingui.cpp | 36 +++++++++++++++++++++++++++++++++--- src/qt/clientmodel.cpp | 6 ++++++ src/qt/clientmodel.h | 6 ++++++ 5 files changed, 53 insertions(+), 3 deletions(-) diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index 786427f202..f58b0da40c 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -52,3 +52,9 @@ Designer: Jack Cai License: Creative Commons Attribution No Derivatives (by-nd) Site: http://findicons.com/icon/175944/home?id=176221# +Icon: src/qt/res/icons/synced.png, + src/qt/res/icons/notsynced.png +Icon Pack: Gloss: Basic +Designer: Momenticons +License: Creative Commons Attribution (by) +Site: http://www.momenticons.com/ diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index e64744cda8..1522ce61e3 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -29,6 +29,8 @@ res/icons/history.png res/icons/overview.png res/icons/export.png + res/icons/synced.png + res/icons/notsynced.png
res/images/about.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index cfdccbea6b..06c2034da5 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -109,11 +110,13 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): labelConnections = new QLabel(); labelConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); labelConnections->setMinimumWidth(150); + labelConnections->setMaximumWidth(150); labelConnections->setToolTip(tr("Number of connections to other clients")); labelBlocks = new QLabel(); labelBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelBlocks->setMinimumWidth(130); + labelBlocks->setMinimumWidth(150); + labelBlocks->setMaximumWidth(150); labelBlocks->setToolTip(tr("Number of blocks in the block chain")); // Progress bar for blocks download @@ -295,7 +298,7 @@ void BitcoinGUI::setNumConnections(int count) default: icon = ":/icons/connect_4"; break; } labelConnections->setTextFormat(Qt::RichText); - labelConnections->setText(" " + tr("%n connection(s)", "", count)); + labelConnections->setText("" + tr("%n connection(s)", "", count)); } void BitcoinGUI::setNumBlocks(int count) @@ -314,7 +317,34 @@ void BitcoinGUI::setNumBlocks(int count) progressBar->setVisible(false); } - labelBlocks->setText(tr("%n block(s)", "", count)); + QDateTime now = QDateTime::currentDateTime(); + QDateTime lastBlockDate = clientModel->getLastBlockDate(); + int secs = lastBlockDate.secsTo(now); + QString text; + QString icon = ":/icons/notsynced"; + + // "Up to date" icon, and outdated icon + if(secs < 30*60) + { + text = "Up to date"; + icon = ":/icons/synced"; + } + else if(secs < 60*60) + { + text = tr("%n minute(s) ago","",secs/60); + } + else if(secs < 24*60*60) + { + text = tr("%n hour(s) ago","",secs/(60*60)); + } + else + { + text = tr("%n day(s) ago","",secs/(60*60*24)); + } + + labelBlocks->setText(" " + text); + labelBlocks->setToolTip(tr("%n block(s) in total, last block was generated %1", "", count) + .arg(QLocale::system().toString(lastBlockDate))); } void BitcoinGUI::setNumTransactions(int count) diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 06ad5adfc8..8885b4cb5b 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -7,6 +7,7 @@ #include "headers.h" #include +#include ClientModel::ClientModel(CWallet *wallet, QObject *parent) : QObject(parent), wallet(wallet), optionsModel(0) @@ -30,6 +31,11 @@ int ClientModel::getNumBlocks() const return nBestHeight; } +QDateTime ClientModel::getLastBlockDate() const +{ + return QDateTime::fromTime_t(pindexBest->GetBlockTime()); +} + void ClientModel::update() { // Plainly emit all signals for now. To be more efficient this should check diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 659fa65762..6c2c275cef 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -8,6 +8,10 @@ class AddressTableModel; class TransactionTableModel; class CWallet; +QT_BEGIN_NAMESPACE +class QDateTime; +QT_END_NAMESPACE + // Interface to Bitcoin network client class ClientModel : public QObject { @@ -22,6 +26,8 @@ public: int getNumConnections() const; int getNumBlocks() const; + QDateTime getLastBlockDate() const; + // Return true if client connected to testnet bool isTestNet() const; // Return true if core is doing initial block download -- cgit v1.2.3 From d8aeb8dd2af508febafd9f7aa23526b67b97185f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 8 Jul 2011 19:00:08 +0200 Subject: Reorganize "send coins" tab --- src/qt/forms/sendcoinsdialog.ui | 191 ++++++++++++++++++++++------------------ src/qt/sendcoinsdialog.cpp | 1 + 2 files changed, 106 insertions(+), 86 deletions(-) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 7f843c4518..a5c38a5306 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -6,16 +6,94 @@ 0 0 - 660 - 193 + 686 + 217 Send Coins + + + + Qt::Vertical + + + QSizePolicy::Preferred + + + + 20 + 12 + + + + + + 12 + + + + + A&mount: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + payAmount + + + + + + + Pay &To: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + payTo + + + + + + + + + + 0 + + + + + true + + + Enter a label for this address to add it to your address book + + + + + + + + + &Label: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + addAsLabel + + + @@ -76,94 +154,13 @@ - - - - A&mount: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - payAmount - - - - - - - Pay &To: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - payTo - - - - - - - - 9 - - - - Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - - - - - - - - - - 0 - - - - - true - - - Enter a label for this address to add it to your address book - - - - - - - - - &Label: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - addAsLabel - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - + + 12 + @@ -179,6 +176,12 @@ + + + 120 + 0 + + Confirm the send action @@ -208,10 +211,26 @@ QDialogButtonBox::Cancel + + false + + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 2265f88cb0..91397a7d25 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -20,6 +20,7 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : { ui->setupUi(this); #if QT_VERSION >= 0x040700 + ui->payTo->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); #endif GUIUtil::setupAddressWidget(ui->payTo, this); -- cgit v1.2.3 From 83b8237046a25248c1b178917c2219d03bc9648f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 8 Jul 2011 19:25:35 +0200 Subject: forgot synced icons --- src/qt/bitcoingui.cpp | 1 + src/qt/forms/sendcoinsdialog.ui | 25 ++++--------------------- src/qt/res/icons/notsynced.png | Bin 0 -> 1013 bytes src/qt/res/icons/synced.png | Bin 0 -> 1127 bytes src/qt/sendcoinsdialog.cpp | 1 + 5 files changed, 6 insertions(+), 21 deletions(-) create mode 100644 src/qt/res/icons/notsynced.png create mode 100644 src/qt/res/icons/synced.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 06c2034da5..add8abc995 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -474,6 +474,7 @@ void BitcoinGUI::gotoReceiveCoinsPage() void BitcoinGUI::gotoSendCoinsPage() { sendCoinsAction->setChecked(true); + sendCoinsPage->clear(); centralWidget->setCurrentWidget(sendCoinsPage); exportAction->setEnabled(false); } diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index a5c38a5306..c3b966cac7 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -107,6 +107,9 @@ 34 + + + @@ -178,7 +181,7 @@ - 120 + 150 0 @@ -197,25 +200,6 @@ - - - - - 0 - 0 - - - - Abort the send action - - - QDialogButtonBox::Cancel - - - false - - - @@ -248,7 +232,6 @@ addAsLabel payAmount sendButton - buttonBox diff --git a/src/qt/res/icons/notsynced.png b/src/qt/res/icons/notsynced.png new file mode 100644 index 0000000000..c9e71184c5 Binary files /dev/null and b/src/qt/res/icons/notsynced.png differ diff --git a/src/qt/res/icons/synced.png b/src/qt/res/icons/synced.png new file mode 100644 index 0000000000..910fc396ed Binary files /dev/null and b/src/qt/res/icons/synced.png differ diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 91397a7d25..9724368a9c 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -23,6 +23,7 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : ui->payTo->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); #endif + GUIUtil::setupAddressWidget(ui->payTo, this); // Set initial send-to address if provided -- cgit v1.2.3 From 3ddf10e5cacabc75feea25350e98bfefb922b909 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 8 Jul 2011 19:51:24 +0200 Subject: send coins dialog: make sure send button remain default button (triggered with enter) --- src/qt/sendcoinsdialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 9724368a9c..9b7cc632e0 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -140,6 +140,7 @@ void SendCoinsDialog::clear() ui->addAsLabel->setText(QString()); ui->payAmount->setText(QString()); ui->payTo->setFocus(); + ui->sendButton->setDefault(true); } void SendCoinsDialog::reject() -- cgit v1.2.3 From 35105534e702d1a6db4fcac994869ab05266285b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 8 Jul 2011 19:56:28 +0200 Subject: Transaction list: less terse tooltip --- src/qt/transactiontablemodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index a529dadb1a..2477a1e8c9 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -278,7 +278,7 @@ QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) con status = tr("Offline (%1 confirmations)").arg(wtx->status.depth); break; case TransactionStatus::Unconfirmed: - status = tr("Unconfirmed (%1/%2 confirmations)").arg(wtx->status.depth).arg(TransactionRecord::NumConfirmations); + status = tr("Unconfirmed (%1 of %2 confirmations required)").arg(wtx->status.depth).arg(TransactionRecord::NumConfirmations); break; case TransactionStatus::HaveConfirmations: status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth); -- cgit v1.2.3 From 51d7cc07f10209ac12bd5286391e3c8b095abd34 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 8 Jul 2011 22:27:36 +0200 Subject: Add context menu on transaction list: copy label, copy address, edit label, show details --- src/qt/addresstablemodel.cpp | 30 +++++++++++ src/qt/addresstablemodel.h | 9 ++++ src/qt/bitcoingui.cpp | 13 ++--- src/qt/bitcoingui.h | 1 - src/qt/sendcoinsdialog.cpp | 3 +- src/qt/transactiontablemodel.cpp | 5 +- src/qt/transactionview.cpp | 110 ++++++++++++++++++++++++++++++++++++++- src/qt/transactionview.h | 16 ++++-- src/qt/walletmodel.cpp | 14 ----- src/qt/walletmodel.h | 4 -- 10 files changed, 168 insertions(+), 37 deletions(-) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index d04989ea2e..9ca7542016 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -296,3 +296,33 @@ bool AddressTableModel::validateAddress(const QString &address) return AddressToHash160(address.toStdString(), hash160); } + +/* Look up label for address in address book, if not found return empty string. + */ +QString AddressTableModel::labelForAddress(const QString &address) const +{ + CRITICAL_BLOCK(wallet->cs_mapAddressBook) + { + std::map::iterator mi = wallet->mapAddressBook.find(address.toStdString()); + if (mi != wallet->mapAddressBook.end()) + { + return QString::fromStdString(mi->second); + } + } + return QString(); +} + +int AddressTableModel::lookupAddress(const QString &address) const +{ + QModelIndexList lst = match(index(0, Address, QModelIndex()), + Qt::EditRole, address, 1, Qt::MatchExactly); + if(lst.isEmpty()) + { + return -1; + } + else + { + return lst.at(0).row(); + } +} + diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 6f34a60061..d48e786621 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -49,6 +49,15 @@ public: */ bool validateAddress(const QString &address); + /* Look up label for address in address book, if not found return empty string. + */ + QString labelForAddress(const QString &address) const; + + /* Look up row index of an address in the model. + Return -1 if not found. + */ + int lookupAddress(const QString &address) const; + private: CWallet *wallet; AddressTablePriv *priv; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index add8abc995..5291951334 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -84,7 +84,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QVBoxLayout *vbox = new QVBoxLayout(); transactionView = new TransactionView(this); - connect(transactionView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(transactionDetails(const QModelIndex&))); + connect(transactionView, SIGNAL(doubleClicked(const QModelIndex&)), transactionView, SLOT(transactionDetails())); vbox->addWidget(transactionView); transactionsPage = new QWidget(this); @@ -229,7 +229,7 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) connect(walletModel, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); // Put transaction list in tabs - transactionView->setModel(walletModel->getTransactionTableModel()); + transactionView->setModel(walletModel); addressBookPage->setModel(walletModel->getAddressTableModel()); receiveCoinsPage->setModel(walletModel->getAddressTableModel()); @@ -298,7 +298,7 @@ void BitcoinGUI::setNumConnections(int count) default: icon = ":/icons/connect_4"; break; } labelConnections->setTextFormat(Qt::RichText); - labelConnections->setText("" + tr("%n connection(s)", "", count)); + labelConnections->setText(" " + tr("%n connection(s)", "", count)); } void BitcoinGUI::setNumBlocks(int count) @@ -411,13 +411,6 @@ void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) *payFee = (retval == QMessageBox::Yes); } -void BitcoinGUI::transactionDetails(const QModelIndex& idx) -{ - // A transaction is doubleclicked - TransactionDescDialog dlg(idx); - dlg.exec(); -} - void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) { TransactionTableModel *ttm = walletModel->getTransactionTableModel(); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 8c3632a3a6..b82818aa3f 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -102,7 +102,6 @@ private slots: void optionsClicked(); void aboutClicked(); void trayIconActivated(QSystemTrayIcon::ActivationReason reason); - void transactionDetails(const QModelIndex& idx); void incomingTransaction(const QModelIndex & parent, int start, int end); void exportClicked(); }; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 9b7cc632e0..4adda7e8fd 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1,6 +1,7 @@ #include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" #include "walletmodel.h" +#include "addresstablemodel.h" #include "guiutil.h" #include "addressbookpage.h" @@ -131,7 +132,7 @@ void SendCoinsDialog::on_buttonBox_rejected() void SendCoinsDialog::on_payTo_textChanged(const QString &address) { - ui->addAsLabel->setText(model->labelForAddress(address)); + ui->addAsLabel->setText(model->getAddressTableModel()->labelForAddress(address)); } void SendCoinsDialog::clear() diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 2477a1e8c9..17622e07fa 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -4,6 +4,7 @@ #include "guiconstants.h" #include "transactiondesc.h" #include "walletmodel.h" +#include "addresstablemodel.h" #include "headers.h" @@ -325,7 +326,7 @@ QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const */ QString TransactionTableModel::lookupAddress(const std::string &address) const { - QString label = walletModel->labelForAddress(QString::fromStdString(address)); + QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address)); QString description; if(label.isEmpty()) { @@ -538,7 +539,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const } else if (role == LabelRole) { - return walletModel->labelForAddress(QString::fromStdString(rec->address)); + return walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address)); } else if (role == AbsoluteAmountRole) { diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 5a383da2b8..53c33c7c48 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -2,9 +2,13 @@ #include "transactionfilterproxy.h" #include "transactionrecord.h" +#include "walletmodel.h" +#include "addresstablemodel.h" #include "transactiontablemodel.h" #include "guiutil.h" #include "csvmodelwriter.h" +#include "transactiondescdialog.h" +#include "editaddressdialog.h" #include #include @@ -17,6 +21,10 @@ #include #include #include +#include +#include +#include +#include #include @@ -90,23 +98,47 @@ TransactionView::TransactionView(QWidget *parent) : // Always show scroll bar view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); view->setTabKeyNavigation(false); + view->setContextMenuPolicy(Qt::CustomContextMenu); transactionView = view; + // Actions + QAction *copyAddressAction = new QAction("Copy address", this); + QAction *copyLabelAction = new QAction("Copy label", this); + QAction *editLabelAction = new QAction("Edit label", this); + QAction *showDetailsAction = new QAction("Show details...", this); + + contextMenu = new QMenu(); + contextMenu->addAction(copyAddressAction); + contextMenu->addAction(copyLabelAction); + contextMenu->addAction(editLabelAction); + contextMenu->addAction(showDetailsAction); + + // Connect actions connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); connect(addressWidget, SIGNAL(textChanged(const QString&)), this, SLOT(changedPrefix(const QString&))); connect(amountWidget, SIGNAL(textChanged(const QString&)), this, SLOT(changedAmount(const QString&))); connect(view, SIGNAL(doubleClicked(const QModelIndex&)), this, SIGNAL(doubleClicked(const QModelIndex&))); + + connect(view, + SIGNAL(customContextMenuRequested(const QPoint &)), + this, + SLOT(contextualMenu(const QPoint &))); + + connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress())); + connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel())); + connect(editLabelAction, SIGNAL(triggered()), this, SLOT(editLabel())); + connect(showDetailsAction, SIGNAL(triggered()), this, SLOT(showDetails())); } -void TransactionView::setModel(TransactionTableModel *model) +void TransactionView::setModel(WalletModel *model) { this->model = model; transactionProxyModel = new TransactionFilterProxy(this); - transactionProxyModel->setSourceModel(model); + transactionProxyModel->setSourceModel(model->getTransactionTableModel()); transactionProxyModel->setDynamicSortFilter(true); transactionProxyModel->setSortRole(Qt::EditRole); @@ -233,3 +265,77 @@ void TransactionView::exportClicked() } } +void TransactionView::contextualMenu(const QPoint &point) +{ + QModelIndex index = transactionView->indexAt(point); + if(index.isValid()) + { + contextMenu->exec(QCursor::pos()); + } +} + +void TransactionView::copyAddress() +{ + QModelIndexList selection = transactionView->selectionModel()->selectedRows(); + if(!selection.isEmpty()) + { + QApplication::clipboard()->setText(selection.at(0).data(TransactionTableModel::AddressRole).toString()); + } +} + +void TransactionView::copyLabel() +{ + QModelIndexList selection = transactionView->selectionModel()->selectedRows(); + if(!selection.isEmpty()) + { + QApplication::clipboard()->setText(selection.at(0).data(TransactionTableModel::LabelRole).toString()); + } +} + +void TransactionView::editLabel() +{ + QModelIndexList selection = transactionView->selectionModel()->selectedRows(); + if(!selection.isEmpty()) + { + AddressTableModel *addressBook = model->getAddressTableModel(); + QString address = selection.at(0).data(TransactionTableModel::AddressRole).toString(); + if(address.isEmpty()) + { + // If this transaction has no associated address, exit + return; + } + int idx = addressBook->lookupAddress(address); + if(idx != -1) + { + // Edit sending / receiving address + QModelIndex modelIdx = addressBook->index(idx, 0, QModelIndex()); + // Determine type of address, launch appropriate editor dialog type + QString type = modelIdx.data(AddressTableModel::TypeRole).toString(); + + EditAddressDialog dlg(type==AddressTableModel::Receive + ? EditAddressDialog::EditReceivingAddress + : EditAddressDialog::EditSendingAddress, + this); + dlg.setModel(addressBook); + dlg.loadRow(idx); + dlg.exec(); + } + else + { + // Add sending address + EditAddressDialog dlg(EditAddressDialog::NewSendingAddress, + this); + dlg.exec(); + } + } +} + +void TransactionView::showDetails() +{ + QModelIndexList selection = transactionView->selectionModel()->selectedRows(); + if(!selection.isEmpty()) + { + TransactionDescDialog dlg(selection.at(0)); + dlg.exec(); + } +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 25212c976e..f02751a074 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -3,7 +3,7 @@ #include -class TransactionTableModel; +class WalletModel; class TransactionFilterProxy; QT_BEGIN_NAMESPACE @@ -11,6 +11,7 @@ class QTableView; class QComboBox; class QLineEdit; class QModelIndex; +class QMenu; QT_END_NAMESPACE class TransactionView : public QWidget @@ -19,7 +20,7 @@ class TransactionView : public QWidget public: explicit TransactionView(QWidget *parent = 0); - void setModel(TransactionTableModel *model); + void setModel(WalletModel *model); enum DateEnum { @@ -33,7 +34,7 @@ public: }; private: - TransactionTableModel *model; + WalletModel *model; TransactionFilterProxy *transactionProxyModel; QTableView *transactionView; @@ -42,6 +43,11 @@ private: QLineEdit *addressWidget; QLineEdit *amountWidget; + QMenu *contextMenu; + +private slots: + void contextualMenu(const QPoint &); + signals: void doubleClicked(const QModelIndex&); @@ -51,6 +57,10 @@ public slots: void changedPrefix(const QString &prefix); void changedAmount(const QString &amount); void exportClicked(); + void showDetails(); + void copyAddress(); + void editLabel(); + void copyLabel(); }; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index f962b9a7c7..052bf37e39 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -123,18 +123,4 @@ TransactionTableModel *WalletModel::getTransactionTableModel() return transactionTableModel; } -/* Look up label for address in address book, if not found return empty string. - */ -QString WalletModel::labelForAddress(const QString &address) const -{ - CRITICAL_BLOCK(wallet->cs_mapAddressBook) - { - std::map::iterator mi = wallet->mapAddressBook.find(address.toStdString()); - if (mi != wallet->mapAddressBook.end()) - { - return QString::fromStdString(mi->second); - } - } - return QString(); -} diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 9c7d16fce6..5b46dfb69d 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -33,10 +33,6 @@ public: qint64 getBalance() const; int getNumTransactions() const; - /* Look up label for address in address book, if not found return empty string. - */ - QString labelForAddress(const QString &address) const; - /* Send coins */ StatusCode sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs=QString()); private: -- cgit v1.2.3 From 460cd40aa51a5367c361ebb32c2a2d39cdd08195 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 8 Jul 2011 22:45:15 +0200 Subject: Readme update --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index da480adf1c..5412cb9905 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,7 @@ This has been implemented: - CSV export of transactions -- User friendly transaction list with status icons, and real-time filtering +- User friendly transaction list with status icons, real-time filtering and a context menu that allows editing and copying labels - Show alternative icon when on testnet -- cgit v1.2.3 From fa989f42c1e2a927267ef8839563e21a2cd4b712 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 9 Jul 2011 10:06:49 +0200 Subject: remove magic number: change threshold for nLockTime to constant --- src/main.h | 4 +++- src/ui.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.h b/src/main.h index aa74ac5ab3..124c7c2671 100644 --- a/src/main.h +++ b/src/main.h @@ -37,6 +37,8 @@ static const int64 MIN_RELAY_TX_FEE = 10000; static const int64 MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } static const int COINBASE_MATURITY = 100; +// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. +static const int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC #ifdef USE_UPNP static const int fHaveUPnP = true; #else @@ -441,7 +443,7 @@ public: nBlockHeight = nBestHeight; if (nBlockTime == 0) nBlockTime = GetAdjustedTime(); - if ((int64)nLockTime < (nLockTime < 500000000 ? (int64)nBlockHeight : nBlockTime)) + if ((int64)nLockTime < (nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime)) return true; BOOST_FOREACH(const CTxIn& txin, vin) if (!txin.IsFinal()) diff --git a/src/ui.cpp b/src/ui.cpp index 9b84fb9e6b..ff0b4afb55 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -522,7 +522,7 @@ string FormatTxStatus(const CWalletTx& wtx) // Status if (!wtx.IsFinal()) { - if (wtx.nLockTime < 500000000) + if (wtx.nLockTime < LOCKTIME_THRESHOLD) return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime); else return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str()); -- cgit v1.2.3 From 2eace48d9a50e5393f3627bcd0614ed9b262794a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 9 Jul 2011 10:26:46 +0200 Subject: remove magic number: change threshold for nLockTime to constant --- src/main.h | 4 +++- src/qt/transactionrecord.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.h b/src/main.h index aa74ac5ab3..124c7c2671 100644 --- a/src/main.h +++ b/src/main.h @@ -37,6 +37,8 @@ static const int64 MIN_RELAY_TX_FEE = 10000; static const int64 MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } static const int COINBASE_MATURITY = 100; +// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. +static const int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC #ifdef USE_UPNP static const int fHaveUPnP = true; #else @@ -441,7 +443,7 @@ public: nBlockHeight = nBestHeight; if (nBlockTime == 0) nBlockTime = GetAdjustedTime(); - if ((int64)nLockTime < (nLockTime < 500000000 ? (int64)nBlockHeight : nBlockTime)) + if ((int64)nLockTime < (nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime)) return true; BOOST_FOREACH(const CTxIn& txin, vin) if (!txin.IsFinal()) diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 1b527bc7d3..c74b48bd61 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -195,7 +195,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) if (!wtx.IsFinal()) { - if (wtx.nLockTime < 500000000) + if (wtx.nLockTime < LOCKTIME_THRESHOLD) { status.status = TransactionStatus::OpenUntilBlock; status.open_for = nBestHeight - wtx.nLockTime; -- cgit v1.2.3 From f54d59ba4a9136a79734ac399433b0e74b1bec00 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 9 Jul 2011 10:53:55 +0200 Subject: add export functionality for address book / receiving addresses --- src/qt/addressbookpage.cpp | 57 +++++++++++++++++++++++++++++++--------------- src/qt/addressbookpage.h | 3 +++ src/qt/bitcoingui.cpp | 25 ++++++++++++-------- src/qt/bitcoingui.h | 1 - src/qt/transactionview.cpp | 4 ---- 5 files changed, 57 insertions(+), 33 deletions(-) diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 5127eb6009..4f629fc262 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -3,9 +3,12 @@ #include "addresstablemodel.h" #include "editaddressdialog.h" +#include "csvmodelwriter.h" #include #include +#include +#include #include AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : @@ -51,29 +54,24 @@ void AddressBookPage::setModel(AddressTableModel *model) // Refresh list from core model->updateList(); + proxyModel = new QSortFilterProxyModel(this); + proxyModel->setSourceModel(model); + proxyModel->setDynamicSortFilter(true); switch(tab) { - case ReceivingTab: { + case ReceivingTab: // Receive filter - QSortFilterProxyModel *receive_model = new QSortFilterProxyModel(this); - receive_model->setSourceModel(model); - receive_model->setDynamicSortFilter(true); - receive_model->setFilterRole(AddressTableModel::TypeRole); - receive_model->setFilterFixedString(AddressTableModel::Receive); - ui->tableView->setModel(receive_model); - ui->tableView->sortByColumn(0, Qt::AscendingOrder); - } break; - case SendingTab: { + proxyModel->setFilterRole(AddressTableModel::TypeRole); + proxyModel->setFilterFixedString(AddressTableModel::Receive); + break; + case SendingTab: // Send filter - QSortFilterProxyModel *send_model = new QSortFilterProxyModel(this); - send_model->setSourceModel(model); - send_model->setDynamicSortFilter(true); - send_model->setFilterRole(AddressTableModel::TypeRole); - send_model->setFilterFixedString(AddressTableModel::Send); - ui->tableView->setModel(send_model); - ui->tableView->sortByColumn(0, Qt::AscendingOrder); - } break; + proxyModel->setFilterRole(AddressTableModel::TypeRole); + proxyModel->setFilterFixedString(AddressTableModel::Send); + break; } + ui->tableView->setModel(proxyModel); + ui->tableView->sortByColumn(0, Qt::AscendingOrder); // Set column widths ui->tableView->horizontalHeader()->resizeSection( @@ -179,3 +177,26 @@ void AddressBookPage::done(int retval) QDialog::done(retval); } + +void AddressBookPage::exportClicked() +{ + // CSV is currently the only supported format + QString filename = QFileDialog::getSaveFileName( + this, + tr("Export Address Book Data"), + QDir::currentPath(), + tr("Comma separated file (*.csv)")); + + CSVModelWriter writer(filename); + + // name, column, role + writer.setModel(proxyModel); + writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole); + writer.addColumn("Address", AddressTableModel::Address, 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/addressbookpage.h b/src/qt/addressbookpage.h index c4039523af..53c7728c8c 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -11,6 +11,7 @@ class AddressTableModel; QT_BEGIN_NAMESPACE class QTableView; class QItemSelection; +class QSortFilterProxyModel; QT_END_NAMESPACE class AddressBookPage : public QDialog @@ -36,6 +37,7 @@ public: public slots: void done(int retval); + void exportClicked(); private: Ui::AddressBookPage *ui; @@ -43,6 +45,7 @@ private: Mode mode; Tabs tab; QString returnValue; + QSortFilterProxyModel *proxyModel; QTableView *getCurrentTable(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 5291951334..94bd9765e3 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -184,7 +184,6 @@ void BitcoinGUI::createActions() connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked())); connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); connect(openBitcoinAction, SIGNAL(triggered()), this, SLOT(show())); - connect(exportAction, SIGNAL(triggered()), this, SLOT(exportClicked())); } void BitcoinGUI::setClientModel(ClientModel *clientModel) @@ -440,28 +439,39 @@ void BitcoinGUI::gotoOverviewPage() { overviewAction->setChecked(true); centralWidget->setCurrentWidget(overviewPage); + exportAction->setEnabled(false); + disconnect(exportAction, SIGNAL(triggered()), 0, 0); } void BitcoinGUI::gotoHistoryPage() { historyAction->setChecked(true); centralWidget->setCurrentWidget(transactionsPage); + exportAction->setEnabled(true); + disconnect(exportAction, SIGNAL(triggered()), 0, 0); + connect(exportAction, SIGNAL(triggered()), transactionView, SLOT(exportClicked())); } void BitcoinGUI::gotoAddressBookPage() { addressBookAction->setChecked(true); centralWidget->setCurrentWidget(addressBookPage); - exportAction->setEnabled(false); // TODO + + exportAction->setEnabled(true); + disconnect(exportAction, SIGNAL(triggered()), 0, 0); + connect(exportAction, SIGNAL(triggered()), addressBookPage, SLOT(exportClicked())); } void BitcoinGUI::gotoReceiveCoinsPage() { receiveCoinsAction->setChecked(true); centralWidget->setCurrentWidget(receiveCoinsPage); - exportAction->setEnabled(false); // TODO + + exportAction->setEnabled(true); + disconnect(exportAction, SIGNAL(triggered()), 0, 0); + connect(exportAction, SIGNAL(triggered()), receiveCoinsPage, SLOT(exportClicked())); } void BitcoinGUI::gotoSendCoinsPage() @@ -469,13 +479,8 @@ void BitcoinGUI::gotoSendCoinsPage() sendCoinsAction->setChecked(true); sendCoinsPage->clear(); centralWidget->setCurrentWidget(sendCoinsPage); - exportAction->setEnabled(false); -} -void BitcoinGUI::exportClicked() -{ - // Redirect to the right view, as soon as export for other views - // (such as address book) is implemented. - transactionView->exportClicked(); + exportAction->setEnabled(false); + disconnect(exportAction, SIGNAL(triggered()), 0, 0); } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index b82818aa3f..6f4ca19146 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -103,7 +103,6 @@ private slots: void aboutClicked(); void trayIconActivated(QSystemTrayIcon::ActivationReason reason); void incomingTransaction(const QModelIndex & parent, int start, int end); - void exportClicked(); }; #endif diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 53c33c7c48..03df422605 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -241,10 +241,6 @@ void TransactionView::exportClicked() tr("Export Transaction Data"), QDir::currentPath(), tr("Comma separated file (*.csv)")); - if(!filename.endsWith(".csv")) - { - filename += ".csv"; - } CSVModelWriter writer(filename); -- cgit v1.2.3 From adad8e46c90f36d4828ce53b1a9f83b17994efc0 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 9 Jul 2011 10:55:46 +0200 Subject: README update --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 5412cb9905..25ed9a8cfc 100644 --- a/README.rst +++ b/README.rst @@ -18,7 +18,7 @@ This has been implemented: - Asks for confirmation before sending coins -- CSV export of transactions +- CSV export of transactions and address book - User friendly transaction list with status icons, real-time filtering and a context menu that allows editing and copying labels -- cgit v1.2.3 From ea8440d74264cf107da563031f4ccea4160d8486 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 9 Jul 2011 11:54:25 +0200 Subject: Add -fstack-protector to gcc CXX flags --- bitcoin-qt.pro | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 2a389e6840..99b6eb3896 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -12,6 +12,9 @@ macx:LIBS += -lboost_thread-mt windows:DEFINES += __WXMSW__ windows:LIBS += -lssl -lcrypto -lboost_system-mgw44-mt-1_43 -lboost_filesystem-mgw44-mt-1_43 -lboost_program_options-mgw44-mt-1_43 -lboost_thread-mgw44-mt-1_43 -ldb_cxx -lws2_32 -lgdi32 +# for extra security against potential buffer overflows +QMAKE_CXXFLAGS += -fstack-protector + # disable quite some warnings because bitcoin core "sins" a lot QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wno-invalid-offsetof -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare -Wno-char-subscripts -Wno-unused-value -Wno-sequence-point -Wno-parentheses -Wno-unknown-pragmas -Wno-switch -- cgit v1.2.3 From 7668631d1b65ff1d1e7ad09086f6dc91fdd7ed77 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 9 Jul 2011 14:00:00 +0200 Subject: remove placeholder text from ui form, code generator screws up on older qt --- src/qt/forms/sendcoinsdialog.ui | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index c3b966cac7..8009bd2b94 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -107,9 +107,6 @@ 34 - - - -- cgit v1.2.3 From 0b814f9ea3d47e35b6966061aa3f3c0aac5ab048 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 9 Jul 2011 15:26:57 +0200 Subject: add better windows7/vista look by nico_w --- bitcoin-qt.pro | 7 +- src/qt/bitcoin.cpp | 20 +++++ src/qt/qtwin.cpp | 222 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/qt/qtwin.h | 37 +++++++++ 4 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 src/qt/qtwin.cpp create mode 100644 src/qt/qtwin.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 99b6eb3896..5dd37b0b7e 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -81,7 +81,8 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/walletmodel.h \ src/bitcoinrpc.h \ src/qt/overviewpage.h \ - src/qt/csvmodelwriter.h + src/qt/csvmodelwriter.h \ + src/qt/qtwin.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -119,7 +120,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/walletmodel.cpp \ src/bitcoinrpc.cpp \ src/qt/overviewpage.cpp \ - src/qt/csvmodelwriter.cpp + src/qt/csvmodelwriter.cpp \ + src/qt/qtwin.cpp RESOURCES += \ src/qt/bitcoin.qrc @@ -134,4 +136,3 @@ FORMS += \ CODECFORTR = UTF-8 TRANSLATIONS = src/qt/locale/bitcoin_nl.ts src/qt/locale/bitcoin_de.ts - diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 78a20c51c0..bcc73de865 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -4,6 +4,7 @@ #include "bitcoingui.h" #include "clientmodel.h" #include "walletmodel.h" +#include "qtwin.h" #include "headers.h" #include "init.h" @@ -119,10 +120,29 @@ int main(int argc, char *argv[]) BitcoinGUI window; ClientModel clientModel(pwalletMain); WalletModel walletModel(pwalletMain); + guiref = &window; window.setClientModel(&clientModel); window.setWalletModel(&walletModel); +#ifdef Q_WS_WIN32 + // Windows-specific customization + window.setAttribute(Qt::WA_TranslucentBackground); + window.setAttribute(Qt::WA_NoSystemBackground, false); + QPalette pal = window.palette(); + QColor bg = pal.window().color(); + bg.setAlpha(0); + pal.setColor(QPalette::Window, bg); + window.setPalette(pal); + window.ensurePolished(); + window.setAttribute(Qt::WA_StyledBackground, false); +#endif + if (QtWin::isCompositionEnabled()) + { + QtWin::extendFrameIntoClientArea(&window); + window.setContentsMargins(0, 0, 0, 0); + } + window.show(); app.exec(); diff --git a/src/qt/qtwin.cpp b/src/qt/qtwin.cpp new file mode 100644 index 0000000000..bb029bba24 --- /dev/null +++ b/src/qt/qtwin.cpp @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Use, modification and distribution is allowed without limitation, +** warranty, liability or support of any kind. +** +****************************************************************************/ + +#include "qtwin.h" +#include +#include +#include +#include +#include + +#ifdef Q_WS_WIN + +#include + +// Blur behind data structures +#define DWM_BB_ENABLE 0x00000001 // fEnable has been specified +#define DWM_BB_BLURREGION 0x00000002 // hRgnBlur has been specified +#define DWM_BB_TRANSITIONONMAXIMIZED 0x00000004 // fTransitionOnMaximized has been specified +#define WM_DWMCOMPOSITIONCHANGED 0x031E // Composition changed window message + +typedef struct _DWM_BLURBEHIND +{ + DWORD dwFlags; + BOOL fEnable; + HRGN hRgnBlur; + BOOL fTransitionOnMaximized; +} DWM_BLURBEHIND, *PDWM_BLURBEHIND; + +typedef struct _MARGINS +{ + int cxLeftWidth; + int cxRightWidth; + int cyTopHeight; + int cyBottomHeight; +} MARGINS, *PMARGINS; + +typedef HRESULT (WINAPI *PtrDwmIsCompositionEnabled)(BOOL* pfEnabled); +typedef HRESULT (WINAPI *PtrDwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS* pMarInset); +typedef HRESULT (WINAPI *PtrDwmEnableBlurBehindWindow)(HWND hWnd, const DWM_BLURBEHIND* pBlurBehind); +typedef HRESULT (WINAPI *PtrDwmGetColorizationColor)(DWORD *pcrColorization, BOOL *pfOpaqueBlend); + +static PtrDwmIsCompositionEnabled pDwmIsCompositionEnabled= 0; +static PtrDwmEnableBlurBehindWindow pDwmEnableBlurBehindWindow = 0; +static PtrDwmExtendFrameIntoClientArea pDwmExtendFrameIntoClientArea = 0; +static PtrDwmGetColorizationColor pDwmGetColorizationColor = 0; + + +/* + * Internal helper class that notifies windows if the + * DWM compositing state changes and updates the widget + * flags correspondingly. + */ +class WindowNotifier : public QWidget +{ +public: + WindowNotifier() { winId(); } + void addWidget(QWidget *widget) { widgets.append(widget); } + void removeWidget(QWidget *widget) { widgets.removeAll(widget); } + bool winEvent(MSG *message, long *result); + +private: + QWidgetList widgets; +}; + +static bool resolveLibs() +{ + if (!pDwmIsCompositionEnabled) { + QLibrary dwmLib(QString::fromAscii("dwmapi")); + pDwmIsCompositionEnabled =(PtrDwmIsCompositionEnabled)dwmLib.resolve("DwmIsCompositionEnabled"); + pDwmExtendFrameIntoClientArea = (PtrDwmExtendFrameIntoClientArea)dwmLib.resolve("DwmExtendFrameIntoClientArea"); + pDwmEnableBlurBehindWindow = (PtrDwmEnableBlurBehindWindow)dwmLib.resolve("DwmEnableBlurBehindWindow"); + pDwmGetColorizationColor = (PtrDwmGetColorizationColor)dwmLib.resolve("DwmGetColorizationColor"); + } + return pDwmIsCompositionEnabled != 0; +} + +#endif + +/*! + * Chekcs and returns true if Windows DWM composition + * is currently enabled on the system. + * + * To get live notification on the availability of + * this feature, you will currently have to + * reimplement winEvent() on your widget and listen + * for the WM_DWMCOMPOSITIONCHANGED event to occur. + * + */ +bool QtWin::isCompositionEnabled() +{ +#ifdef Q_WS_WIN + if (resolveLibs()) { + HRESULT hr = S_OK; + BOOL isEnabled = false; + hr = pDwmIsCompositionEnabled(&isEnabled); + if (SUCCEEDED(hr)) + return isEnabled; + } +#endif + return false; +} + +/*! + * Enables Blur behind on a Widget. + * + * \a enable tells if the blur should be enabled or not + */ +bool QtWin::enableBlurBehindWindow(QWidget *widget, bool enable) +{ + Q_ASSERT(widget); + bool result = false; +#ifdef Q_WS_WIN + if (resolveLibs()) { + DWM_BLURBEHIND bb = {0}; + HRESULT hr = S_OK; + bb.fEnable = enable; + bb.dwFlags = DWM_BB_ENABLE; + bb.hRgnBlur = NULL; + widget->setAttribute(Qt::WA_TranslucentBackground, enable); + widget->setAttribute(Qt::WA_NoSystemBackground, enable); + hr = pDwmEnableBlurBehindWindow(widget->winId(), &bb); + if (SUCCEEDED(hr)) { + result = true; + windowNotifier()->addWidget(widget); + } + } +#endif + return result; +} + +/*! + * ExtendFrameIntoClientArea. + * + * This controls the rendering of the frame inside the window. + * Note that passing margins of -1 (the default value) will completely + * remove the frame from the window. + * + * \note you should not call enableBlurBehindWindow before calling + * this functions + * + * \a enable tells if the blur should be enabled or not + */ +bool QtWin::extendFrameIntoClientArea(QWidget *widget, int left, int top, int right, int bottom) +{ + + Q_ASSERT(widget); + Q_UNUSED(left); + Q_UNUSED(top); + Q_UNUSED(right); + Q_UNUSED(bottom); + + bool result = false; +#ifdef Q_WS_WIN + if (resolveLibs()) { + QLibrary dwmLib(QString::fromAscii("dwmapi")); + HRESULT hr = S_OK; + MARGINS m = {left, top, right, bottom}; + hr = pDwmExtendFrameIntoClientArea(widget->winId(), &m); + if (SUCCEEDED(hr)) { + result = true; + windowNotifier()->addWidget(widget); + } + widget->setAttribute(Qt::WA_TranslucentBackground, result); + } +#endif + return result; +} + +/*! + * Returns the current colorizationColor for the window. + * + * \a enable tells if the blur should be enabled or not + */ +QColor QtWin::colorizatinColor() +{ + QColor resultColor = QApplication::palette().window().color(); + +#ifdef Q_WS_WIN + if (resolveLibs()) { + DWORD color = 0; + BOOL opaque = FALSE; + QLibrary dwmLib(QString::fromAscii("dwmapi")); + HRESULT hr = S_OK; + hr = pDwmGetColorizationColor(&color, &opaque); + if (SUCCEEDED(hr)) + resultColor = QColor(color); + } +#endif + return resultColor; +} + +#ifdef Q_WS_WIN +WindowNotifier *QtWin::windowNotifier() +{ + static WindowNotifier *windowNotifierInstance = 0; + if (!windowNotifierInstance) + windowNotifierInstance = new WindowNotifier; + return windowNotifierInstance; +} + + +/* Notify all enabled windows that the DWM state changed */ +bool WindowNotifier::winEvent(MSG *message, long *result) +{ + if (message && message->message == WM_DWMCOMPOSITIONCHANGED) { + bool compositionEnabled = QtWin::isCompositionEnabled(); + foreach(QWidget * widget, widgets) { + if (widget) { + widget->setAttribute(Qt::WA_NoSystemBackground, compositionEnabled); + } + widget->update(); + } + } + return QWidget::winEvent(message, result); +} +#endif diff --git a/src/qt/qtwin.h b/src/qt/qtwin.h new file mode 100644 index 0000000000..4008c7fa28 --- /dev/null +++ b/src/qt/qtwin.h @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Use, modification and distribution is allowed without limitation, +** warranty, liability or support of any kind. +** +****************************************************************************/ + +#ifndef QTWIN_H +#define QTWIN_H + +#include +#include +/** + * This is a helper class for using the Desktop Window Manager + * functionality on Windows 7 and Windows Vista. On other platforms + * these functions will simply not do anything. + */ + +class WindowNotifier; + +class QtWin +{ +public: + static bool enableBlurBehindWindow(QWidget *widget, bool enable = true); + static bool extendFrameIntoClientArea(QWidget *widget, + int left = -1, int top = -1, + int right = -1, int bottom = -1); + static bool isCompositionEnabled(); + static QColor colorizatinColor(); + +private: + static WindowNotifier *windowNotifier(); +}; + +#endif // QTWIN_H -- cgit v1.2.3 From c87cdc9160d27e56d7a4236d1bab87e06c9b7c31 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 9 Jul 2011 15:58:05 +0200 Subject: wxp/mingw build fixes --- bitcoin-qt.pro | 1 + src/qt/bitcoin.cpp | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 5dd37b0b7e..aee056eaee 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -14,6 +14,7 @@ windows:LIBS += -lssl -lcrypto -lboost_system-mgw44-mt-1_43 -lboost_filesystem-m # for extra security against potential buffer overflows QMAKE_CXXFLAGS += -fstack-protector +QMAKE_LFLAGS += -fstack-protector # disable quite some warnings because bitcoin core "sins" a lot QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wno-invalid-offsetof -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare -Wno-char-subscripts -Wno-unused-value -Wno-sequence-point -Wno-parentheses -Wno-unknown-pragmas -Wno-switch diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index bcc73de865..63d1d70670 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -125,20 +125,20 @@ int main(int argc, char *argv[]) window.setClientModel(&clientModel); window.setWalletModel(&walletModel); -#ifdef Q_WS_WIN32 - // Windows-specific customization - window.setAttribute(Qt::WA_TranslucentBackground); - window.setAttribute(Qt::WA_NoSystemBackground, false); - QPalette pal = window.palette(); - QColor bg = pal.window().color(); - bg.setAlpha(0); - pal.setColor(QPalette::Window, bg); - window.setPalette(pal); - window.ensurePolished(); - window.setAttribute(Qt::WA_StyledBackground, false); -#endif if (QtWin::isCompositionEnabled()) { +#ifdef Q_WS_WIN32 + // Windows-specific customization + window.setAttribute(Qt::WA_TranslucentBackground); + window.setAttribute(Qt::WA_NoSystemBackground, false); + QPalette pal = window.palette(); + QColor bg = pal.window().color(); + bg.setAlpha(0); + pal.setColor(QPalette::Window, bg); + window.setPalette(pal); + window.ensurePolished(); + window.setAttribute(Qt::WA_StyledBackground, false); +#endif QtWin::extendFrameIntoClientArea(&window); window.setContentsMargins(0, 0, 0, 0); } -- cgit v1.2.3 From eee0d2391cb9d2c72d75c9912363392f87c7b976 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 11 Jul 2011 19:35:08 +0200 Subject: Make tooltip on refresh more clear --- src/qt/bitcoingui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 94bd9765e3..567de52275 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -342,7 +342,7 @@ void BitcoinGUI::setNumBlocks(int count) } labelBlocks->setText(" " + text); - labelBlocks->setToolTip(tr("%n block(s) in total, last block was generated %1", "", count) + labelBlocks->setToolTip(tr("Downloaded %n block(s) of transaction history. Last block was generated %1.", "", count) .arg(QLocale::system().toString(lastBlockDate))); } -- cgit v1.2.3 From df5ccbd2b2c72039f1e0e69fc51957f7c2d6068d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 11 Jul 2011 20:42:10 +0200 Subject: Show unconfirmed balance on overview page --- README.rst | 8 +++++--- src/qt/bitcoingui.cpp | 18 +----------------- src/qt/bitcoingui.h | 2 -- src/qt/forms/overviewpage.ui | 18 ++++++++++++++++-- src/qt/overviewpage.cpp | 26 +++++++++++++++++++++++--- src/qt/overviewpage.h | 6 +++++- src/qt/walletmodel.cpp | 5 +++++ src/qt/walletmodel.h | 1 + src/wallet.cpp | 15 +++++++++++++++ src/wallet.h | 1 + 10 files changed, 72 insertions(+), 28 deletions(-) diff --git a/README.rst b/README.rst index 25ed9a8cfc..9ef4576509 100644 --- a/README.rst +++ b/README.rst @@ -16,13 +16,15 @@ This has been implemented: - Tabbed interface +- Overview page with current balance, unconfirmed balance, etc + +- User friendly transaction list with status icons, real-time filtering and a context menu that allows editing and copying labels + - Asks for confirmation before sending coins - CSV export of transactions and address book -- User friendly transaction list with status icons, real-time filtering and a context menu that allows editing and copying labels - -- Show alternative icon when on testnet +- Shows alternative icon when connected to testnet - Progress bar on initial block download diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 567de52275..144bb22a8a 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -217,19 +217,13 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) { this->walletModel = walletModel; - // Keep up to date with wallet - setBalance(walletModel->getBalance()); - connect(walletModel, SIGNAL(balanceChanged(qint64)), this, SLOT(setBalance(qint64))); - - setNumTransactions(walletModel->getNumTransactions()); - connect(walletModel, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); - // Report errors from wallet thread connect(walletModel, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); // Put transaction list in tabs transactionView->setModel(walletModel); + overviewPage->setModel(walletModel); addressBookPage->setModel(walletModel->getAddressTableModel()); receiveCoinsPage->setModel(walletModel->getAddressTableModel()); sendCoinsPage->setModel(walletModel); @@ -280,11 +274,6 @@ void BitcoinGUI::aboutClicked() dlg.exec(); } -void BitcoinGUI::setBalance(qint64 balance) -{ - overviewPage->setBalance(balance); -} - void BitcoinGUI::setNumConnections(int count) { QString icon; @@ -346,11 +335,6 @@ void BitcoinGUI::setNumBlocks(int count) .arg(QLocale::system().toString(lastBlockDate))); } -void BitcoinGUI::setNumTransactions(int count) -{ - overviewPage->setNumTransactions(count); -} - void BitcoinGUI::error(const QString &title, const QString &message) { // Report errors from network/worker thread diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 6f4ca19146..95e0eb70fe 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -79,10 +79,8 @@ private: void createTrayIcon(); public slots: - void setBalance(qint64 balance); void setNumConnections(int count); void setNumBlocks(int count); - void setNumTransactions(int count); void error(const QString &title, const QString &message); /* It is currently not possible to pass a return value to another thread through BlockingQueuedConnection, so use an indirected pointer. diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 4cb28d5bf7..d8362a7b25 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -46,20 +46,34 @@ - + Number of transactions: - + 0 + + + + Unconfirmed: + + + + + + + 0 BTC + + + diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 87f95fddf3..c620200b49 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -1,6 +1,7 @@ #include "overviewpage.h" #include "ui_overviewpage.h" +#include "walletmodel.h" #include "guiutil.h" OverviewPage::OverviewPage(QWidget *parent) : @@ -14,9 +15,14 @@ OverviewPage::OverviewPage(QWidget *parent) : ui->labelBalance->setToolTip(tr("Your current balance")); ui->labelBalance->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard); + // Balance: + ui->labelUnconfirmed->setFont(QFont("Monospace", -1, QFont::Bold)); + ui->labelUnconfirmed->setToolTip(tr("Balance of transactions that have yet to be confirmed")); + ui->labelUnconfirmed->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard); + + ui->labelNumTransactions->setToolTip(tr("Total number of transactions in wallet")); + // Overview page should show: - // Balance - // Unconfirmed balance // Last received transaction(s) // Last sent transaction(s) } @@ -26,12 +32,26 @@ OverviewPage::~OverviewPage() delete ui; } -void OverviewPage::setBalance(qint64 balance) +void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance) { ui->labelBalance->setText(GUIUtil::formatMoney(balance) + QString(" BTC")); + ui->labelUnconfirmed->setText(GUIUtil::formatMoney(unconfirmedBalance) + QString(" BTC")); } void OverviewPage::setNumTransactions(int count) { ui->labelNumTransactions->setText(QLocale::system().toString(count)); } + +void OverviewPage::setModel(WalletModel *model) +{ + this->model = model; + + // Keep up to date with wallet + setBalance(model->getBalance(), model->getUnconfirmedBalance()); + connect(model, SIGNAL(balanceChanged(qint64)), this, SLOT(setBalance(qint64))); + + setNumTransactions(model->getNumTransactions()); + connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); + +} diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index fbd6853b37..acf83c720f 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -6,6 +6,7 @@ namespace Ui { class OverviewPage; } +class WalletModel; class OverviewPage : public QWidget { @@ -15,12 +16,15 @@ public: explicit OverviewPage(QWidget *parent = 0); ~OverviewPage(); + void setModel(WalletModel *model); + public slots: - void setBalance(qint64 balance); + void setBalance(qint64 balance, qint64 unconfirmedBalance); void setNumTransactions(int count); private: Ui::OverviewPage *ui; + WalletModel *model; }; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 052bf37e39..3e7152da14 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -28,6 +28,11 @@ qint64 WalletModel::getBalance() const return wallet->GetBalance(); } +qint64 WalletModel::getUnconfirmedBalance() const +{ + return wallet->GetUnconfirmedBalance(); +} + int WalletModel::getNumTransactions() const { int numTransactions = 0; diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 5b46dfb69d..4c34cfbb59 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -31,6 +31,7 @@ public: TransactionTableModel *getTransactionTableModel(); qint64 getBalance() const; + qint64 getUnconfirmedBalance() const; int getNumTransactions() const; /* Send coins */ diff --git a/src/wallet.cpp b/src/wallet.cpp index 5b88f387c7..fa57755242 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -570,6 +570,21 @@ int64 CWallet::GetBalance() const return nTotal; } +int64 CWallet::GetUnconfirmedBalance() const +{ + int64 nTotal = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsFinal() && pcoin->IsConfirmed()) + continue; + nTotal += pcoin->GetAvailableCredit(); + } + } + return nTotal; +} bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) const { diff --git a/src/wallet.h b/src/wallet.h index 7d9db97267..078d7e6e97 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -57,6 +57,7 @@ public: void ReacceptWalletTransactions(); void ResendWalletTransactions(); int64 GetBalance() const; + int64 GetUnconfirmedBalance() const; bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); -- cgit v1.2.3 From 4a843976e094eee9eb8528f7828f389bd31c462e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 11 Jul 2011 21:01:53 +0200 Subject: also show balloon on sent transaction, to notify when coins sent --- src/qt/bitcoingui.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 144bb22a8a..0c66bd4ca7 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -399,7 +399,7 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int TransactionTableModel *ttm = walletModel->getTransactionTableModel(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent) .data(Qt::EditRole).toULongLong(); - if(amount>0 && !clientModel->inInitialBlockDownload()) + if(!clientModel->inInitialBlockDownload()) { // On incoming transaction, make an info balloon // Unless the initial block download is in progress, to prevent balloon-spam @@ -410,7 +410,8 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int QString address = ttm->index(start, TransactionTableModel::ToAddress, parent) .data().toString(); - trayIcon->showMessage(tr("Incoming transaction"), + trayIcon->showMessage((amount)<0 ? tr("Sent transaction") : + tr("Incoming transaction"), tr("Date: ") + date + "\n" + tr("Amount: ") + GUIUtil::formatMoney(amount, true) + "\n" + tr("Type: ") + type + "\n" + -- cgit v1.2.3 From 2a097fc5edc2f22aefe520a7578bb4b2e53cb6f2 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 13 Jul 2011 08:27:41 +0200 Subject: Update mac build (alkor on forums) --- bitcoin-qt.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index aee056eaee..9708b82fe6 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -8,7 +8,7 @@ CONFIG += no_include_pwd # for boost 1.37, add -mt to the boost libraries unix:LIBS += -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 BOOST_FILESYSTEM_VERSION=3 -macx:LIBS += -lboost_thread-mt +macx:LIBS += -lboost_thread-mt -lboost_system-mt -lboost_filesystem-mt -lboost_program_options-mt windows:DEFINES += __WXMSW__ windows:LIBS += -lssl -lcrypto -lboost_system-mgw44-mt-1_43 -lboost_filesystem-mgw44-mt-1_43 -lboost_program_options-mgw44-mt-1_43 -lboost_thread-mgw44-mt-1_43 -ldb_cxx -lws2_32 -lgdi32 -- cgit v1.2.3 From 77b615cebabbe7274e6409f95dda61e393a75e7f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 14 Jul 2011 21:21:17 +0200 Subject: solve warnings at startup --- src/qt/bitcoingui.cpp | 2 +- src/qt/overviewpage.cpp | 2 +- src/qt/sendcoinsdialog.cpp | 5 ----- src/qt/sendcoinsdialog.h | 1 - src/qt/walletmodel.cpp | 2 +- src/qt/walletmodel.h | 2 +- 6 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 0c66bd4ca7..016f261941 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -84,7 +84,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QVBoxLayout *vbox = new QVBoxLayout(); transactionView = new TransactionView(this); - connect(transactionView, SIGNAL(doubleClicked(const QModelIndex&)), transactionView, SLOT(transactionDetails())); + connect(transactionView, SIGNAL(doubleClicked(const QModelIndex&)), transactionView, SLOT(showDetails())); vbox->addWidget(transactionView); transactionsPage = new QWidget(this); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index c620200b49..d991f0d7ba 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -49,7 +49,7 @@ void OverviewPage::setModel(WalletModel *model) // Keep up to date with wallet setBalance(model->getBalance(), model->getUnconfirmedBalance()); - connect(model, SIGNAL(balanceChanged(qint64)), this, SLOT(setBalance(qint64))); + connect(model, SIGNAL(balanceChanged(qint64, qint64)), this, SLOT(setBalance(qint64, qint64))); setNumTransactions(model->getNumTransactions()); connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 4adda7e8fd..01d68dc0af 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -125,11 +125,6 @@ void SendCoinsDialog::on_addressBookButton_clicked() } } -void SendCoinsDialog::on_buttonBox_rejected() -{ - reject(); -} - void SendCoinsDialog::on_payTo_textChanged(const QString &address) { ui->addAsLabel->setText(model->getAddressTableModel()->labelForAddress(address)); diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 4e019b7296..46814af466 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -29,7 +29,6 @@ private: private slots: void on_payTo_textChanged(const QString &address); - void on_buttonBox_rejected(); void on_addressBookButton_clicked(); void on_pasteButton_clicked(); void on_sendButton_clicked(); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 3e7152da14..afe095c977 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -48,7 +48,7 @@ void WalletModel::update() // Plainly emit all signals for now. To be more efficient this should check // whether the values actually changed first, although it'd be even better if these // were events coming in from the bitcoin core. - emit balanceChanged(getBalance()); + emit balanceChanged(getBalance(), wallet->GetUnconfirmedBalance()); emit numTransactionsChanged(getNumTransactions()); addressTableModel->update(); diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 4c34cfbb59..1105fb03fa 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -47,7 +47,7 @@ private: TransactionTableModel *transactionTableModel; signals: - void balanceChanged(qint64 balance); + void balanceChanged(qint64 balance, qint64 unconfirmedBalance); void numTransactionsChanged(int count); // Asynchronous error notification -- cgit v1.2.3 From 608810a3e77b9cdc6513e1f09388fd75f655dbee Mon Sep 17 00:00:00 2001 From: Celil Date: Thu, 14 Jul 2011 17:11:11 -0700 Subject: Fix error when export is cancelled without specifying a filename. --- src/qt/addressbookpage.cpp | 2 ++ src/qt/transactionview.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 4f629fc262..063e510c30 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -187,6 +187,8 @@ void AddressBookPage::exportClicked() QDir::currentPath(), tr("Comma separated file (*.csv)")); + if (filename.isNull()) return; + CSVModelWriter writer(filename); // name, column, role diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 03df422605..8aa43395e7 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -242,6 +242,8 @@ void TransactionView::exportClicked() QDir::currentPath(), tr("Comma separated file (*.csv)")); + if (filename.isNull()) return; + CSVModelWriter writer(filename); // name, column, role -- cgit v1.2.3 From ea37fb9187195d746a4b8a84da5cb6bd9e0e4aab Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 15 Jul 2011 14:55:21 +0200 Subject: Now that send coins / receive coins etc are tabs, remove them from menu, and reorganize menu bar --- src/qt/bitcoingui.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 016f261941..9ee178ac64 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -55,14 +55,10 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Menus QMenu *file = menuBar()->addMenu("&File"); - file->addAction(sendCoinsAction); - file->addAction(receiveCoinsAction); + file->addAction(optionsAction); file->addSeparator(); file->addAction(quitAction); - QMenu *settings = menuBar()->addMenu("&Settings"); - settings->addAction(optionsAction); - QMenu *help = menuBar()->addMenu("&Help"); help->addAction(aboutAction); -- cgit v1.2.3 From 249c6818f171b75930736872d61b0cdcda7e9832 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 15 Jul 2011 15:06:28 +0200 Subject: fix quoting --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9ef4576509..16d03c3ab8 100644 --- a/README.rst +++ b/README.rst @@ -71,7 +71,7 @@ Windows build instructions: - Download and extract the `dependencies archive`_ [#]_, or compile openssl, boost and dbcxx yourself. -- Copy the contents of the folder "deps" to "X:\QtSDK\mingw", replace X:\ with the location where you installed the Qt SDK. Make sure that the contents of "deps/include" end up in the current "include" directory and such. +- Copy the contents of the folder "deps" to "X:\\QtSDK\\mingw", replace X:\\ with the location where you installed the Qt SDK. Make sure that the contents of "deps\\include" end up in the current "include" directory. - Open the .pro file in QT creator and build as normal (ctrl-B) -- cgit v1.2.3 From a35ee9633690fbadc001b2d8fe1dd3ebb852dc25 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 15 Jul 2011 15:09:49 +0200 Subject: Add call to request unconfirmed balance --- src/wallet.cpp | 15 +++++++++++++++ src/wallet.h | 1 + 2 files changed, 16 insertions(+) diff --git a/src/wallet.cpp b/src/wallet.cpp index 5b88f387c7..fa57755242 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -570,6 +570,21 @@ int64 CWallet::GetBalance() const return nTotal; } +int64 CWallet::GetUnconfirmedBalance() const +{ + int64 nTotal = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsFinal() && pcoin->IsConfirmed()) + continue; + nTotal += pcoin->GetAvailableCredit(); + } + } + return nTotal; +} bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) const { diff --git a/src/wallet.h b/src/wallet.h index 7d9db97267..078d7e6e97 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -57,6 +57,7 @@ public: void ReacceptWalletTransactions(); void ResendWalletTransactions(); int64 GetBalance() const; + int64 GetUnconfirmedBalance() const; bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); -- cgit v1.2.3 From a24b23622e504f5134dd8011af5bbe68cb9443f1 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 15 Jul 2011 15:42:02 +0200 Subject: move README.md out of the way for now --- README-original.md | 16 ++++++++++++++++ README.md | 16 ---------------- 2 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 README-original.md delete mode 100644 README.md diff --git a/README-original.md b/README-original.md new file mode 100644 index 0000000000..ab328adbff --- /dev/null +++ b/README-original.md @@ -0,0 +1,16 @@ +Bitcoin integration/staging tree + +Development process +=================== + +Developers work in their own trees, then submit pull requests when they think their feature or bug fix is ready. + +If it is a simple/trivial/non-controversial change, then one of the bitcoin development team members simply pulls it. + +If it is a more complicated or potentially controversial change, then the patch submitter will be asked to start a discussion (if they haven't already) on the development forums: http://www.bitcoin.org/smf/index.php?board=6.0 +The patch will be accepted if there is broad consensus that it is a good thing. Developers should expect to rework and resubmit patches if they don't match the project's coding conventions (see coding.txt) or are controversial. + +The master branch is regularly built and tested (by who? need people willing to be quality assurance testers), and periodically pushed to the subversion repo to become the official, stable, released bitcoin. + + +Feature branches are created when there are major new features being worked on by several people. diff --git a/README.md b/README.md deleted file mode 100644 index ab328adbff..0000000000 --- a/README.md +++ /dev/null @@ -1,16 +0,0 @@ -Bitcoin integration/staging tree - -Development process -=================== - -Developers work in their own trees, then submit pull requests when they think their feature or bug fix is ready. - -If it is a simple/trivial/non-controversial change, then one of the bitcoin development team members simply pulls it. - -If it is a more complicated or potentially controversial change, then the patch submitter will be asked to start a discussion (if they haven't already) on the development forums: http://www.bitcoin.org/smf/index.php?board=6.0 -The patch will be accepted if there is broad consensus that it is a good thing. Developers should expect to rework and resubmit patches if they don't match the project's coding conventions (see coding.txt) or are controversial. - -The master branch is regularly built and tested (by who? need people willing to be quality assurance testers), and periodically pushed to the subversion repo to become the official, stable, released bitcoin. - - -Feature branches are created when there are major new features being worked on by several people. -- cgit v1.2.3 From 97f908e483d06796a1b9c8ce34de304fcc36c31f Mon Sep 17 00:00:00 2001 From: Celil Date: Fri, 15 Jul 2011 16:10:48 -0700 Subject: Suppress uninitialized warnings. --- bitcoin-qt.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 9af4c671f1..e1b855176d 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -17,7 +17,7 @@ QMAKE_CXXFLAGS += -fstack-protector QMAKE_LFLAGS += -fstack-protector # disable quite some warnings because bitcoin core "sins" a lot -QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wno-invalid-offsetof -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare -Wno-char-subscripts -Wno-unused-value -Wno-sequence-point -Wno-parentheses -Wno-unknown-pragmas -Wno-switch +QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wno-invalid-offsetof -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare -Wno-char-subscripts -Wno-unused-value -Wno-sequence-point -Wno-parentheses -Wno-unknown-pragmas -Wno-switch -Wno-uninitialized # Input DEPENDPATH += src/qt src src/cryptopp src json/include -- cgit v1.2.3 From a5e6d72339f28699bc356603f695bd620be37e83 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 16 Jul 2011 19:01:05 +0200 Subject: add sendmany support --- bitcoin-qt.pro | 11 ++- doc/assets-attribution.txt | 2 +- src/qt/addresstablemodel.cpp | 28 +++--- src/qt/addresstablemodel.h | 22 +++-- src/qt/bitcoin.qrc | 1 + src/qt/bitcoinaddressvalidator.cpp | 6 ++ src/qt/bitcoinamountfield.cpp | 33 ++++++- src/qt/bitcoinamountfield.h | 10 ++- src/qt/editaddressdialog.cpp | 19 ++-- src/qt/forms/sendcoinsdialog.ui | 166 +++------------------------------- src/qt/forms/sendcoinsentry.ui | 175 ++++++++++++++++++++++++++++++++++++ src/qt/qvalidatedlineedit.cpp | 37 ++++++++ src/qt/qvalidatedlineedit.h | 27 ++++++ src/qt/sendcoinsdialog.cpp | 176 +++++++++++++++++++++++++------------ src/qt/sendcoinsdialog.h | 14 ++- src/qt/sendcoinsentry.cpp | 119 +++++++++++++++++++++++++ src/qt/sendcoinsentry.h | 45 ++++++++++ src/qt/walletmodel.cpp | 91 ++++++++++++++----- src/qt/walletmodel.h | 31 ++++++- 19 files changed, 735 insertions(+), 278 deletions(-) create mode 100644 src/qt/forms/sendcoinsentry.ui create mode 100644 src/qt/qvalidatedlineedit.cpp create mode 100644 src/qt/qvalidatedlineedit.h create mode 100644 src/qt/sendcoinsentry.cpp create mode 100644 src/qt/sendcoinsentry.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 9af4c671f1..cdff04b43a 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -84,7 +84,9 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/overviewpage.h \ src/qt/csvmodelwriter.h \ src/qt/qtwin.h \ - src/crypter.h + src/crypter.h \ + src/qt/sendcoinsentry.h \ + src/qt/qvalidatedlineedit.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -124,7 +126,9 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/overviewpage.cpp \ src/qt/csvmodelwriter.cpp \ src/qt/qtwin.cpp \ - src/crypter.cpp + src/crypter.cpp \ + src/qt/sendcoinsentry.cpp \ + src/qt/qvalidatedlineedit.cpp RESOURCES += \ src/qt/bitcoin.qrc @@ -135,7 +139,8 @@ FORMS += \ src/qt/forms/aboutdialog.ui \ src/qt/forms/editaddressdialog.ui \ src/qt/forms/transactiondescdialog.ui \ - src/qt/forms/overviewpage.ui + src/qt/forms/overviewpage.ui \ + src/qt/forms/sendcoinsentry.ui CODECFORTR = UTF-8 TRANSLATIONS = src/qt/locale/bitcoin_nl.ts src/qt/locale/bitcoin_de.ts diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index f58b0da40c..f3900fe0fa 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -29,7 +29,7 @@ License: You are free to do with these icons as you wish, including selling, Icon: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png, src/qt/res/icons/editcopy.png, src/qt/res/icons/editpaste.png, src/qt/res/icons/add.png, src/qt/res/icons/edit.png, - src/qt/res/icons/editdelete.png + src/qt/res/icons/editdelete.png, src/qt/res/icons/remove.png (edited) Designer: http://www.everaldo.com Icon Pack: Crystal SVG License: LGPL diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 4578ca740f..125ceebb33 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -1,5 +1,6 @@ #include "addresstablemodel.h" #include "guiutil.h" +#include "walletmodel.h" #include "headers.h" @@ -72,8 +73,8 @@ struct AddressTablePriv } }; -AddressTableModel::AddressTableModel(CWallet *wallet, QObject *parent) : - QAbstractTableModel(parent),wallet(wallet),priv(0) +AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) : + QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0) { columns << tr("Label") << tr("Address"); priv = new AddressTablePriv(wallet); @@ -150,6 +151,8 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu return false; AddressTableEntry *rec = static_cast(index.internalPointer()); + editStatus = OK; + if(role == Qt::EditRole) { switch(index.column()) @@ -160,8 +163,11 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu break; case Address: // Refuse to set invalid address - if(!validateAddress(value.toString())) + if(!walletModel->validateAddress(value.toString())) + { + editStatus = INVALID_ADDRESS; return false; + } // Double-check that we're not overwriting receiving address if(rec->type == AddressTableEntry::Sending) { @@ -240,13 +246,22 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con std::string strLabel = label.toStdString(); std::string strAddress = address.toStdString(); + editStatus = OK; + + if(type == Send) { + if(!walletModel->validateAddress(address)) + { + editStatus = INVALID_ADDRESS; + return QString(); + } // Check for duplicate CRITICAL_BLOCK(wallet->cs_mapAddressBook) { if(wallet->mapAddressBook.count(strAddress)) { + editStatus = DUPLICATE_ADDRESS; return QString(); } } @@ -291,13 +306,6 @@ void AddressTableModel::update() } -bool AddressTableModel::validateAddress(const QString &address) -{ - uint160 hash160 = 0; - - return AddressToHash160(address.toStdString(), hash160); -} - /* Look up label for address in address book, if not found return empty string. */ QString AddressTableModel::labelForAddress(const QString &address) const diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index d48e786621..296fa58054 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -6,12 +6,13 @@ class AddressTablePriv; class CWallet; +class WalletModel; class AddressTableModel : public QAbstractTableModel { Q_OBJECT public: - explicit AddressTableModel(CWallet *wallet, QObject *parent = 0); + explicit AddressTableModel(CWallet *wallet, WalletModel *parent = 0); ~AddressTableModel(); enum ColumnIndex { @@ -19,9 +20,16 @@ public: Address = 1 /* Bitcoin address */ }; - enum { + enum RoleIndex { TypeRole = Qt::UserRole - } RoleIndex; + }; + + // Return status of last edit/insert operation + enum EditStatus { + OK = 0, + INVALID_ADDRESS = 1, + DUPLICATE_ADDRESS = 2 + }; static const QString Send; /* Send addres */ static const QString Receive; /* Receive address */ @@ -45,10 +53,6 @@ public: */ void updateList(); - /* Check address for validity - */ - bool validateAddress(const QString &address); - /* Look up label for address in address book, if not found return empty string. */ QString labelForAddress(const QString &address) const; @@ -58,10 +62,14 @@ public: */ int lookupAddress(const QString &address) const; + EditStatus getEditStatus() const { return editStatus; } + private: + WalletModel *walletModel; CWallet *wallet; AddressTablePriv *priv; QStringList columns; + EditStatus editStatus; signals: void defaultAddressChanged(const QString &address); diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 1522ce61e3..9ef8b2afd5 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -31,6 +31,7 @@ res/icons/export.png res/icons/synced.png res/icons/notsynced.png + res/icons/remove.png res/images/about.png diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index 4308a893c2..373877808f 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -57,5 +57,11 @@ QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) co } } + // Empty address is "intermediate" input + if(input.isEmpty()) + { + state = QValidator::Intermediate; + } + return state; } diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 1359a32b87..d545dc52e8 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -1,4 +1,5 @@ #include "bitcoinamountfield.h" +#include "qvalidatedlineedit.h" #include #include @@ -9,12 +10,12 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): QWidget(parent), amount(0), decimals(0) { - amount = new QLineEdit(this); + amount = new QValidatedLineEdit(this); amount->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this)); amount->setAlignment(Qt::AlignRight|Qt::AlignVCenter); amount->installEventFilter(this); amount->setMaximumWidth(100); - decimals = new QLineEdit(this); + decimals = new QValidatedLineEdit(this); decimals->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this)); decimals->setMaxLength(8); decimals->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); @@ -29,8 +30,9 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): layout->addStretch(1); layout->setContentsMargins(0,0,0,0); - setFocusPolicy(Qt::TabFocus); setLayout(layout); + + setFocusPolicy(Qt::TabFocus); setFocusProxy(amount); // If one if the widgets changes, the combined content changes as well @@ -53,10 +55,28 @@ void BitcoinAmountField::setText(const QString &text) } } +bool BitcoinAmountField::validate() +{ + bool valid = true; + if(amount->text().isEmpty()) + { + amount->setValid(false); + valid = false; + } + if(decimals->text().isEmpty()) + { + decimals->setValid(false); + valid = false; + } + return valid; +} + QString BitcoinAmountField::text() const { if(amount->text().isEmpty() || decimals->text().isEmpty()) + { return QString(); + } return amount->text() + QString(".") + decimals->text(); } @@ -75,3 +95,10 @@ bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event) } return false; } + +QWidget *BitcoinAmountField::setupTabChain(QWidget *prev) +{ + QWidget::setTabOrder(prev, amount); + QWidget::setTabOrder(amount, decimals); + return decimals; +} diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 67304c8b3a..2a0ef4bd99 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -4,7 +4,7 @@ #include QT_BEGIN_NAMESPACE -class QLineEdit; +class QValidatedLineEdit; QT_END_NAMESPACE // Coin amount entry widget with separate parts for whole @@ -18,6 +18,10 @@ public: void setText(const QString &text); QString text() const; + bool validate(); + // Qt messes up the tab chain by default in some cases (issue http://bugreports.qt.nokia.com/browse/QTBUG-10907) + // Hence we have to set it up manually + QWidget *setupTabChain(QWidget *prev); signals: void textChanged(); @@ -27,8 +31,8 @@ protected: bool eventFilter(QObject *object, QEvent *event); private: - QLineEdit *amount; - QLineEdit *decimals; + QValidatedLineEdit *amount; + QValidatedLineEdit *decimals; }; diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 7ea5638b4b..a0b27e83bb 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -79,23 +79,22 @@ QString EditAddressDialog::saveCurrentRow() void EditAddressDialog::accept() { - if(mode == NewSendingAddress || mode == EditSendingAddress) + if(saveCurrentRow().isEmpty()) { - // For sending addresses, check validity - // Not needed for receiving addresses, as those are generated - if(!model->validateAddress(ui->addressEdit->text())) + switch(model->getEditStatus()) { + case AddressTableModel::DUPLICATE_ADDRESS: + QMessageBox::warning(this, windowTitle(), + tr("The entered address \"%1\" is already in the address book.").arg(ui->addressEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); + break; + case AddressTableModel::INVALID_ADDRESS: QMessageBox::warning(this, windowTitle(), tr("The entered address \"%1\" is not a valid bitcoin address.").arg(ui->addressEdit->text()), QMessageBox::Ok, QMessageBox::Ok); return; } - } - if(saveCurrentRow().isEmpty()) - { - QMessageBox::warning(this, windowTitle(), - tr("The entered address \"%1\" is already in the address book.").arg(ui->addressEdit->text()), - QMessageBox::Ok, QMessageBox::Ok); + return; } QDialog::accept(); diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 8009bd2b94..57b79279af 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -15,145 +15,10 @@ - - - Qt::Vertical - - - QSizePolicy::Preferred - - - - 20 - 12 - - - - - - + - 12 + 6 - - - - A&mount: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - payAmount - - - - - - - Pay &To: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - payTo - - - - - - - - - - 0 - - - - - true - - - Enter a label for this address to add it to your address book - - - - - - - - - &Label: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - addAsLabel - - - - - - - 0 - - - - - The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - - - 34 - - - - - - - Look up adress in address book - - - - - - - :/icons/address-book:/icons/address-book - - - Alt+A - - - false - - - false - - - - - - - Paste address from system clipboard - - - - - - - :/icons/editpaste:/icons/editpaste - - - Alt+P - - - false - - - - - @@ -174,6 +39,17 @@ + + + + &Add recipient... + + + + :/icons/add:/icons/add + + + @@ -214,22 +90,6 @@ - - - BitcoinAmountField - QLineEdit -
bitcoinamountfield.h
- 1 -
-
- - payTo - addressBookButton - pasteButton - addAsLabel - payAmount - sendButton - diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui new file mode 100644 index 0000000000..1159ef5389 --- /dev/null +++ b/src/qt/forms/sendcoinsentry.ui @@ -0,0 +1,175 @@ + + + SendCoinsEntry + + + + 0 + 0 + 729 + 136 + + + + Form + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + 12 + + + + + A&mount: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + payAmount + + + + + + + Pay &To: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + payTo + + + + + + + + + + 0 + + + + + true + + + Enter a label for this address to add it to your address book + + + + + + + + + &Label: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + addAsLabel + + + + + + + 0 + + + + + The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + + + 34 + + + + + + + Look up adress in address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + false + + + false + + + + + + + Paste address from system clipboard + + + + + + + :/icons/editpaste:/icons/editpaste + + + Alt+P + + + false + + + + + + + + + + + :/icons/res/icons/remove.png:/icons/res/icons/remove.png + + + + + + + + + + BitcoinAmountField + QLineEdit +
bitcoinamountfield.h
+ 1 +
+ + QValidatedLineEdit + QLineEdit +
qvalidatedlineedit.h
+
+
+ + + + +
diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp new file mode 100644 index 0000000000..4b5acd8b07 --- /dev/null +++ b/src/qt/qvalidatedlineedit.cpp @@ -0,0 +1,37 @@ +#include "qvalidatedlineedit.h" + +QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) : + QLineEdit(parent), valid(true) +{ + connect(this, SIGNAL(textChanged(QString)), this, SLOT(markValid())); +} + +void QValidatedLineEdit::setValid(bool valid) +{ + if(valid == this->valid) + { + return; + } + + if(valid) + { + setStyleSheet(""); + } + else + { + setStyleSheet("background:#FF8080"); + } + this->valid = valid; +} + +void QValidatedLineEdit::focusInEvent(QFocusEvent *evt) +{ + // Clear invalid flag on focus + setValid(true); + QLineEdit::focusInEvent(evt); +} + +void QValidatedLineEdit::markValid() +{ + setValid(true); +} diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h new file mode 100644 index 0000000000..9fc026fab1 --- /dev/null +++ b/src/qt/qvalidatedlineedit.h @@ -0,0 +1,27 @@ +#ifndef QVALIDATEDLINEEDIT_H +#define QVALIDATEDLINEEDIT_H + +#include + +// Line edit that can be marked as "invalid". When marked as invalid, +// it will get a red background until it is focused. +class QValidatedLineEdit : public QLineEdit +{ + Q_OBJECT +public: + explicit QValidatedLineEdit(QWidget *parent = 0); + +protected: + void focusInEvent(QFocusEvent *evt); + +private: + bool valid; + +public slots: + void setValid(bool valid); + +private slots: + void markValid(); +}; + +#endif // QVALIDATEDLINEEDIT_H diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 01d68dc0af..38a0a65520 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1,43 +1,40 @@ #include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" #include "walletmodel.h" -#include "addresstablemodel.h" #include "guiutil.h" - #include "addressbookpage.h" #include "optionsmodel.h" +#include "sendcoinsentry.h" + -#include -#include #include #include #include -#include -SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : +SendCoinsDialog::SendCoinsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::SendCoinsDialog), model(0) { ui->setupUi(this); -#if QT_VERSION >= 0x040700 - ui->payTo->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); - ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); -#endif - GUIUtil::setupAddressWidget(ui->payTo, this); + addEntry(); - // Set initial send-to address if provided - if(!address.isEmpty()) - { - ui->payTo->setText(address); - ui->payAmount->setFocus(); - } + connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry())); } void SendCoinsDialog::setModel(WalletModel *model) { this->model = model; + + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + { + entry->setModel(model); + } + } } SendCoinsDialog::~SendCoinsDialog() @@ -47,26 +44,38 @@ SendCoinsDialog::~SendCoinsDialog() void SendCoinsDialog::on_sendButton_clicked() { - bool valid; - QString payAmount = ui->payAmount->text(); - QString label; - qint64 payAmountParsed; - - valid = GUIUtil::parseMoney(payAmount, &payAmountParsed); + QList recipients; + bool valid = true; + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + { + if(entry->validate()) + { + recipients.append(entry->getValue()); + } + else + { + valid = false; + } + } + } - if(!valid || payAmount.isEmpty()) + if(!valid || recipients.isEmpty()) { - QMessageBox::warning(this, tr("Send Coins"), - tr("Must fill in an amount to pay."), - QMessageBox::Ok, QMessageBox::Ok); return; } - // Add address to address book under label, if specified - label = ui->addAsLabel->text(); + // Format confirmation message + QStringList formatted; + foreach(const SendCoinsRecipient &rcp, recipients) + { + formatted.append(tr("%1 BTC to %2 (%3)").arg(GUIUtil::formatMoney(rcp.amount), rcp.label, rcp.address)); + } QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), - tr("Are you sure you want to send %1 BTC to %2 (%3)?").arg(GUIUtil::formatMoney(payAmountParsed), label, ui->payTo->text()), + tr("Are you sure you want to send %1?").arg(formatted.join(tr(" and "))), QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Cancel); @@ -75,32 +84,45 @@ void SendCoinsDialog::on_sendButton_clicked() return; } - switch(model->sendCoins(ui->payTo->text(), payAmountParsed, label)) + WalletModel::SendCoinsReturn sendstatus = model->sendCoins(recipients); + switch(sendstatus.status) { case WalletModel::InvalidAddress: QMessageBox::warning(this, tr("Send Coins"), tr("The recepient address is not valid, please recheck."), QMessageBox::Ok, QMessageBox::Ok); - ui->payTo->setFocus(); break; case WalletModel::InvalidAmount: QMessageBox::warning(this, tr("Send Coins"), tr("The amount to pay must be larger than 0."), QMessageBox::Ok, QMessageBox::Ok); - ui->payAmount->setFocus(); break; case WalletModel::AmountExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), tr("Amount exceeds your balance"), QMessageBox::Ok, QMessageBox::Ok); - ui->payAmount->setFocus(); break; case WalletModel::AmountWithFeeExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), tr("Total exceeds your balance when the %1 transaction fee is included"). - arg(GUIUtil::formatMoney(model->getOptionsModel()->getTransactionFee())), + arg(GUIUtil::formatMoney(sendstatus.fee)), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::DuplicateAddress: + QMessageBox::warning(this, tr("Send Coins"), + tr("Duplicate address found, can only send to each address once in one send operation"), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::TransactionCreationFailed: + QMessageBox::warning(this, tr("Send Coins"), + tr("Error: Transaction creation failed "), + QMessageBox::Ok, QMessageBox::Ok); + break; + break; + case WalletModel::TransactionCommitFailed: + QMessageBox::warning(this, tr("Send Coins"), + tr("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."), QMessageBox::Ok, QMessageBox::Ok); - ui->payAmount->setFocus(); break; case WalletModel::OK: accept(); @@ -108,34 +130,23 @@ void SendCoinsDialog::on_sendButton_clicked() } } -void SendCoinsDialog::on_pasteButton_clicked() +void SendCoinsDialog::clear() { - // Paste text from clipboard into recipient field - ui->payTo->setText(QApplication::clipboard()->text()); -} + // Remove entries until only one left + while(ui->entries->count() > 1) + { + delete ui->entries->takeAt(0)->widget(); + } -void SendCoinsDialog::on_addressBookButton_clicked() -{ - AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); - dlg.setModel(model->getAddressTableModel()); - if(dlg.exec()) + // Reset the entry that is left to empty + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(0)->widget()); + if(entry) { - ui->payTo->setText(dlg.getReturnValue()); - ui->payAmount->setFocus(); + entry->clear(); } -} -void SendCoinsDialog::on_payTo_textChanged(const QString &address) -{ - ui->addAsLabel->setText(model->getAddressTableModel()->labelForAddress(address)); -} + updateRemoveEnabled(); -void SendCoinsDialog::clear() -{ - ui->payTo->setText(QString()); - ui->addAsLabel->setText(QString()); - ui->payAmount->setText(QString()); - ui->payTo->setFocus(); ui->sendButton->setDefault(true); } @@ -148,3 +159,52 @@ void SendCoinsDialog::accept() { clear(); } + +void SendCoinsDialog::addEntry() +{ + SendCoinsEntry *entry = new SendCoinsEntry(this); + entry->setModel(model); + ui->entries->addWidget(entry); + connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*))); + + updateRemoveEnabled(); + + // Focus the field, so that entry can start immediately + entry->clear(); +} + +void SendCoinsDialog::updateRemoveEnabled() +{ + // Remove buttons are enabled as soon as there is more than one send-entry + bool enabled = (ui->entries->count() > 1); + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + { + entry->setRemoveEnabled(enabled); + } + } + setupTabChain(0); +} + +void SendCoinsDialog::removeEntry(SendCoinsEntry* entry) +{ + delete entry; + updateRemoveEnabled(); +} + +QWidget *SendCoinsDialog::setupTabChain(QWidget *prev) +{ + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + { + prev = entry->setupTabChain(prev); + } + } + QWidget::setTabOrder(prev, ui->addButton); + QWidget::setTabOrder(ui->addButton, ui->sendButton); + return ui->sendButton; +} diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 46814af466..0f90be8165 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -7,31 +7,37 @@ namespace Ui { class SendCoinsDialog; } class WalletModel; +class SendCoinsEntry; class SendCoinsDialog : public QDialog { Q_OBJECT public: - explicit SendCoinsDialog(QWidget *parent = 0, const QString &address = ""); + explicit SendCoinsDialog(QWidget *parent = 0); ~SendCoinsDialog(); void setModel(WalletModel *model); + // Qt messes up the tab chain by default in some cases (issue http://bugreports.qt.nokia.com/browse/QTBUG-10907) + // Hence we have to set it up manually + QWidget *setupTabChain(QWidget *prev); + public slots: void clear(); void reject(); void accept(); + void addEntry(); + void updateRemoveEnabled(); private: Ui::SendCoinsDialog *ui; WalletModel *model; private slots: - void on_payTo_textChanged(const QString &address); - void on_addressBookButton_clicked(); - void on_pasteButton_clicked(); void on_sendButton_clicked(); + + void removeEntry(SendCoinsEntry* entry); }; #endif // SENDCOINSDIALOG_H diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp new file mode 100644 index 0000000000..6e87e9cf99 --- /dev/null +++ b/src/qt/sendcoinsentry.cpp @@ -0,0 +1,119 @@ +#include "sendcoinsentry.h" +#include "ui_sendcoinsentry.h" +#include "guiutil.h" +#include "addressbookpage.h" +#include "walletmodel.h" +#include "addresstablemodel.h" + +#include "qapplication.h" +#include "qclipboard.h" + +#include + +SendCoinsEntry::SendCoinsEntry(QWidget *parent) : + QFrame(parent), + ui(new Ui::SendCoinsEntry), + model(0) +{ + ui->setupUi(this); + +#if QT_VERSION >= 0x040700 + ui->payTo->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); + ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); +#endif + setFocusPolicy(Qt::TabFocus); + setFocusProxy(ui->payTo); + + GUIUtil::setupAddressWidget(ui->payTo, this); +} + +SendCoinsEntry::~SendCoinsEntry() +{ + delete ui; +} + +void SendCoinsEntry::on_pasteButton_clicked() +{ + // Paste text from clipboard into recipient field + ui->payTo->setText(QApplication::clipboard()->text()); +} + +void SendCoinsEntry::on_addressBookButton_clicked() +{ + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); + dlg.setModel(model->getAddressTableModel()); + if(dlg.exec()) + { + ui->payTo->setText(dlg.getReturnValue()); + ui->payAmount->setFocus(); + } +} + +void SendCoinsEntry::on_payTo_textChanged(const QString &address) +{ + ui->addAsLabel->setText(model->getAddressTableModel()->labelForAddress(address)); +} + +void SendCoinsEntry::setModel(WalletModel *model) +{ + this->model = model; +} + +void SendCoinsEntry::setRemoveEnabled(bool enabled) +{ + ui->deleteButton->setEnabled(enabled); +} + +void SendCoinsEntry::clear() +{ + ui->payTo->clear(); + ui->addAsLabel->clear(); + ui->payAmount->setText(QString()); + ui->payTo->setFocus(); +} + +void SendCoinsEntry::on_deleteButton_clicked() +{ + emit removeEntry(this); +} + +bool SendCoinsEntry::validate() +{ + // Check input validity + bool retval = true; + + if(!ui->payAmount->validate()) + { + retval = false; + } + + if(!ui->payTo->hasAcceptableInput() || + (model && !model->validateAddress(ui->payTo->text()))) + { + ui->payTo->setValid(false); + retval = false; + } + + return retval; +} + +SendCoinsRecipient SendCoinsEntry::getValue() +{ + SendCoinsRecipient rv; + + rv.address = ui->payTo->text(); + rv.label = ui->addAsLabel->text(); + GUIUtil::parseMoney(ui->payAmount->text(), &rv.amount); + + return rv; +} + +QWidget *SendCoinsEntry::setupTabChain(QWidget *prev) +{ + QWidget::setTabOrder(prev, ui->payTo); + QWidget::setTabOrder(ui->payTo, ui->addressBookButton); + QWidget::setTabOrder(ui->addressBookButton, ui->pasteButton); + QWidget::setTabOrder(ui->pasteButton, ui->deleteButton); + QWidget::setTabOrder(ui->deleteButton, ui->addAsLabel); + return ui->payAmount->setupTabChain(ui->addAsLabel); +} diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h new file mode 100644 index 0000000000..55fd12a14b --- /dev/null +++ b/src/qt/sendcoinsentry.h @@ -0,0 +1,45 @@ +#ifndef SENDCOINSENTRY_H +#define SENDCOINSENTRY_H + +#include + +namespace Ui { + class SendCoinsEntry; +} +class WalletModel; +class SendCoinsRecipient; + +class SendCoinsEntry : public QFrame +{ + Q_OBJECT + +public: + explicit SendCoinsEntry(QWidget *parent = 0); + ~SendCoinsEntry(); + + void setModel(WalletModel *model); + bool validate(); + SendCoinsRecipient getValue(); + // Qt messes up the tab chain by default in some cases (issue http://bugreports.qt.nokia.com/browse/QTBUG-10907) + // Hence we have to set it up manually + QWidget *setupTabChain(QWidget *prev); + +public slots: + void setRemoveEnabled(bool enabled); + void clear(); + +signals: + void removeEntry(SendCoinsEntry *entry); + +private slots: + void on_deleteButton_clicked(); + void on_payTo_textChanged(const QString &address); + void on_addressBookButton_clicked(); + void on_pasteButton_clicked(); + +private: + Ui::SendCoinsEntry *ui; + WalletModel *model; +}; + +#endif // SENDCOINSENTRY_H diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index afe095c977..6e4b814d12 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -7,6 +7,7 @@ #include "headers.h" #include +#include WalletModel::WalletModel(CWallet *wallet, QObject *parent) : QObject(parent), wallet(wallet), optionsModel(0), addressTableModel(0), @@ -54,63 +55,105 @@ void WalletModel::update() addressTableModel->update(); } -WalletModel::StatusCode WalletModel::sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs) +bool WalletModel::validateAddress(const QString &address) { uint160 hash160 = 0; - bool valid = false; - if(!AddressToHash160(payTo.toUtf8().constData(), hash160)) + return AddressToHash160(address.toStdString(), hash160); +} + +WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList &recipients) +{ + qint64 total = 0; + QSet setAddress; + QString hex; + + if(recipients.empty()) { - return InvalidAddress; + return OK; } - if(payAmount <= 0) + // Pre-check input data for validity + foreach(const SendCoinsRecipient &rcp, recipients) { - return InvalidAmount; + uint160 hash160 = 0; + + if(!AddressToHash160(rcp.address.toUtf8().constData(), hash160)) + { + return InvalidAddress; + } + setAddress.insert(rcp.address); + + if(rcp.amount <= 0) + { + return InvalidAmount; + } + total += rcp.amount; } - if(payAmount > getBalance()) + if(recipients.size() > setAddress.size()) + { + return DuplicateAddress; + } + + if(total > getBalance()) { return AmountExceedsBalance; } - if((payAmount + nTransactionFee) > getBalance()) + if((total + nTransactionFee) > getBalance()) { - return AmountWithFeeExceedsBalance; + return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee); } CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(wallet->cs_mapWallet) { - // Send to bitcoin address + // Sendmany + std::vector > vecSend; + foreach(const SendCoinsRecipient &rcp, recipients) + { + CScript scriptPubKey; + scriptPubKey.SetBitcoinAddress(rcp.address.toStdString()); + vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); + } + CWalletTx wtx; - CScript scriptPubKey; - scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; + CReserveKey keyChange(wallet); + int64 nFeeRequired = 0; + bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); - std::string strError = wallet->SendMoney(scriptPubKey, payAmount, wtx, true); - if (strError == "") + if(!fCreated) { - // OK + if((total + nFeeRequired) > wallet->GetBalance()) + { + return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); + } + return TransactionCreationFailed; } - else if (strError == "ABORTED") + if(!ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString(), NULL)) { return Aborted; } - else + if(!wallet->CommitTransaction(wtx, keyChange)) { - emit error(tr("Sending..."), QString::fromStdString(strError)); - return MiscError; + return TransactionCommitFailed; } + hex = QString::fromStdString(wtx.GetHash().GetHex()); } // Add addresses that we've sent to to the address book - std::string strAddress = payTo.toStdString(); - CRITICAL_BLOCK(wallet->cs_mapAddressBook) + foreach(const SendCoinsRecipient &rcp, recipients) { - if (!wallet->mapAddressBook.count(strAddress)) - wallet->SetAddressBookName(strAddress, addToAddressBookAs.toStdString()); + std::string strAddress = rcp.address.toStdString(); + CRITICAL_BLOCK(wallet->cs_mapAddressBook) + { + if (!wallet->mapAddressBook.count(strAddress)) + wallet->SetAddressBookName(strAddress, rcp.label.toStdString()); + } } - return OK; + return SendCoinsReturn(OK, 0, hex); } OptionsModel *WalletModel::getOptionsModel() diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 1105fb03fa..af2cac4b31 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -8,6 +8,13 @@ class AddressTableModel; class TransactionTableModel; class CWallet; +struct SendCoinsRecipient +{ + QString address; + QString label; + qint64 amount; +}; + // Interface to a Bitcoin wallet class WalletModel : public QObject { @@ -22,6 +29,9 @@ public: InvalidAddress, AmountExceedsBalance, AmountWithFeeExceedsBalance, + DuplicateAddress, + TransactionCreationFailed, + TransactionCommitFailed, Aborted, MiscError }; @@ -34,8 +44,25 @@ public: qint64 getUnconfirmedBalance() const; int getNumTransactions() const; - /* Send coins */ - StatusCode sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs=QString()); + // Check address for validity + bool validateAddress(const QString &address); + + // Return status record for SendCoins + // fee is used in case status is "AmountWithFeeExceedsBalance" + // hex is filled with the transaction hash if status is "OK" + struct SendCoinsReturn + { + SendCoinsReturn(StatusCode status, + qint64 fee=0, + QString hex=QString()): + status(status), fee(fee), hex(hex) {} + StatusCode status; + qint64 fee; + QString hex; + }; + + // Send coins to list of recipients + SendCoinsReturn sendCoins(const QList &recipients); private: CWallet *wallet; -- cgit v1.2.3 From 9958e09dbc90c8a81a16be5de8c9bc62a00615d5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 16 Jul 2011 19:28:15 +0200 Subject: Revert "Now that send coins / receive coins etc are tabs, remove them from menu, and reorganize menu bar" This reverts commit ea37fb9187195d746a4b8a84da5cb6bd9e0e4aab. --- src/qt/bitcoingui.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 9ee178ac64..016f261941 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -55,10 +55,14 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Menus QMenu *file = menuBar()->addMenu("&File"); - file->addAction(optionsAction); + file->addAction(sendCoinsAction); + file->addAction(receiveCoinsAction); file->addSeparator(); file->addAction(quitAction); + QMenu *settings = menuBar()->addMenu("&Settings"); + settings->addAction(optionsAction); + QMenu *help = menuBar()->addMenu("&Help"); help->addAction(aboutAction); -- cgit v1.2.3 From 9b9cd3dd2011d78c1b85bc7ddae6e193e9e84a62 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 16 Jul 2011 19:35:41 +0200 Subject: add missing icon --- src/qt/res/icons/remove.png | Bin 0 -> 1224 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/qt/res/icons/remove.png diff --git a/src/qt/res/icons/remove.png b/src/qt/res/icons/remove.png new file mode 100644 index 0000000000..a44b6d130b Binary files /dev/null and b/src/qt/res/icons/remove.png differ -- cgit v1.2.3 From 2a429030ff289c8355323297e792d48b0537b5eb Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 16 Jul 2011 19:45:37 +0200 Subject: update readme --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 16d03c3ab8..dc9a8b15a8 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ Bitcoin-qt: Qt4 based GUI replacement for Bitcoin **Warning** **Warning** **Warning** -Pre-alpha stuff! I'm using this client myself on the production network, and I haven't noticed any glitches, but remember: always backup your wallet. +Alpha version! I'm using this client myself on the production network, and I haven't noticed any glitches, but remember: always backup your wallet. Testing on the testnet is recommended. This has been implemented: @@ -28,6 +28,8 @@ This has been implemented: - Progress bar on initial block download +- Sendmany support + This has to be done: - Start at system start -- cgit v1.2.3 From b5f918cbd69e02f1e955fe90a13444a15a7de43f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 16 Jul 2011 19:54:44 +0200 Subject: readme update --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index dc9a8b15a8..98add9f3c0 100644 --- a/README.rst +++ b/README.rst @@ -28,7 +28,7 @@ This has been implemented: - Progress bar on initial block download -- Sendmany support +- Sendmany support in UI (send to multiple recipients as well) This has to be done: -- cgit v1.2.3 From 5df0b03c950184b2e2fdbfc6e9f8075dcf81c75c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 17 Jul 2011 14:06:43 +0200 Subject: make initial block download reporting somewhat better by tracking version responses --- src/main.cpp | 6 +++++- src/qt/bitcoingui.cpp | 11 +++++++++-- src/qt/clientmodel.cpp | 18 ++++++++++++------ src/qt/clientmodel.h | 3 +++ src/qt/guiconstants.h | 2 +- src/qt/walletmodel.cpp | 21 +++++++++++++++------ src/qt/walletmodel.h | 4 ++++ 7 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index e3ad35044e..3a482e7cfa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,7 +32,7 @@ map mapNextTx; map mapBlockIndex; uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); -const int nTotalBlocksEstimate = 134444; // Conservative estimate of total nr of blocks on main chain +int nTotalBlocksEstimate = 134444; // Conservative estimate of total nr of blocks on main chain const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download" CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; @@ -1869,6 +1869,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight); + if(pfrom->nStartingHeight > nTotalBlocksEstimate) + { + nTotalBlocksEstimate = pfrom->nStartingHeight; + } } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 016f261941..6a6f3f32ec 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -292,17 +292,21 @@ void BitcoinGUI::setNumConnections(int count) void BitcoinGUI::setNumBlocks(int count) { int total = clientModel->getTotalBlocksEstimate(); + QString tooltip; + if(count < total) { progressBarLabel->setVisible(true); progressBar->setVisible(true); progressBar->setMaximum(total); progressBar->setValue(count); + tooltip = tr("Downloaded %1 of %2 blocks of transaction history.").arg(count).arg(total); } else { progressBarLabel->setVisible(false); progressBar->setVisible(false); + tooltip = tr("Downloaded %1 blocks of transaction history.").arg(count); } QDateTime now = QDateTime::currentDateTime(); @@ -329,10 +333,13 @@ void BitcoinGUI::setNumBlocks(int count) { text = tr("%n day(s) ago","",secs/(60*60*24)); } + tooltip += QString("\n"); + tooltip += tr("Last block was generated %1.").arg(QLocale::system().toString(lastBlockDate)); labelBlocks->setText(" " + text); - labelBlocks->setToolTip(tr("Downloaded %n block(s) of transaction history. Last block was generated %1.", "", count) - .arg(QLocale::system().toString(lastBlockDate))); + labelBlocks->setToolTip(tooltip); + progressBarLabel->setToolTip(tooltip); + progressBar->setToolTip(tooltip); } void BitcoinGUI::error(const QString &title, const QString &message) diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 8885b4cb5b..c147aa5a6e 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -10,7 +10,8 @@ #include ClientModel::ClientModel(CWallet *wallet, QObject *parent) : - QObject(parent), wallet(wallet), optionsModel(0) + QObject(parent), wallet(wallet), optionsModel(0), + cachedNumConnections(0), cachedNumBlocks(0) { // Until signal notifications is built into the bitcoin core, // simply update everything after polling using a timer. @@ -38,11 +39,16 @@ QDateTime ClientModel::getLastBlockDate() const void ClientModel::update() { - // Plainly emit all signals for now. To be more efficient this should check - // whether the values actually changed first, although it'd be even better if these - // were events coming in from the bitcoin core. - emit numConnectionsChanged(getNumConnections()); - emit numBlocksChanged(getNumBlocks()); + int newNumConnections = getNumConnections(); + int newNumBlocks = getNumBlocks(); + + if(cachedNumConnections != newNumConnections) + emit numConnectionsChanged(newNumConnections); + if(cachedNumBlocks != newNumBlocks) + emit numBlocksChanged(newNumBlocks); + + cachedNumConnections = newNumConnections; + cachedNumBlocks = newNumBlocks; } bool ClientModel::isTestNet() const diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 6c2c275cef..f7ad14c28b 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -42,6 +42,9 @@ private: OptionsModel *optionsModel; + int cachedNumConnections; + int cachedNumBlocks; + signals: void numConnectionsChanged(int count); void numBlocksChanged(int count); diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index cdd1a74d98..59f49625bd 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -2,7 +2,7 @@ #define GUICONSTANTS_H /* milliseconds between model updates */ -static const int MODEL_UPDATE_DELAY = 250; +static const int MODEL_UPDATE_DELAY = 500; /* size of cache */ static const unsigned int WALLET_CACHE_SIZE = 100; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 6e4b814d12..4ff2e0ab15 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -11,7 +11,8 @@ WalletModel::WalletModel(CWallet *wallet, QObject *parent) : QObject(parent), wallet(wallet), optionsModel(0), addressTableModel(0), - transactionTableModel(0) + transactionTableModel(0), + cachedBalance(0), cachedUnconfirmedBalance(0), cachedNumTransactions(0) { // Until signal notifications is built into the bitcoin core, // simply update everything after polling using a timer. @@ -46,11 +47,19 @@ int WalletModel::getNumTransactions() const void WalletModel::update() { - // Plainly emit all signals for now. To be more efficient this should check - // whether the values actually changed first, although it'd be even better if these - // were events coming in from the bitcoin core. - emit balanceChanged(getBalance(), wallet->GetUnconfirmedBalance()); - emit numTransactionsChanged(getNumTransactions()); + qint64 newBalance = getBalance(); + qint64 newUnconfirmedBalance = getUnconfirmedBalance(); + int newNumTransactions = getNumTransactions(); + + if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance) + emit balanceChanged(newBalance, newUnconfirmedBalance); + + if(cachedNumTransactions != newNumTransactions) + emit numTransactionsChanged(newNumTransactions); + + cachedBalance = newBalance; + cachedUnconfirmedBalance = newUnconfirmedBalance; + cachedNumTransactions = newNumTransactions; addressTableModel->update(); } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index af2cac4b31..668d44632f 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -73,6 +73,10 @@ private: AddressTableModel *addressTableModel; TransactionTableModel *transactionTableModel; + qint64 cachedBalance; + qint64 cachedUnconfirmedBalance; + qint64 cachedNumTransactions; + signals: void balanceChanged(qint64 balance, qint64 unconfirmedBalance); void numTransactionsChanged(int count); -- cgit v1.2.3 From 8dcffd4d0717e71226da8c3a848b7b6905074637 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 17 Jul 2011 17:30:58 +0200 Subject: show rotating spinner when block download out of date, tick otherwise --- doc/assets-attribution.txt | 13 ++++--- scripts/img/reload.xcf | Bin 0 -> 28597 bytes scripts/img/reload_scaled.png | Bin 0 -> 905 bytes scripts/make_spinner.py | 41 ++++++++++++++++++++++ src/qt/bitcoin.qrc | 6 ++-- src/qt/bitcoingui.cpp | 64 ++++++++++++++++++++++++++--------- src/qt/bitcoingui.h | 3 ++ src/qt/res/icons/synced.png | Bin 1127 -> 698 bytes src/qt/res/movies/update_spinner.mng | Bin 0 -> 27707 bytes 9 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 scripts/img/reload.xcf create mode 100644 scripts/img/reload_scaled.png create mode 100755 scripts/make_spinner.py create mode 100644 src/qt/res/movies/update_spinner.mng diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index f3900fe0fa..2ab8f56e40 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -14,7 +14,7 @@ Designer: FatCow Web Hosting License: Creative Commons Attribution (by) Site: http://findicons.com/icon/163938/book_open -Icon: src/qt/res/icons/connect*.png +Icon: src/qt/res/icons/connect*.png, src/qt/res/icons/synced.png Icon Pack: Human-O2 Designer: schollidesign License: GNU/GPL @@ -52,9 +52,8 @@ Designer: Jack Cai License: Creative Commons Attribution No Derivatives (by-nd) Site: http://findicons.com/icon/175944/home?id=176221# -Icon: src/qt/res/icons/synced.png, - src/qt/res/icons/notsynced.png -Icon Pack: Gloss: Basic -Designer: Momenticons -License: Creative Commons Attribution (by) -Site: http://www.momenticons.com/ +Icon: scripts/img/reload.xcf (modified),src/qt/res/movies/update_spinner.mng +Icon Pack: Kids +Designer: Everaldo (Everaldo Coelho) +License: GNU/GPL +Site: http://findicons.com/icon/17102/reload?id=17102 diff --git a/scripts/img/reload.xcf b/scripts/img/reload.xcf new file mode 100644 index 0000000000..c3ce165adb Binary files /dev/null and b/scripts/img/reload.xcf differ diff --git a/scripts/img/reload_scaled.png b/scripts/img/reload_scaled.png new file mode 100644 index 0000000000..9a45b1bd1d Binary files /dev/null and b/scripts/img/reload_scaled.png differ diff --git a/scripts/make_spinner.py b/scripts/make_spinner.py new file mode 100755 index 0000000000..c1f94c12c2 --- /dev/null +++ b/scripts/make_spinner.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# W.J. van der Laan, 2011 +# Make spinning .mng animation from a .png +# Requires imagemagick 6.7+ +from __future__ import division +from os import path +from PIL import Image +from subprocess import Popen + +SRC='img/reload_scaled.png' +DST='../src/qt/res/movies/update_spinner.mng' +TMPDIR='/tmp' +TMPNAME='tmp-%03i.png' +NUMFRAMES=35 +FRAMERATE=10.0 +CONVERT='convert' +CLOCKWISE=True + +im_src = Image.open(SRC) + +if CLOCKWISE: + im_src = im_src.transpose(Image.FLIP_LEFT_RIGHT) + +def frame_to_filename(frame): + return path.join(TMPDIR, TMPNAME % frame) + +frame_files = [] +for frame in xrange(NUMFRAMES): + rotation = (frame + 0.5) / NUMFRAMES * 360.0 + if CLOCKWISE: + rotation = -rotation + im_new = im_src.rotate(rotation, Image.BICUBIC) + outfile = frame_to_filename(frame) + im_new.save(outfile, 'png') + frame_files.append(outfile) + +p = Popen([CONVERT, "-delay", str(FRAMERATE), "-dispose", "2"] + frame_files + [DST]) +p.communicate() + + + diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 9ef8b2afd5..f6b22f45f7 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -30,10 +30,12 @@ res/icons/overview.png res/icons/export.png res/icons/synced.png - res/icons/notsynced.png - res/icons/remove.png + res/icons/remove.png
res/images/about.png + + res/movies/update_spinner.mng + diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6a6f3f32ec..ed687c4532 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -107,17 +108,35 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Create status bar statusBar(); + // Status bar "Connections" notification + QFrame *frameConnections = new QFrame(); + frameConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); + frameConnections->setMinimumWidth(150); + frameConnections->setMaximumWidth(150); + QHBoxLayout *frameConnectionsLayout = new QHBoxLayout(frameConnections); + frameConnectionsLayout->setContentsMargins(3,0,3,0); + frameConnectionsLayout->setSpacing(3); + labelConnectionsIcon = new QLabel(); + labelConnectionsIcon->setToolTip(tr("Number of connections to other clients")); + frameConnectionsLayout->addWidget(labelConnectionsIcon); labelConnections = new QLabel(); - labelConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelConnections->setMinimumWidth(150); - labelConnections->setMaximumWidth(150); labelConnections->setToolTip(tr("Number of connections to other clients")); - + frameConnectionsLayout->addWidget(labelConnections); + frameConnectionsLayout->addStretch(); + + // Status bar "Blocks" notification + QFrame *frameBlocks = new QFrame(); + frameBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); + frameBlocks->setMinimumWidth(150); + frameBlocks->setMaximumWidth(150); + QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); + frameBlocksLayout->setContentsMargins(3,0,3,0); + frameBlocksLayout->setSpacing(3); + labelBlocksIcon = new QLabel(); + frameBlocksLayout->addWidget(labelBlocksIcon); labelBlocks = new QLabel(); - labelBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelBlocks->setMinimumWidth(150); - labelBlocks->setMaximumWidth(150); - labelBlocks->setToolTip(tr("Number of blocks in the block chain")); + frameBlocksLayout->addWidget(labelBlocks); + frameBlocksLayout->addStretch(); // Progress bar for blocks download progressBarLabel = new QLabel(tr("Synchronizing with network...")); @@ -128,11 +147,13 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): statusBar()->addWidget(progressBarLabel); statusBar()->addWidget(progressBar); - statusBar()->addPermanentWidget(labelConnections); - statusBar()->addPermanentWidget(labelBlocks); + statusBar()->addPermanentWidget(frameConnections); + statusBar()->addPermanentWidget(frameBlocks); createTrayIcon(); + syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this); + gotoOverviewPage(); } @@ -285,8 +306,8 @@ void BitcoinGUI::setNumConnections(int count) case 7: case 8: case 9: icon = ":/icons/connect_3"; break; default: icon = ":/icons/connect_4"; break; } - labelConnections->setTextFormat(Qt::RichText); - labelConnections->setText(" " + tr("%n connection(s)", "", count)); + labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(16,16)); + labelConnections->setText(tr("%n connection(s)", "", count)); } void BitcoinGUI::setNumBlocks(int count) @@ -313,13 +334,13 @@ void BitcoinGUI::setNumBlocks(int count) QDateTime lastBlockDate = clientModel->getLastBlockDate(); int secs = lastBlockDate.secsTo(now); QString text; - QString icon = ":/icons/notsynced"; + bool spinning = true; // "Up to date" icon, and outdated icon if(secs < 30*60) { text = "Up to date"; - icon = ":/icons/synced"; + spinning = false; } else if(secs < 60*60) { @@ -334,9 +355,20 @@ void BitcoinGUI::setNumBlocks(int count) text = tr("%n day(s) ago","",secs/(60*60*24)); } tooltip += QString("\n"); - tooltip += tr("Last block was generated %1.").arg(QLocale::system().toString(lastBlockDate)); + tooltip += tr("Last received block was generated %1.").arg(text); + + if(spinning) + { + labelBlocksIcon->setMovie(syncIconMovie); + syncIconMovie->start(); + } + else + { + labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(16,16)); + } + labelBlocks->setText(text); - labelBlocks->setText(" " + text); + labelBlocksIcon->setToolTip(tooltip); labelBlocks->setToolTip(tooltip); progressBarLabel->setToolTip(tooltip); progressBar->setToolTip(tooltip); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 95e0eb70fe..4fc17dd36f 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -57,6 +57,7 @@ private: QLabel *labelConnections; QLabel *labelConnectionsIcon; QLabel *labelBlocks; + QLabel *labelBlocksIcon; QLabel *progressBarLabel; QProgressBar *progressBar; @@ -74,6 +75,8 @@ private: QSystemTrayIcon *trayIcon; TransactionView *transactionView; + QMovie *syncIconMovie; + void createActions(); QWidget *createTabs(); void createTrayIcon(); diff --git a/src/qt/res/icons/synced.png b/src/qt/res/icons/synced.png index 910fc396ed..8e428b6a70 100644 Binary files a/src/qt/res/icons/synced.png and b/src/qt/res/icons/synced.png differ diff --git a/src/qt/res/movies/update_spinner.mng b/src/qt/res/movies/update_spinner.mng new file mode 100644 index 0000000000..99b1b14135 Binary files /dev/null and b/src/qt/res/movies/update_spinner.mng differ -- cgit v1.2.3 From 1907b96d69c220d153825c7a80365593b3cf821e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 17 Jul 2011 17:42:41 +0200 Subject: put sendcoins entries in scroll area, so that window does not become bigger than screen with many recipients --- src/qt/forms/sendcoinsdialog.ui | 162 +++++++++++++++++++++++----------------- src/qt/forms/sendcoinsentry.ui | 2 +- 2 files changed, 93 insertions(+), 71 deletions(-) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 57b79279af..8907ab3d29 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -15,78 +15,100 @@ - - - 6 + + + true - - - - - - 12 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - &Add recipient... - - - - :/icons/add:/icons/add - - - - - - - - 150 - 0 - - - - Confirm the send action + + + + 0 + 0 + 666 + 197 + + + + + 0 - - &Send - - - - :/icons/send:/icons/send - - - true - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - + + + + 6 + + + + + + + 12 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Add recipient... + + + + :/icons/add:/icons/add + + + + + + + + 150 + 0 + + + + Confirm the send action + + + &Send + + + + :/icons/send:/icons/send + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 1159ef5389..2dca6b74b1 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -147,7 +147,7 @@ - :/icons/res/icons/remove.png:/icons/res/icons/remove.png + :/icons/remove:/icons/remove -- cgit v1.2.3 From a75e1e32923365dada7bee8cc0c2468d759355eb Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 18 Jul 2011 06:55:05 +0200 Subject: Fix "Last received block was generated Up to date" --- src/qt/bitcoingui.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index ed687c4532..c4462dd40c 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -334,13 +334,11 @@ void BitcoinGUI::setNumBlocks(int count) QDateTime lastBlockDate = clientModel->getLastBlockDate(); int secs = lastBlockDate.secsTo(now); QString text; - bool spinning = true; - // "Up to date" icon, and outdated icon - if(secs < 30*60) + // Represent time from last generated block in human readable text + if(secs < 60) { - text = "Up to date"; - spinning = false; + text = tr("%n second(s) ago","",secs); } else if(secs < 60*60) { @@ -354,6 +352,16 @@ void BitcoinGUI::setNumBlocks(int count) { text = tr("%n day(s) ago","",secs/(60*60*24)); } + + // In the label we want to be less specific + QString labelText = text; + bool spinning = true; + if(secs < 30*60) + { + labelText = "Up to date"; + spinning = false; + } + tooltip += QString("\n"); tooltip += tr("Last received block was generated %1.").arg(text); @@ -366,7 +374,7 @@ void BitcoinGUI::setNumBlocks(int count) { labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(16,16)); } - labelBlocks->setText(text); + labelBlocks->setText(labelText); labelBlocksIcon->setToolTip(tooltip); labelBlocks->setToolTip(tooltip); -- cgit v1.2.3 From 24c835b0b669dbd3c072d06e27c2e92b252e6af9 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 18 Jul 2011 18:34:53 +0200 Subject: windows build fix --- src/serialize.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/serialize.h b/src/serialize.h index cb3a3ea03c..857d0468d1 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -30,6 +30,7 @@ typedef unsigned long long uint64; #endif #ifdef __WXMSW__ +#include // This is used to attempt to keep keying material out of swap // Note that VirtualLock does not provide this as a guarantee on Windows, // but, in practice, memory that has been VirtualLock'd almost never gets written to -- cgit v1.2.3 From 68e327ae7b7232837c675bb3d51e17e823bc17d8 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 18 Jul 2011 18:38:56 +0200 Subject: move buttons to bottom of send coins tab, outside of scroll area --- src/qt/forms/sendcoinsdialog.ui | 110 ++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 8907ab3d29..547582e8df 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -25,7 +25,7 @@ 0 0 666 - 197 + 162 @@ -39,60 +39,6 @@ - - - - 12 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - &Add recipient... - - - - :/icons/add:/icons/add - - - - - - - - 150 - 0 - - - - Confirm the send action - - - &Send - - - - :/icons/send:/icons/send - - - true - - - - - @@ -110,6 +56,60 @@ + + + + 12 + + + + + &Add recipient... + + + + :/icons/add:/icons/add + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 150 + 0 + + + + Confirm the send action + + + &Send + + + + :/icons/send:/icons/send + + + true + + + + + -- cgit v1.2.3 From 174b3eddc05f26abbc97a83027a4da9ba4114d57 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 18 Jul 2011 21:02:17 +0200 Subject: one remove/delete icon is enough and the red minus better matches the add icon --- doc/assets-attribution.txt | 2 +- src/qt/bitcoin.qrc | 1 - src/qt/forms/addressbookpage.ui | 2 +- src/qt/res/icons/editdelete.png | Bin 1368 -> 0 bytes 4 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 src/qt/res/icons/editdelete.png diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index 2ab8f56e40..c03233790e 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -29,7 +29,7 @@ License: You are free to do with these icons as you wish, including selling, Icon: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png, src/qt/res/icons/editcopy.png, src/qt/res/icons/editpaste.png, src/qt/res/icons/add.png, src/qt/res/icons/edit.png, - src/qt/res/icons/editdelete.png, src/qt/res/icons/remove.png (edited) + src/qt/res/icons/remove.png (edited) Designer: http://www.everaldo.com Icon Pack: Crystal SVG License: LGPL diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index f6b22f45f7..5199a8ea31 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -25,7 +25,6 @@ res/icons/bitcoin_testnet.png res/icons/toolbar_testnet.png res/icons/edit.png - res/icons/editdelete.png res/icons/history.png res/icons/overview.png res/icons/export.png diff --git a/src/qt/forms/addressbookpage.ui b/src/qt/forms/addressbookpage.ui index d3feedb5e0..fb098c8280 100644 --- a/src/qt/forms/addressbookpage.ui +++ b/src/qt/forms/addressbookpage.ui @@ -89,7 +89,7 @@ - :/icons/editdelete:/icons/editdelete + :/icons/remove:/icons/remove diff --git a/src/qt/res/icons/editdelete.png b/src/qt/res/icons/editdelete.png deleted file mode 100644 index 945d221eea..0000000000 Binary files a/src/qt/res/icons/editdelete.png and /dev/null differ -- cgit v1.2.3 From 73cd5e5212be7ff05f7205c1e7519af1cf01e322 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 22 Jul 2011 17:06:37 +0200 Subject: fix clear() (clear red/invalid status) --- src/qt/bitcoinamountfield.cpp | 6 ++++++ src/qt/bitcoinamountfield.h | 2 ++ src/qt/qvalidatedlineedit.cpp | 6 ++++++ src/qt/qvalidatedlineedit.h | 1 + src/qt/sendcoinsentry.cpp | 2 +- 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index d545dc52e8..f9df91b333 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -55,6 +55,12 @@ void BitcoinAmountField::setText(const QString &text) } } +void BitcoinAmountField::clear() +{ + amount->clear(); + decimals->clear(); +} + bool BitcoinAmountField::validate() { bool valid = true; diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 2a0ef4bd99..fd09ab2c9b 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -18,6 +18,8 @@ public: void setText(const QString &text); QString text() const; + + void clear(); bool validate(); // Qt messes up the tab chain by default in some cases (issue http://bugreports.qt.nokia.com/browse/QTBUG-10907) // Hence we have to set it up manually diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp index 4b5acd8b07..2430cc9fb5 100644 --- a/src/qt/qvalidatedlineedit.cpp +++ b/src/qt/qvalidatedlineedit.cpp @@ -35,3 +35,9 @@ void QValidatedLineEdit::markValid() { setValid(true); } + +void QValidatedLineEdit::clear() +{ + setValid(true); + QLineEdit::clear(); +} diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h index 9fc026fab1..f7b9486a6e 100644 --- a/src/qt/qvalidatedlineedit.h +++ b/src/qt/qvalidatedlineedit.h @@ -10,6 +10,7 @@ class QValidatedLineEdit : public QLineEdit Q_OBJECT public: explicit QValidatedLineEdit(QWidget *parent = 0); + void clear(); protected: void focusInEvent(QFocusEvent *evt); diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 6e87e9cf99..2d4fe9b123 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -68,7 +68,7 @@ void SendCoinsEntry::clear() { ui->payTo->clear(); ui->addAsLabel->clear(); - ui->payAmount->setText(QString()); + ui->payAmount->clear(); ui->payTo->setFocus(); } -- cgit v1.2.3 From 8b936b617f2d51f66a86b407a51af55d8fb804fb Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 22 Jul 2011 18:30:25 +0200 Subject: Implement range... transaction filter --- src/qt/transactionview.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++-- src/qt/transactionview.h | 9 ++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 8aa43395e7..50291fea42 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include @@ -90,6 +92,7 @@ TransactionView::TransactionView(QWidget *parent) : QTableView *view = new QTableView(this); vlayout->addLayout(hlayout); + vlayout->addWidget(createDateRangeWidget()); vlayout->addWidget(view); vlayout->setSpacing(0); int width = view->verticalScrollBar()->sizeHint().width(); @@ -167,6 +170,7 @@ void TransactionView::setModel(WalletModel *model) void TransactionView::chooseDate(int idx) { QDate current = QDate::currentDate(); + dateRangeWidget->setVisible(false); switch(dateWidget->itemData(idx).toInt()) { case All: @@ -203,10 +207,10 @@ void TransactionView::chooseDate(int idx) TransactionFilterProxy::MAX_DATE); break; case Range: - // TODO ask specific range + dateRangeWidget->setVisible(true); + dateRangeChanged(); break; } - } void TransactionView::chooseType(int idx) @@ -337,3 +341,46 @@ void TransactionView::showDetails() dlg.exec(); } } + +QWidget *TransactionView::createDateRangeWidget() +{ + dateRangeWidget = new QFrame(); + dateRangeWidget->setFrameStyle(QFrame::Panel | QFrame::Raised); + dateRangeWidget->setContentsMargins(1,1,1,1); + QHBoxLayout *layout = new QHBoxLayout(dateRangeWidget); + layout->setContentsMargins(0,0,0,0); + layout->addSpacing(23); + layout->addWidget(new QLabel("Range:")); + + dateFrom = new QDateTimeEdit(this); + dateFrom->setDisplayFormat("dd/MM/yy"); + dateFrom->setCalendarPopup(true); + dateFrom->setMinimumWidth(100); + dateFrom->setDate(QDate::currentDate().addDays(-7)); + layout->addWidget(dateFrom); + layout->addWidget(new QLabel("to")); + + dateTo = new QDateTimeEdit(this); + dateTo->setDisplayFormat("dd/MM/yy"); + dateTo->setCalendarPopup(true); + dateTo->setMinimumWidth(100); + dateTo->setDate(QDate::currentDate()); + layout->addWidget(dateTo); + layout->addStretch(); + + // Hide by default + dateRangeWidget->setVisible(false); + + // Notify on change + connect(dateFrom, SIGNAL(dateChanged(QDate)), this, SLOT(dateRangeChanged())); + connect(dateTo, SIGNAL(dateChanged(QDate)), this, SLOT(dateRangeChanged())); + + return dateRangeWidget; +} + +void TransactionView::dateRangeChanged() +{ + transactionProxyModel->setDateRange( + QDateTime(dateFrom->date()), + QDateTime(dateTo->date()).addDays(1)); +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index f02751a074..54925f2855 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -12,6 +12,8 @@ class QComboBox; class QLineEdit; class QModelIndex; class QMenu; +class QFrame; +class QDateTimeEdit; QT_END_NAMESPACE class TransactionView : public QWidget @@ -45,8 +47,15 @@ private: QMenu *contextMenu; + QFrame *dateRangeWidget; + QDateTimeEdit *dateFrom; + QDateTimeEdit *dateTo; + + QWidget *createDateRangeWidget(); + private slots: void contextualMenu(const QPoint &); + void dateRangeChanged(); signals: void doubleClicked(const QModelIndex&); -- cgit v1.2.3 From 2eac3a6decf3353e5db8a8b07f3307d04e77d329 Mon Sep 17 00:00:00 2001 From: Celil Date: Fri, 22 Jul 2011 16:55:50 -0700 Subject: Allow ammount field to be empty so that one can specify .05 instead of having to type the leading zero as in 0.05 --- src/qt/bitcoinamountfield.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index f9df91b333..ea0a98b7f2 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -11,7 +11,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): QWidget(parent), amount(0), decimals(0) { amount = new QValidatedLineEdit(this); - amount->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this)); + amount->setValidator(new QRegExpValidator(QRegExp("[0-9]?"), this)); amount->setAlignment(Qt::AlignRight|Qt::AlignVCenter); amount->installEventFilter(this); amount->setMaximumWidth(100); @@ -64,11 +64,6 @@ void BitcoinAmountField::clear() bool BitcoinAmountField::validate() { bool valid = true; - if(amount->text().isEmpty()) - { - amount->setValid(false); - valid = false; - } if(decimals->text().isEmpty()) { decimals->setValid(false); @@ -79,10 +74,14 @@ bool BitcoinAmountField::validate() QString BitcoinAmountField::text() const { - if(amount->text().isEmpty() || decimals->text().isEmpty()) + if(decimals->text().isEmpty()) { return QString(); } + if(amount->text().isEmpty()) + { + return QString("0.") + decimals->text(); + } return amount->text() + QString(".") + decimals->text(); } -- cgit v1.2.3 From 8a13456f3ae0b55427e5a5b90151a3842e804950 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 24 Jul 2011 15:53:27 +0200 Subject: add windows program (.exe) icon --- bitcoin-qt.pro | 1 + scripts/make_windows_icon.py | 9 +++++++++ src/qt/res/bitcoin-qt.rc | 1 + src/qt/res/icons/bitcoin.ico | Bin 0 -> 15086 bytes 4 files changed, 11 insertions(+) create mode 100755 scripts/make_windows_icon.py create mode 100644 src/qt/res/bitcoin-qt.rc create mode 100644 src/qt/res/icons/bitcoin.ico diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index cdff04b43a..f1bc8e7fd2 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -11,6 +11,7 @@ macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 BOOST_FILESYSTEM_VERSION=3 macx:LIBS += -lboost_thread-mt -lboost_system-mt -lboost_filesystem-mt -lboost_program_options-mt windows:DEFINES += __WXMSW__ windows:LIBS += -lssl -lcrypto -lboost_system-mgw44-mt-1_43 -lboost_filesystem-mgw44-mt-1_43 -lboost_program_options-mgw44-mt-1_43 -lboost_thread-mgw44-mt-1_43 -ldb_cxx -lws2_32 -lgdi32 +windows:RC_FILE = src/qt/res/bitcoin-qt.rc # for extra security against potential buffer overflows QMAKE_CXXFLAGS += -fstack-protector diff --git a/scripts/make_windows_icon.py b/scripts/make_windows_icon.py new file mode 100755 index 0000000000..d722ebe9b6 --- /dev/null +++ b/scripts/make_windows_icon.py @@ -0,0 +1,9 @@ +#!/bin/bash +# create multiresolution windows icon +ICON_SRC=../src/qt/res/icons/bitcoin.png +ICON_DST=../src/qt/res/icons/bitcoin.ico +convert ${ICON_SRC} -resize 16x16 bitcoin-16.png +convert ${ICON_SRC} -resize 32x32 bitcoin-32.png +convert ${ICON_SRC} -resize 48x48 bitcoin-48.png +convert bitcoin-16.png bitcoin-32.png bitcoin-48.png ${ICON_DST} + diff --git a/src/qt/res/bitcoin-qt.rc b/src/qt/res/bitcoin-qt.rc new file mode 100644 index 0000000000..1a1ab53be4 --- /dev/null +++ b/src/qt/res/bitcoin-qt.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icons/bitcoin.ico" diff --git a/src/qt/res/icons/bitcoin.ico b/src/qt/res/icons/bitcoin.ico new file mode 100644 index 0000000000..01afd3c6da Binary files /dev/null and b/src/qt/res/icons/bitcoin.ico differ -- cgit v1.2.3 From 591dcaf68170c32193de515dd1ab267c4eefdbae Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 24 Jul 2011 18:06:07 +0200 Subject: improve tooltip texts --- src/qt/bitcoingui.cpp | 4 +++- src/qt/forms/sendcoinsdialog.ui | 3 +++ src/qt/forms/sendcoinsentry.ui | 7 +++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index c4462dd40c..938d030d25 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -162,10 +162,12 @@ void BitcoinGUI::createActions() QActionGroup *tabGroup = new QActionGroup(this); overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Overview"), this); + overviewAction->setToolTip(tr("Show general overview of wallet")); overviewAction->setCheckable(true); tabGroup->addAction(overviewAction); historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this); + historyAction->setToolTip(tr("Browse transaction history")); historyAction->setCheckable(true); tabGroup->addAction(historyAction); @@ -199,7 +201,7 @@ void BitcoinGUI::createActions() openBitcoinAction = new QAction(QIcon(":/icons/bitcoin"), tr("Open &Bitcoin"), this); openBitcoinAction->setToolTip(tr("Show the Bitcoin window")); exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this); - exportAction->setToolTip(tr("Export data in current view to a file")); + exportAction->setToolTip(tr("Export the current view to a file")); connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked())); diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 547582e8df..5b30d99e56 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -63,6 +63,9 @@ + + Send to multiple recipients at once + &Add recipient... diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 2dca6b74b1..13593c2c1e 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -100,7 +100,7 @@ - Look up adress in address book + Choose adress from address book @@ -123,7 +123,7 @@ - Paste address from system clipboard + Paste address from clipboard @@ -142,6 +142,9 @@ + + Remove this recipient + -- cgit v1.2.3 From f5472574a7ae087b764a5295feba025891703ded Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 24 Jul 2011 18:32:21 +0200 Subject: make all tab icons blue/gray for more consistencyx --- src/qt/res/icons/receive.png | Bin 1763 -> 1815 bytes src/qt/res/icons/send.png | Bin 1704 -> 1806 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/qt/res/icons/receive.png b/src/qt/res/icons/receive.png index e59a2cdc92..e8f418a4f8 100644 Binary files a/src/qt/res/icons/receive.png and b/src/qt/res/icons/receive.png differ diff --git a/src/qt/res/icons/send.png b/src/qt/res/icons/send.png index a1c56f52e0..55ce550b4f 100644 Binary files a/src/qt/res/icons/send.png and b/src/qt/res/icons/send.png differ -- cgit v1.2.3 From daa1a7398fc1c6a70c9253ee7c5825674e7ba378 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 24 Jul 2011 19:09:29 +0200 Subject: better exit icon --- src/qt/res/icons/quit.png | Bin 876 -> 2163 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/qt/res/icons/quit.png b/src/qt/res/icons/quit.png index fb510fbea6..0dde6f395c 100644 Binary files a/src/qt/res/icons/quit.png and b/src/qt/res/icons/quit.png differ -- cgit v1.2.3 From 2af40a501351d2304182f542b072639e2e9a8287 Mon Sep 17 00:00:00 2001 From: Celil Date: Sun, 24 Jul 2011 14:45:51 -0700 Subject: On Mac OS X do not link aginst the boost libraries in UNIX:LIBS --- bitcoin-qt.pro | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index f1bc8e7fd2..e0025472f8 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -6,9 +6,10 @@ DEFINES += QT_GUI CONFIG += no_include_pwd # for boost 1.37, add -mt to the boost libraries -unix:LIBS += -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -ldb_cxx +unix:LIBS += -lssl -lcrypto -ldb_cxx +unix:!macx:LIBS += -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 BOOST_FILESYSTEM_VERSION=3 -macx:LIBS += -lboost_thread-mt -lboost_system-mt -lboost_filesystem-mt -lboost_program_options-mt +macx:LIBS += -lboost_system-mt -lboost_filesystem-mt -lboost_program_options-mt -lboost_thread-mt windows:DEFINES += __WXMSW__ windows:LIBS += -lssl -lcrypto -lboost_system-mgw44-mt-1_43 -lboost_filesystem-mgw44-mt-1_43 -lboost_program_options-mgw44-mt-1_43 -lboost_thread-mgw44-mt-1_43 -ldb_cxx -lws2_32 -lgdi32 windows:RC_FILE = src/qt/res/bitcoin-qt.rc -- cgit v1.2.3 From a8c50c06e315007941689d3297919622bc93053b Mon Sep 17 00:00:00 2001 From: celil-kj Date: Sun, 24 Jul 2011 14:54:40 -0700 Subject: Clean up the project file. --- bitcoin-qt.pro | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index e0025472f8..feb64533c0 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -6,12 +6,12 @@ DEFINES += QT_GUI CONFIG += no_include_pwd # for boost 1.37, add -mt to the boost libraries -unix:LIBS += -lssl -lcrypto -ldb_cxx +LIBS += -lssl -lcrypto -ldb_cxx unix:!macx:LIBS += -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 BOOST_FILESYSTEM_VERSION=3 macx:LIBS += -lboost_system-mt -lboost_filesystem-mt -lboost_program_options-mt -lboost_thread-mt +macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 BOOST_FILESYSTEM_VERSION=3 +windows:LIBS += -lboost_system-mgw44-mt-1_43 -lboost_filesystem-mgw44-mt-1_43 -lboost_program_options-mgw44-mt-1_43 -lboost_thread-mgw44-mt-1_43 -lws2_32 -lgdi32 windows:DEFINES += __WXMSW__ -windows:LIBS += -lssl -lcrypto -lboost_system-mgw44-mt-1_43 -lboost_filesystem-mgw44-mt-1_43 -lboost_program_options-mgw44-mt-1_43 -lboost_thread-mgw44-mt-1_43 -ldb_cxx -lws2_32 -lgdi32 windows:RC_FILE = src/qt/res/bitcoin-qt.rc # for extra security against potential buffer overflows -- cgit v1.2.3 From bbae0fc9efa6eb7155d679a1cc3eeb451d594d14 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 25 Jul 2011 18:39:52 +0200 Subject: put color constants in guiconstants.h --- src/qt/guiconstants.h | 8 ++++++-- src/qt/qvalidatedlineedit.cpp | 4 +++- src/qt/transactiontablemodel.cpp | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 59f49625bd..79856d4781 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -4,8 +4,12 @@ /* milliseconds between model updates */ static const int MODEL_UPDATE_DELAY = 500; -/* size of cache */ -static const unsigned int WALLET_CACHE_SIZE = 100; +/* Invalid field background style */ +#define STYLE_INVALID "background:#FF8080" +/* Transaction list -- unconfirmed transaction */ +#define COLOR_UNCONFIRMED QColor(128, 128, 128) +/* Transaction list -- negative amount */ +#define COLOR_NEGATIVE QColor(128, 128, 128) #endif // GUICONSTANTS_H diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp index 2430cc9fb5..8ca230c9d7 100644 --- a/src/qt/qvalidatedlineedit.cpp +++ b/src/qt/qvalidatedlineedit.cpp @@ -1,5 +1,7 @@ #include "qvalidatedlineedit.h" +#include "guiconstants.h" + QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) : QLineEdit(parent), valid(true) { @@ -19,7 +21,7 @@ void QValidatedLineEdit::setValid(bool valid) } else { - setStyleSheet("background:#FF8080"); + setStyleSheet(STYLE_INVALID); } this->valid = valid; } diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 17622e07fa..7062ceecbd 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -514,11 +514,11 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const /* Non-confirmed transactions are grey */ if(!rec->status.confirmed) { - return QColor(128, 128, 128); + return COLOR_UNCONFIRMED; } if(index.column() == Amount && (rec->credit+rec->debit) < 0) { - return QColor(255, 0, 0); + return COLOR_NEGATIVE; } } else if (role == TypeRole) -- cgit v1.2.3 From e285ffcd052a42a6e870f093e7663671a2a3b147 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 25 Jul 2011 21:35:45 +0200 Subject: preparations for multiple unit (uBTC, mBTC, BTC) support, fix amount entry issue --- bitcoin-qt.pro | 6 ++- src/qt/bitcoinamountfield.cpp | 12 ++++- src/qt/bitcoingui.cpp | 7 +-- src/qt/bitcoinunits.cpp | 96 ++++++++++++++++++++++++++++++++++++++++ src/qt/bitcoinunits.h | 34 ++++++++++++++ src/qt/guiutil.cpp | 10 ----- src/qt/guiutil.h | 6 --- src/qt/optionsdialog.cpp | 64 ++++++++++++++++++++++----- src/qt/optionsdialog.h | 9 +++- src/qt/overviewpage.cpp | 6 +-- src/qt/sendcoinsdialog.cpp | 6 +-- src/qt/sendcoinsentry.cpp | 3 +- src/qt/transactiontablemodel.cpp | 3 +- src/qt/transactionview.cpp | 4 +- 14 files changed, 220 insertions(+), 46 deletions(-) create mode 100644 src/qt/bitcoinunits.cpp create mode 100644 src/qt/bitcoinunits.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index f1bc8e7fd2..e8d2deffc4 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -87,7 +87,8 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/qtwin.h \ src/crypter.h \ src/qt/sendcoinsentry.h \ - src/qt/qvalidatedlineedit.h + src/qt/qvalidatedlineedit.h \ + src/qt/bitcoinunits.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -129,7 +130,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/qtwin.cpp \ src/crypter.cpp \ src/qt/sendcoinsentry.cpp \ - src/qt/qvalidatedlineedit.cpp + src/qt/qvalidatedlineedit.cpp \ + src/qt/bitcoinunits.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index b312b9792e..330a7bfdef 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -1,5 +1,6 @@ #include "bitcoinamountfield.h" #include "qvalidatedlineedit.h" +#include "bitcoinunits.h" #include #include @@ -11,7 +12,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): QWidget(parent), amount(0), decimals(0) { amount = new QValidatedLineEdit(this); - amount->setValidator(new QRegExpValidator(QRegExp("[0-9]?"), this)); + amount->setValidator(new QRegExpValidator(QRegExp("[0-9]*"), this)); amount->setAlignment(Qt::AlignRight|Qt::AlignVCenter); amount->installEventFilter(this); amount->setMaximumWidth(100); @@ -26,7 +27,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): layout->addWidget(amount); layout->addWidget(new QLabel(QString("."))); layout->addWidget(decimals); - layout->addWidget(new QLabel(QString(" BTC"))); + layout->addWidget(new QLabel(QString(" ") + BitcoinUnits::name(BitcoinUnits::BTC))); layout->addStretch(1); layout->setContentsMargins(0,0,0,0); @@ -69,6 +70,13 @@ bool BitcoinAmountField::validate() decimals->setValid(false); valid = false; } + if(!BitcoinUnits::parse(BitcoinUnits::BTC, text(), 0)) + { + amount->setValid(false); + decimals->setValid(false); + valid = false; + } + return valid; } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 938d030d25..bd80e42d03 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -11,13 +11,13 @@ #include "aboutdialog.h" #include "clientmodel.h" #include "walletmodel.h" -#include "guiutil.h" #include "editaddressdialog.h" #include "optionsmodel.h" #include "transactiondescdialog.h" #include "addresstablemodel.h" #include "transactionview.h" #include "overviewpage.h" +#include "bitcoinunits.h" #include #include @@ -436,7 +436,8 @@ void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) QString strMessage = tr("This transaction is over the size limit. You can still send it for a fee of %1, " "which goes to the nodes that process your transaction and helps to support the network. " - "Do you want to pay the fee?").arg(GUIUtil::formatMoney(nFeeRequired)); + "Do you want to pay the fee?").arg( + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired)); QMessageBox::StandardButton retval = QMessageBox::question( this, tr("Sending..."), strMessage, QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); @@ -462,7 +463,7 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int trayIcon->showMessage((amount)<0 ? tr("Sent transaction") : tr("Incoming transaction"), tr("Date: ") + date + "\n" + - tr("Amount: ") + GUIUtil::formatMoney(amount, true) + "\n" + + tr("Amount: ") + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, amount, true) + "\n" + tr("Type: ") + type + "\n" + tr("Address: ") + address + "\n", QSystemTrayIcon::Information); diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp new file mode 100644 index 0000000000..ff30563f97 --- /dev/null +++ b/src/qt/bitcoinunits.cpp @@ -0,0 +1,96 @@ +#include "bitcoinunits.h" + +#include + +QString BitcoinUnits::name(BitcoinUnits::Unit unit) +{ + switch(unit) + { + case BTC: return "BTC"; + case mBTC: return "mBTC"; + case uBTC: return "uBTC"; + default: return "???"; + } +} + +QString BitcoinUnits::description(BitcoinUnits::Unit unit) +{ + switch(unit) + { + case BTC: return "Bitcoin"; + case mBTC: return "Milli-bitcoin"; + case uBTC: return "Micro-bitcoin"; + default: return "???"; + } +} + +qint64 BitcoinUnits::factor(BitcoinUnits::Unit unit) +{ + switch(unit) + { + case BTC: return 100000000; + case mBTC: return 100000; + case uBTC: return 100; + default: return 100000000; + } +} + +int BitcoinUnits::decimals(BitcoinUnits::Unit unit) +{ + switch(unit) + { + case BTC: return 8; + case mBTC: return 5; + case uBTC: return 2; + default: return 0; + } +} + +QString BitcoinUnits::format(BitcoinUnits::Unit unit, qint64 n, bool fPlus) +{ + // Note: not using straight sprintf here because we do NOT want + // localized number formatting. + qint64 coin = factor(unit); + int num_decimals = decimals(unit); + qint64 n_abs = (n > 0 ? n : -n); + qint64 quotient = n_abs / coin; + qint64 remainder = n_abs % coin; + QString quotient_str = QString::number(quotient); + QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0'); + + // Right-trim excess 0's after the decimal point + int nTrim = 0; + for (int i = remainder_str.size()-1; i>=2 && (remainder_str.at(i) == '0'); --i) + ++nTrim; + remainder_str.chop(nTrim); + + if (n < 0) + quotient_str.insert(0, '-'); + else if (fPlus && n > 0) + quotient_str.insert(0, '+'); + return quotient_str + QString(".") + remainder_str; +} + +QString BitcoinUnits::formatWithUnit(BitcoinUnits::Unit unit, qint64 amount, bool plussign) +{ + return format(unit, amount, plussign) + QString(" ") + name(unit); +} + +bool BitcoinUnits::parse(BitcoinUnits::Unit unit, const QString &value, qint64 *val_out) +{ + int num_decimals = decimals(unit); + QStringList parts = value.split("."); + if(parts.size() != 2 || parts.at(1).size() > num_decimals) + return false; // Max num decimals + bool ok = false; + QString str = parts[0] + parts[1].leftJustified(num_decimals, '0'); + if(str.size()>18) + return false; // Bounds check + + qint64 retvalue = str.toLongLong(&ok); + if(val_out) + { + *val_out = retvalue; + } + return ok; +} diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h new file mode 100644 index 0000000000..fa85755ad4 --- /dev/null +++ b/src/qt/bitcoinunits.h @@ -0,0 +1,34 @@ +#ifndef BITCOINUNITS_H +#define BITCOINUNITS_H + +#include + +// Bitcoin unit definitions +class BitcoinUnits +{ +public: + enum Unit + { + BTC, + mBTC, + uBTC + }; + + // Short name + static QString name(Unit unit); + // Longer description + static QString description(Unit unit); + // Number of satoshis / unit + static qint64 factor(Unit unit); + // Number of decimals left + static int decimals(Unit unit); + // Format as string + static QString format(Unit unit, qint64 amount, bool plussign=false); + // Format as string (with unit) + static QString formatWithUnit(Unit unit, qint64 amount, bool plussign=false); + // Parse string to coin amount + static bool parse(Unit unit, const QString &value, qint64 *val_out); + +}; + +#endif // BITCOINUNITS_H diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 31b28024df..308a6ba9bc 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -37,13 +37,3 @@ void GUIUtil::setupAmountWidget(QLineEdit *widget, QWidget *parent) widget->setValidator(amountValidator); widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter); } - -bool GUIUtil::parseMoney(const QString &amount, qint64 *val_out) -{ - return ParseMoney(amount.toStdString(), *val_out); -} - -QString GUIUtil::formatMoney(qint64 amount, bool plussign) -{ - return QString::fromStdString(FormatMoney(amount, plussign)); -} diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 58f8979511..26a1a03712 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -20,12 +20,6 @@ public: static void setupAddressWidget(QLineEdit *widget, QWidget *parent); static void setupAmountWidget(QLineEdit *widget, QWidget *parent); - - // Convenience wrapper around ParseMoney that takes QString - static bool parseMoney(const QString &amount, qint64 *val_out); - - // Convenience wrapper around FormatMoney that returns QString - static QString formatMoney(qint64 amount, bool plussign=false); }; #endif // GUIUTIL_H diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 3697b9fe41..4f3a82d319 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -17,8 +17,9 @@ #include #include #include +#include -/* First (currently only) page of options */ +/* First page of options */ class MainOptionsPage : public QWidget { public: @@ -41,9 +42,23 @@ public slots: }; +class DisplayOptionsPage : public QWidget +{ +public: + explicit DisplayOptionsPage(QWidget *parent=0); + + void setMapper(MonitoredDataMapper *mapper); +private: + QLineEdit *unit; +signals: + +public slots: + +}; + OptionsDialog::OptionsDialog(QWidget *parent): QDialog(parent), contents_widget(0), pages_widget(0), - main_options_page(0), model(0) + model(0), main_page(0), display_page(0) { contents_widget = new QListWidget(); contents_widget->setMaximumWidth(128); @@ -53,8 +68,13 @@ OptionsDialog::OptionsDialog(QWidget *parent): QListWidgetItem *item_main = new QListWidgetItem(tr("Main")); contents_widget->addItem(item_main); - main_options_page = new MainOptionsPage(this); - pages_widget->addWidget(main_options_page); + main_page = new MainOptionsPage(this); + pages_widget->addWidget(main_page); + + QListWidgetItem *item_display = new QListWidgetItem(tr("Display")); + //contents_widget->addItem(item_display); + display_page = new DisplayOptionsPage(this); + pages_widget->addWidget(display_page); contents_widget->setCurrentRow(0); @@ -83,6 +103,8 @@ OptionsDialog::OptionsDialog(QWidget *parent): connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApply())); /* Event bindings */ + qDebug() << "setup"; + connect(contents_widget, SIGNAL(currentRowChanged(int)), this, SLOT(changePage(int))); connect(buttonbox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(okClicked())); connect(buttonbox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(cancelClicked())); connect(buttonbox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(applyClicked())); @@ -93,18 +115,16 @@ void OptionsDialog::setModel(OptionsModel *model) this->model = model; mapper->setModel(model); - main_options_page->setMapper(mapper); + main_page->setMapper(mapper); + display_page->setMapper(mapper); mapper->toFirst(); } -void OptionsDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) +void OptionsDialog::changePage(int index) { - Q_UNUSED(previous); - if(current) - { - pages_widget->setCurrentIndex(contents_widget->row(current)); - } + qDebug() << "page" << index; + pages_widget->setCurrentIndex(index); } void OptionsDialog::okClicked() @@ -224,3 +244,25 @@ void MainOptionsPage::setMapper(MonitoredDataMapper *mapper) mapper->addMapping(fee_edit, OptionsModel::Fee); } +DisplayOptionsPage::DisplayOptionsPage(QWidget *parent): + QWidget(parent) +{ + QVBoxLayout *layout = new QVBoxLayout(); + QHBoxLayout *unit_hbox = new QHBoxLayout(); + unit_hbox->addSpacing(18); + QLabel *unit_label = new QLabel(tr("&Unit: ")); + unit_hbox->addWidget(unit_label); + unit = new QLineEdit(); + + unit_label->setBuddy(unit); + unit_hbox->addWidget(unit); + + layout->addLayout(unit_hbox); + layout->addStretch(); + + setLayout(layout); +} + +void DisplayOptionsPage::setMapper(MonitoredDataMapper *mapper) +{ +} diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index 07e85297d5..d5238a3664 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -11,6 +11,7 @@ class QPushButton; QT_END_NAMESPACE class OptionsModel; class MainOptionsPage; +class DisplayOptionsPage; class MonitoredDataMapper; class OptionsDialog : public QDialog @@ -24,7 +25,8 @@ public: signals: public slots: - void changePage(QListWidgetItem *current, QListWidgetItem *previous); + void changePage(int index); + private slots: void okClicked(); void cancelClicked(); @@ -34,11 +36,14 @@ private slots: private: QListWidget *contents_widget; QStackedWidget *pages_widget; - MainOptionsPage *main_options_page; OptionsModel *model; MonitoredDataMapper *mapper; QPushButton *apply_button; + // Pages + MainOptionsPage *main_page; + DisplayOptionsPage *display_page; + void setupMainPage(); }; diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index d991f0d7ba..9515117c21 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -2,7 +2,7 @@ #include "ui_overviewpage.h" #include "walletmodel.h" -#include "guiutil.h" +#include "bitcoinunits.h" OverviewPage::OverviewPage(QWidget *parent) : QWidget(parent), @@ -34,8 +34,8 @@ OverviewPage::~OverviewPage() void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance) { - ui->labelBalance->setText(GUIUtil::formatMoney(balance) + QString(" BTC")); - ui->labelUnconfirmed->setText(GUIUtil::formatMoney(unconfirmedBalance) + QString(" BTC")); + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, balance)); + ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, unconfirmedBalance)); } void OverviewPage::setNumTransactions(int count) diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 38a0a65520..58f9422b27 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1,7 +1,7 @@ #include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" #include "walletmodel.h" -#include "guiutil.h" +#include "bitcoinunits.h" #include "addressbookpage.h" #include "optionsmodel.h" #include "sendcoinsentry.h" @@ -71,7 +71,7 @@ void SendCoinsDialog::on_sendButton_clicked() QStringList formatted; foreach(const SendCoinsRecipient &rcp, recipients) { - formatted.append(tr("%1 BTC to %2 (%3)").arg(GUIUtil::formatMoney(rcp.amount), rcp.label, rcp.address)); + formatted.append(tr("%1 to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), rcp.label, rcp.address)); } QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), @@ -105,7 +105,7 @@ void SendCoinsDialog::on_sendButton_clicked() case WalletModel::AmountWithFeeExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), tr("Total exceeds your balance when the %1 transaction fee is included"). - arg(GUIUtil::formatMoney(sendstatus.fee)), + arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, sendstatus.fee)), QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::DuplicateAddress: diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 2d4fe9b123..9fc041111f 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -1,6 +1,7 @@ #include "sendcoinsentry.h" #include "ui_sendcoinsentry.h" #include "guiutil.h" +#include "bitcoinunits.h" #include "addressbookpage.h" #include "walletmodel.h" #include "addresstablemodel.h" @@ -103,7 +104,7 @@ SendCoinsRecipient SendCoinsEntry::getValue() rv.address = ui->payTo->text(); rv.label = ui->addAsLabel->text(); - GUIUtil::parseMoney(ui->payAmount->text(), &rv.amount); + BitcoinUnits::parse(BitcoinUnits::BTC, ui->payAmount->text(), &rv.amount); return rv; } diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 7062ceecbd..2b8fe0b471 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -5,6 +5,7 @@ #include "transactiondesc.h" #include "walletmodel.h" #include "addresstablemodel.h" +#include "bitcoinunits.h" #include "headers.h" @@ -397,7 +398,7 @@ QVariant TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx) QVariant TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const { - QString str = QString::fromStdString(FormatMoney(wtx->credit + wtx->debit)); + QString str = BitcoinUnits::format(BitcoinUnits::BTC, wtx->credit + wtx->debit); if(showUnconfirmed) { if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 50291fea42..f88b6fc123 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -5,7 +5,7 @@ #include "walletmodel.h" #include "addresstablemodel.h" #include "transactiontablemodel.h" -#include "guiutil.h" +#include "bitcoinunits.h" #include "csvmodelwriter.h" #include "transactiondescdialog.h" #include "editaddressdialog.h" @@ -227,7 +227,7 @@ void TransactionView::changedPrefix(const QString &prefix) void TransactionView::changedAmount(const QString &amount) { qint64 amount_parsed = 0; - if(GUIUtil::parseMoney(amount, &amount_parsed)) + if(BitcoinUnits::parse(BitcoinUnits::BTC, amount, &amount_parsed)) { transactionProxyModel->setMinAmount(amount_parsed); } -- cgit v1.2.3 From e780b94bd3395cb9ecf032c5dcd406d034422d75 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 25 Jul 2011 22:28:12 +0200 Subject: =?UTF-8?q?fix=20unit=20names=20(=CE=BCBTC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/qt/bitcoinunits.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index ff30563f97..567e51ff39 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -6,10 +6,10 @@ QString BitcoinUnits::name(BitcoinUnits::Unit unit) { switch(unit) { - case BTC: return "BTC"; - case mBTC: return "mBTC"; - case uBTC: return "uBTC"; - default: return "???"; + case BTC: return QString("BTC"); + case mBTC: return QString("mBTC"); + case uBTC: return QString::fromUtf8("μBTC"); + default: return QString("???"); } } @@ -17,10 +17,10 @@ QString BitcoinUnits::description(BitcoinUnits::Unit unit) { switch(unit) { - case BTC: return "Bitcoin"; - case mBTC: return "Milli-bitcoin"; - case uBTC: return "Micro-bitcoin"; - default: return "???"; + case BTC: return QString("Bitcoin"); + case mBTC: return QString("Milli-bitcoin (1/1000)"); + case uBTC: return QString("Micro-bitcoin (1/1000,000)"); + default: return QString("???"); } } -- cgit v1.2.3 From ca1dbe10ed3c8cf253ee79e71d4f76363daae17b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 26 Jul 2011 09:16:50 +0200 Subject: Negative transaction color changed to red (was grey due to mistake) --- src/qt/guiconstants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 79856d4781..deef3e3484 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -10,6 +10,6 @@ static const int MODEL_UPDATE_DELAY = 500; /* Transaction list -- unconfirmed transaction */ #define COLOR_UNCONFIRMED QColor(128, 128, 128) /* Transaction list -- negative amount */ -#define COLOR_NEGATIVE QColor(128, 128, 128) +#define COLOR_NEGATIVE QColor(255, 0, 0) #endif // GUICONSTANTS_H -- cgit v1.2.3 From 587e52855a4c6c4f672ecec28ab9a029d4e4f850 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 26 Jul 2011 13:08:34 +0200 Subject: allow multiple units in bitcoin amount widget (for example, for sending) using a combobox --- src/qt/bitcoinamountfield.cpp | 71 ++++++++++++++++++++++++++++++++++++++---- src/qt/bitcoinamountfield.h | 24 ++++++++++++--- src/qt/bitcoinunits.cpp | 72 +++++++++++++++++++++++++++++++++++++------ src/qt/bitcoinunits.h | 36 +++++++++++++++++----- src/qt/optionsdialog.cpp | 1 - src/qt/optionsmodel.cpp | 14 ++------- src/qt/optionsmodel.h | 16 +++++----- src/qt/sendcoinsentry.cpp | 12 +++++++- 8 files changed, 197 insertions(+), 49 deletions(-) diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 330a7bfdef..7fe28f9a17 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -7,9 +7,10 @@ #include #include #include +#include BitcoinAmountField::BitcoinAmountField(QWidget *parent): - QWidget(parent), amount(0), decimals(0) + QWidget(parent), amount(0), decimals(0), currentUnit(-1) { amount = new QValidatedLineEdit(this); amount->setValidator(new QRegExpValidator(QRegExp("[0-9]*"), this)); @@ -18,7 +19,6 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): amount->setMaximumWidth(100); decimals = new QValidatedLineEdit(this); decimals->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this)); - decimals->setMaxLength(8); decimals->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); decimals->setMaximumWidth(75); @@ -27,7 +27,9 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): layout->addWidget(amount); layout->addWidget(new QLabel(QString("."))); layout->addWidget(decimals); - layout->addWidget(new QLabel(QString(" ") + BitcoinUnits::name(BitcoinUnits::BTC))); + unit = new QComboBox(this); + unit->setModel(new BitcoinUnits(this)); + layout->addWidget(unit); layout->addStretch(1); layout->setContentsMargins(0,0,0,0); @@ -39,6 +41,10 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): // If one if the widgets changes, the combined content changes as well connect(amount, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged())); connect(decimals, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged())); + connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int))); + + // TODO: set default based on configuration + unitChanged(unit->currentIndex()); } void BitcoinAmountField::setText(const QString &text) @@ -72,17 +78,22 @@ bool BitcoinAmountField::validate() } if(!BitcoinUnits::parse(BitcoinUnits::BTC, text(), 0)) { - amount->setValid(false); - decimals->setValid(false); + setValid(false); valid = false; } return valid; } +void BitcoinAmountField::setValid(bool valid) +{ + amount->setValid(valid); + decimals->setValid(valid); +} + QString BitcoinAmountField::text() const { - if(decimals->text().isEmpty()) + if(decimals->text().isEmpty() && amount->text().isEmpty()) { return QString(); } @@ -111,3 +122,51 @@ QWidget *BitcoinAmountField::setupTabChain(QWidget *prev) QWidget::setTabOrder(amount, decimals); return decimals; } + +qint64 BitcoinAmountField::value(bool *valid_out) const +{ + qint64 val_out = 0; + bool valid = BitcoinUnits::parse(currentUnit, text(), &val_out); + if(valid_out) + { + *valid_out = valid; + } + return val_out; +} + +void BitcoinAmountField::setValue(qint64 value) +{ + setText(BitcoinUnits::format(currentUnit, value)); +} + +void BitcoinAmountField::unitChanged(int idx) +{ + // Use description tooltip for current unit for the combobox + unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString()); + + // Determine new unit ID + int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt(); + + // Parse current value and convert to new unit + bool valid = false; + qint64 currentValue = value(&valid); + + currentUnit = newUnit; + + // Set max length after retrieving the value, to prevent truncation + amount->setMaxLength(BitcoinUnits::amountDigits(currentUnit)); + decimals->setMaxLength(BitcoinUnits::decimals(currentUnit)); + + if(valid) + { + setValue(currentValue); + } + else + { + // If current value is invalid, just clear field + setText(""); + } + setValid(true); + + +} diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index fd09ab2c9b..6e724d06ae 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -5,6 +5,7 @@ QT_BEGIN_NAMESPACE class QValidatedLineEdit; +class QComboBox; QT_END_NAMESPACE // Coin amount entry widget with separate parts for whole @@ -12,15 +13,21 @@ QT_END_NAMESPACE class BitcoinAmountField: public QWidget { Q_OBJECT - Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged USER true); + //Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged USER true); + Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY textChanged USER true); public: explicit BitcoinAmountField(QWidget *parent = 0); - void setText(const QString &text); - QString text() const; + qint64 value(bool *valid=0) const; + void setValue(qint64 value); - void clear(); + // Mark current valid as invalid in UI + void setValid(bool valid); bool validate(); + + // Make field empty and ready for new input + void clear(); + // Qt messes up the tab chain by default in some cases (issue http://bugreports.qt.nokia.com/browse/QTBUG-10907) // Hence we have to set it up manually QWidget *setupTabChain(QWidget *prev); @@ -35,6 +42,15 @@ protected: private: QValidatedLineEdit *amount; QValidatedLineEdit *decimals; + QComboBox *unit; + int currentUnit; + + void setText(const QString &text); + QString text() const; + +private slots: + void unitChanged(int idx); + }; diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 567e51ff39..8414a759ee 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -2,7 +2,22 @@ #include -QString BitcoinUnits::name(BitcoinUnits::Unit unit) +BitcoinUnits::BitcoinUnits(QObject *parent): + QAbstractListModel(parent), + unitlist(availableUnits()) +{ +} + +QList BitcoinUnits::availableUnits() +{ + QList unitlist; + unitlist.append(BTC); + unitlist.append(mBTC); + unitlist.append(uBTC); + return unitlist; +} + +QString BitcoinUnits::name(int unit) { switch(unit) { @@ -13,18 +28,18 @@ QString BitcoinUnits::name(BitcoinUnits::Unit unit) } } -QString BitcoinUnits::description(BitcoinUnits::Unit unit) +QString BitcoinUnits::description(int unit) { switch(unit) { - case BTC: return QString("Bitcoin"); - case mBTC: return QString("Milli-bitcoin (1/1000)"); - case uBTC: return QString("Micro-bitcoin (1/1000,000)"); + case BTC: return QString("Bitcoins"); + case mBTC: return QString("Milli-Bitcoins (1 / 1,000)"); + case uBTC: return QString("Micro-Bitcoins (1 / 1,000,000)"); default: return QString("???"); } } -qint64 BitcoinUnits::factor(BitcoinUnits::Unit unit) +qint64 BitcoinUnits::factor(int unit) { switch(unit) { @@ -35,7 +50,18 @@ qint64 BitcoinUnits::factor(BitcoinUnits::Unit unit) } } -int BitcoinUnits::decimals(BitcoinUnits::Unit unit) +int BitcoinUnits::amountDigits(int unit) +{ + switch(unit) + { + case BTC: return 8; // 21,000,000 + case mBTC: return 11; // 21,000,000,000 + case uBTC: return 14; // 21,000,000,000,000 + default: return 0; + } +} + +int BitcoinUnits::decimals(int unit) { switch(unit) { @@ -46,7 +72,7 @@ int BitcoinUnits::decimals(BitcoinUnits::Unit unit) } } -QString BitcoinUnits::format(BitcoinUnits::Unit unit, qint64 n, bool fPlus) +QString BitcoinUnits::format(int unit, qint64 n, bool fPlus) { // Note: not using straight sprintf here because we do NOT want // localized number formatting. @@ -71,12 +97,12 @@ QString BitcoinUnits::format(BitcoinUnits::Unit unit, qint64 n, bool fPlus) return quotient_str + QString(".") + remainder_str; } -QString BitcoinUnits::formatWithUnit(BitcoinUnits::Unit unit, qint64 amount, bool plussign) +QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign) { return format(unit, amount, plussign) + QString(" ") + name(unit); } -bool BitcoinUnits::parse(BitcoinUnits::Unit unit, const QString &value, qint64 *val_out) +bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out) { int num_decimals = decimals(unit); QStringList parts = value.split("."); @@ -94,3 +120,29 @@ bool BitcoinUnits::parse(BitcoinUnits::Unit unit, const QString &value, qint64 * } return ok; } + +int BitcoinUnits::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return unitlist.size(); +} + +QVariant BitcoinUnits::data(const QModelIndex &index, int role) const +{ + int row = index.row(); + if(row >= 0 && row < unitlist.size()) + { + Unit unit = unitlist.at(row); + switch(role) + { + case Qt::EditRole: + case Qt::DisplayRole: + return QVariant(name(unit)); + case Qt::ToolTipRole: + return QVariant(description(unit)); + case UnitRole: + return QVariant(static_cast(unit)); + } + } + return QVariant(); +} diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index fa85755ad4..18b623517c 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -2,33 +2,53 @@ #define BITCOINUNITS_H #include +#include // Bitcoin unit definitions -class BitcoinUnits +class BitcoinUnits: public QAbstractListModel { public: + explicit BitcoinUnits(QObject *parent); + enum Unit { + // Source: https://en.bitcoin.it/wiki/Units + // Please add only sensible ones BTC, mBTC, uBTC }; + /// Static API + // Get list of units, for dropdown box + static QList availableUnits(); // Short name - static QString name(Unit unit); + static QString name(int unit); // Longer description - static QString description(Unit unit); + static QString description(int unit); // Number of satoshis / unit - static qint64 factor(Unit unit); + static qint64 factor(int unit); + // Number of amount digits (to represent max number of coins) + static int amountDigits(int unit); // Number of decimals left - static int decimals(Unit unit); + static int decimals(int unit); // Format as string - static QString format(Unit unit, qint64 amount, bool plussign=false); + static QString format(int unit, qint64 amount, bool plussign=false); // Format as string (with unit) - static QString formatWithUnit(Unit unit, qint64 amount, bool plussign=false); + static QString formatWithUnit(int unit, qint64 amount, bool plussign=false); // Parse string to coin amount - static bool parse(Unit unit, const QString &value, qint64 *val_out); + static bool parse(int unit, const QString &value, qint64 *val_out); + /// AbstractListModel implementation + enum { + // Unit identifier + UnitRole = Qt::UserRole + } RoleIndex; + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; +private: + QList unitlist; }; +typedef BitcoinUnits::Unit BitcoinUnit; #endif // BITCOINUNITS_H diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 4f3a82d319..94f7abacf2 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -103,7 +103,6 @@ OptionsDialog::OptionsDialog(QWidget *parent): connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApply())); /* Event bindings */ - qDebug() << "setup"; connect(contents_widget, SIGNAL(currentRowChanged(int)), this, SLOT(changePage(int))); connect(buttonbox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(okClicked())); connect(buttonbox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(cancelClicked())); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 8f285c6446..896170975e 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -36,7 +36,7 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const case ProxyPort: return QVariant(QString::fromStdString(addrProxy.ToStringPort())); case Fee: - return QVariant(QString::fromStdString(FormatMoney(nTransactionFee))); + return QVariant(nTransactionFee); default: return QVariant(); } @@ -104,16 +104,8 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in } break; case Fee: { - int64 retval; - if(ParseMoney(value.toString().toStdString(), retval)) - { - nTransactionFee = retval; - walletdb.WriteSetting("nTransactionFee", nTransactionFee); - } - else - { - successful = false; // Parse error - } + nTransactionFee = value.toLongLong(); + walletdb.WriteSetting("nTransactionFee", nTransactionFee); } break; default: diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index bdb797a2de..4ba44dc23f 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -18,14 +18,14 @@ public: explicit OptionsModel(CWallet *wallet, QObject *parent = 0); enum OptionID { - StartAtStartup, - MinimizeToTray, - MapPortUPnP, - MinimizeOnClose, - ConnectSOCKS4, - ProxyIP, - ProxyPort, - Fee, + StartAtStartup, // bool + MinimizeToTray, // bool + MapPortUPnP, // bool + MinimizeOnClose, // bool + ConnectSOCKS4, // bool + ProxyIP, // QString + ProxyPort, // QString + Fee, // qint64 OptionIDRowCount }; diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 9fc041111f..f3847f1693 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -87,6 +87,16 @@ bool SendCoinsEntry::validate() { retval = false; } + else + { + if(ui->payAmount->value() <= 0) + { + // Cannot send 0 coins or less + ui->payAmount->setValid(false); + retval = false; + } + } + if(!ui->payTo->hasAcceptableInput() || (model && !model->validateAddress(ui->payTo->text()))) @@ -104,7 +114,7 @@ SendCoinsRecipient SendCoinsEntry::getValue() rv.address = ui->payTo->text(); rv.label = ui->addAsLabel->text(); - BitcoinUnits::parse(BitcoinUnits::BTC, ui->payAmount->text(), &rv.amount); + rv.amount = ui->payAmount->value(); return rv; } -- cgit v1.2.3 From f2b10f6469dd53bbd51f045b1ae88dfb392fcd13 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 26 Jul 2011 13:11:28 +0200 Subject: refuse to format nor parse invalid units --- src/qt/bitcoinunits.cpp | 19 ++++++++++++++++++- src/qt/bitcoinunits.h | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 8414a759ee..7cd50232dd 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -17,6 +17,19 @@ QList BitcoinUnits::availableUnits() return unitlist; } +bool BitcoinUnits::valid(int unit) +{ + switch(unit) + { + case BTC: + case mBTC: + case uBTC: + return true; + default: + return false; + } +} + QString BitcoinUnits::name(int unit) { switch(unit) @@ -54,7 +67,7 @@ int BitcoinUnits::amountDigits(int unit) { switch(unit) { - case BTC: return 8; // 21,000,000 + case BTC: return 8; // 21,000,000 (# digits, without commas) case mBTC: return 11; // 21,000,000,000 case uBTC: return 14; // 21,000,000,000,000 default: return 0; @@ -76,6 +89,8 @@ QString BitcoinUnits::format(int unit, qint64 n, bool fPlus) { // Note: not using straight sprintf here because we do NOT want // localized number formatting. + if(!valid(unit)) + return QString(); // Refuse to format invalid unit qint64 coin = factor(unit); int num_decimals = decimals(unit); qint64 n_abs = (n > 0 ? n : -n); @@ -104,6 +119,8 @@ QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign) bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out) { + if(!valid(unit)) + return false; // Refuse to parse invalid unit int num_decimals = decimals(unit); QStringList parts = value.split("."); if(parts.size() != 2 || parts.at(1).size() > num_decimals) diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 18b623517c..4dfdbea044 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -22,6 +22,8 @@ public: /// Static API // Get list of units, for dropdown box static QList availableUnits(); + // Is unit ID valid? + static bool valid(int unit); // Short name static QString name(int unit); // Longer description -- cgit v1.2.3 From 83c8d678aaf01e24cfbfdad23ed31591a3666638 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 26 Jul 2011 13:31:59 +0200 Subject: Reset unit to default when clearing the field, to prevent confusion --- src/qt/bitcoinamountfield.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 7fe28f9a17..f1b4e9fdc3 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -66,6 +66,8 @@ void BitcoinAmountField::clear() { amount->clear(); decimals->clear(); + // TODO: set default based on configuration + unit->setCurrentIndex(0); } bool BitcoinAmountField::validate() -- cgit v1.2.3 From c0b892fee89cba7d1fda5fda2d2fc7e966643be1 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 26 Jul 2011 13:43:54 +0200 Subject: update readme --- README.rst | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 98add9f3c0..3ab370018f 100644 --- a/README.rst +++ b/README.rst @@ -16,26 +16,29 @@ This has been implemented: - Tabbed interface -- Overview page with current balance, unconfirmed balance, etc +- Overview page with current balance, unconfirmed balance, and such -- User friendly transaction list with status icons, real-time filtering and a context menu that allows editing and copying labels +- Better transaction list with status icons, real-time filtering and a context menu -- Asks for confirmation before sending coins +- Asks for confirmation before sending coins, for your own safety -- CSV export of transactions and address book +- CSV export of transactions and address book (for Excel bookkeeping) + +- Shows alternative icon when connected to testnet, so you never accidentally send real coins during testing -- Shows alternative icon when connected to testnet +- Shows a progress bar on initial block download, so that you don't have to wonder how many blocks it needs to download to be up to date -- Progress bar on initial block download +- Sendmany support, send to multiple recipients at the same time -- Sendmany support in UI (send to multiple recipients as well) +- Multiple unit support, can show subdivided bitcoins (uBTC, mBTC) for users that like large numbers + +- Support for English, German and Dutch languages This has to be done: - Start at system start -- Internationalization (convert WX language files) - +- Support more languages Build instructions =================== -- cgit v1.2.3 From 5326a312495462af58c8388885b9ba75ba22064a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 26 Jul 2011 16:54:32 +0200 Subject: make SetHash160 return a value (as specified in the function signature) --- src/base58.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/base58.h b/src/base58.h index 266412c861..04922c74d8 100644 --- a/src/base58.h +++ b/src/base58.h @@ -244,6 +244,7 @@ public: bool SetHash160(const uint160& hash160) { SetData(fTestNet ? 111 : 0, &hash160, 20); + return true; } bool SetPubKey(const std::vector& vchPubKey) -- cgit v1.2.3 From dd61035645aea85b393965b4a047ce7f1ad65960 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 26 Jul 2011 17:37:26 +0200 Subject: show amounts in bold in confirmation dialog --- src/qt/sendcoinsdialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 58f9422b27..d5f15e3663 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -71,7 +71,7 @@ void SendCoinsDialog::on_sendButton_clicked() QStringList formatted; foreach(const SendCoinsRecipient &rcp, recipients) { - formatted.append(tr("%1 to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), rcp.label, rcp.address)); + formatted.append(tr("%1 to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), rcp.label, rcp.address)); } QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), -- cgit v1.2.3 From 384625c1a62968ed84fd9664a2c819d2d8ffd575 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 27 Jul 2011 20:49:14 +0200 Subject: also accept numbers without dot/decimals for parsing, fixes transaction filter row --- src/qt/bitcoinunits.cpp | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 7cd50232dd..9a9a4890dc 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -119,17 +119,33 @@ QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign) bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out) { - if(!valid(unit)) - return false; // Refuse to parse invalid unit + if(!valid(unit) || value.isEmpty()) + return false; // Refuse to parse invalid unit or empty string int num_decimals = decimals(unit); QStringList parts = value.split("."); - if(parts.size() != 2 || parts.at(1).size() > num_decimals) - return false; // Max num decimals + + if(parts.size() > 2) + { + return false; // More than one dot + } + QString whole = parts[0]; + QString decimals; + + if(parts.size() > 1) + { + decimals = parts[1]; + } + if(decimals.size() > num_decimals) + { + return false; // Exceeds max precision + } bool ok = false; - QString str = parts[0] + parts[1].leftJustified(num_decimals, '0'); - if(str.size()>18) - return false; // Bounds check + QString str = whole + decimals.leftJustified(num_decimals, '0'); + if(str.size() > 18) + { + return false; // Longer numbers will exceed 63 bits + } qint64 retvalue = str.toLongLong(&ok); if(val_out) { -- cgit v1.2.3 From 7df001be9449bf99e720d6e750d282b77eda5a51 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 27 Jul 2011 20:54:10 +0200 Subject: normalize SIGNAL/SLOT signatures (http://marcmutz.wordpress.com/effective-qt/prefer-to-use-normalised-signalslot-signatures/) --- src/qt/bitcoingui.cpp | 6 +++--- src/qt/transactionview.cpp | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bd80e42d03..d1e7bb94fd 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -85,7 +85,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QVBoxLayout *vbox = new QVBoxLayout(); transactionView = new TransactionView(this); - connect(transactionView, SIGNAL(doubleClicked(const QModelIndex&)), transactionView, SLOT(showDetails())); + connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); vbox->addWidget(transactionView); transactionsPage = new QWidget(this); @@ -252,8 +252,8 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) sendCoinsPage->setModel(walletModel); // Balloon popup for new transaction - connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), - this, SLOT(incomingTransaction(const QModelIndex &, int, int))); + connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(incomingTransaction(QModelIndex,int,int))); } void BitcoinGUI::createTrayIcon() diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index f88b6fc123..9c38934ad5 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -120,15 +120,15 @@ TransactionView::TransactionView(QWidget *parent) : // Connect actions connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); - connect(addressWidget, SIGNAL(textChanged(const QString&)), this, SLOT(changedPrefix(const QString&))); - connect(amountWidget, SIGNAL(textChanged(const QString&)), this, SLOT(changedAmount(const QString&))); + connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); + connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); - connect(view, SIGNAL(doubleClicked(const QModelIndex&)), this, SIGNAL(doubleClicked(const QModelIndex&))); + connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex))); connect(view, - SIGNAL(customContextMenuRequested(const QPoint &)), + SIGNAL(customContextMenuRequested(QPoint)), this, - SLOT(contextualMenu(const QPoint &))); + SLOT(contextualMenu(QPoint))); connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress())); connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel())); -- cgit v1.2.3 From ee014e5b10f5f65820ff056311051ff49813b294 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 29 Jul 2011 14:36:35 +0200 Subject: Full support for other units, add configuration option for default unit (used when displaying amounts) --- bitcoin-qt.pro | 6 ++++-- src/qt/bitcoin.cpp | 6 ++++-- src/qt/bitcoinamountfield.cpp | 11 ++++++++--- src/qt/bitcoinamountfield.h | 8 +++++--- src/qt/bitcoingui.cpp | 2 +- src/qt/clientmodel.cpp | 6 ++---- src/qt/clientmodel.h | 4 +--- src/qt/optionsdialog.cpp | 14 +++++++++----- src/qt/optionsmodel.cpp | 20 +++++++++++++++++++- src/qt/optionsmodel.h | 4 ++++ src/qt/overviewpage.cpp | 21 ++++++++++++++++++--- src/qt/overviewpage.h | 4 ++++ src/qt/qvaluecombobox.cpp | 27 +++++++++++++++++++++++++++ src/qt/qvaluecombobox.h | 33 +++++++++++++++++++++++++++++++++ src/qt/sendcoinsentry.cpp | 5 +++++ src/qt/transactiontablemodel.cpp | 3 ++- src/qt/transactionview.cpp | 3 ++- src/qt/walletmodel.cpp | 5 ++--- src/qt/walletmodel.h | 2 +- 19 files changed, 151 insertions(+), 33 deletions(-) create mode 100644 src/qt/qvaluecombobox.cpp create mode 100644 src/qt/qvaluecombobox.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 250aeff92f..03cd592a15 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -89,7 +89,8 @@ HEADERS += src/qt/bitcoingui.h \ src/crypter.h \ src/qt/sendcoinsentry.h \ src/qt/qvalidatedlineedit.h \ - src/qt/bitcoinunits.h + src/qt/bitcoinunits.h \ + src/qt/qvaluecombobox.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -132,7 +133,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/crypter.cpp \ src/qt/sendcoinsentry.cpp \ src/qt/qvalidatedlineedit.cpp \ - src/qt/bitcoinunits.cpp + src/qt/bitcoinunits.cpp \ + src/qt/qvaluecombobox.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 63d1d70670..bc652d31b0 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -4,6 +4,7 @@ #include "bitcoingui.h" #include "clientmodel.h" #include "walletmodel.h" +#include "optionsmodel.h" #include "qtwin.h" #include "headers.h" @@ -118,8 +119,9 @@ int main(int argc, char *argv[]) // Put this in a block, so that BitcoinGUI is cleaned up properly before // calling shutdown. BitcoinGUI window; - ClientModel clientModel(pwalletMain); - WalletModel walletModel(pwalletMain); + OptionsModel optionsModel(pwalletMain); + ClientModel clientModel(&optionsModel); + WalletModel walletModel(pwalletMain, &optionsModel); guiref = &window; window.setClientModel(&clientModel); diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index f1b4e9fdc3..73498050ad 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -1,5 +1,6 @@ #include "bitcoinamountfield.h" #include "qvalidatedlineedit.h" +#include "qvaluecombobox.h" #include "bitcoinunits.h" #include @@ -8,6 +9,7 @@ #include #include #include +#include BitcoinAmountField::BitcoinAmountField(QWidget *parent): QWidget(parent), amount(0), decimals(0), currentUnit(-1) @@ -27,7 +29,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): layout->addWidget(amount); layout->addWidget(new QLabel(QString("."))); layout->addWidget(decimals); - unit = new QComboBox(this); + unit = new QValueComboBox(this); unit->setModel(new BitcoinUnits(this)); layout->addWidget(unit); layout->addStretch(1); @@ -78,7 +80,7 @@ bool BitcoinAmountField::validate() decimals->setValid(false); valid = false; } - if(!BitcoinUnits::parse(BitcoinUnits::BTC, text(), 0)) + if(!BitcoinUnits::parse(currentUnit, text(), 0)) { setValid(false); valid = false; @@ -169,6 +171,9 @@ void BitcoinAmountField::unitChanged(int idx) setText(""); } setValid(true); +} - +void BitcoinAmountField::setDisplayUnit(int newUnit) +{ + unit->setValue(newUnit); } diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 6e724d06ae..cc92159fed 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -5,7 +5,7 @@ QT_BEGIN_NAMESPACE class QValidatedLineEdit; -class QComboBox; +class QValueComboBox; QT_END_NAMESPACE // Coin amount entry widget with separate parts for whole @@ -13,7 +13,6 @@ QT_END_NAMESPACE class BitcoinAmountField: public QWidget { Q_OBJECT - //Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged USER true); Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY textChanged USER true); public: explicit BitcoinAmountField(QWidget *parent = 0); @@ -25,6 +24,9 @@ public: void setValid(bool valid); bool validate(); + // Change current unit + void setDisplayUnit(int unit); + // Make field empty and ready for new input void clear(); @@ -42,7 +44,7 @@ protected: private: QValidatedLineEdit *amount; QValidatedLineEdit *decimals; - QComboBox *unit; + QValueComboBox *unit; int currentUnit; void setText(const QString &text); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index d1e7bb94fd..655ae5ca77 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -463,7 +463,7 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int trayIcon->showMessage((amount)<0 ? tr("Sent transaction") : tr("Incoming transaction"), tr("Date: ") + date + "\n" + - tr("Amount: ") + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, amount, true) + "\n" + + tr("Amount: ") + BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true) + "\n" + tr("Type: ") + type + "\n" + tr("Address: ") + address + "\n", QSystemTrayIcon::Information); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index c147aa5a6e..5cf02eac7d 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -9,8 +9,8 @@ #include #include -ClientModel::ClientModel(CWallet *wallet, QObject *parent) : - QObject(parent), wallet(wallet), optionsModel(0), +ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : + QObject(parent), optionsModel(optionsModel), cachedNumConnections(0), cachedNumBlocks(0) { // Until signal notifications is built into the bitcoin core, @@ -18,8 +18,6 @@ ClientModel::ClientModel(CWallet *wallet, QObject *parent) : QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(MODEL_UPDATE_DELAY); - - optionsModel = new OptionsModel(wallet, this); } int ClientModel::getNumConnections() const diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index f7ad14c28b..544334e47a 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -19,7 +19,7 @@ class ClientModel : public QObject public: // The only reason that this constructor takes a wallet is because // the global client settings are stored in the main wallet. - explicit ClientModel(CWallet *wallet, QObject *parent = 0); + explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0); OptionsModel *getOptionsModel(); @@ -38,8 +38,6 @@ public: QString formatFullVersion() const; private: - CWallet *wallet; - OptionsModel *optionsModel; int cachedNumConnections; diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 94f7abacf2..a923f3ead5 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -3,6 +3,8 @@ #include "bitcoinamountfield.h" #include "monitoreddatamapper.h" #include "guiutil.h" +#include "bitcoinunits.h" +#include "qvaluecombobox.h" #include #include @@ -49,7 +51,7 @@ public: void setMapper(MonitoredDataMapper *mapper); private: - QLineEdit *unit; + QValueComboBox *unit; signals: public slots: @@ -72,7 +74,7 @@ OptionsDialog::OptionsDialog(QWidget *parent): pages_widget->addWidget(main_page); QListWidgetItem *item_display = new QListWidgetItem(tr("Display")); - //contents_widget->addItem(item_display); + contents_widget->addItem(item_display); display_page = new DisplayOptionsPage(this); pages_widget->addWidget(display_page); @@ -122,7 +124,6 @@ void OptionsDialog::setModel(OptionsModel *model) void OptionsDialog::changePage(int index) { - qDebug() << "page" << index; pages_widget->setCurrentIndex(index); } @@ -249,9 +250,11 @@ DisplayOptionsPage::DisplayOptionsPage(QWidget *parent): QVBoxLayout *layout = new QVBoxLayout(); QHBoxLayout *unit_hbox = new QHBoxLayout(); unit_hbox->addSpacing(18); - QLabel *unit_label = new QLabel(tr("&Unit: ")); + QLabel *unit_label = new QLabel(tr("&Unit to show amounts in: ")); unit_hbox->addWidget(unit_label); - unit = new QLineEdit(); + unit = new QValueComboBox(this); + unit->setModel(new BitcoinUnits(this)); + unit->setToolTip(tr("Choose the default subdivision unit to show in the interface, and when sending coins")); unit_label->setBuddy(unit); unit_hbox->addWidget(unit); @@ -264,4 +267,5 @@ DisplayOptionsPage::DisplayOptionsPage(QWidget *parent): void DisplayOptionsPage::setMapper(MonitoredDataMapper *mapper) { + mapper->addMapping(unit, OptionsModel::DisplayUnit); } diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 896170975e..d72a0e9e91 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -1,4 +1,5 @@ #include "optionsmodel.h" +#include "bitcoinunits.h" #include "headers.h" @@ -6,8 +7,12 @@ OptionsModel::OptionsModel(CWallet *wallet, QObject *parent) : QAbstractListModel(parent), - wallet(wallet) + wallet(wallet), + nDisplayUnit(BitcoinUnits::BTC) { + // Read our specific settings from the wallet db + CWalletDB walletdb(wallet->strWalletFile); + walletdb.ReadSetting("nDisplayUnit", nDisplayUnit); } int OptionsModel::rowCount(const QModelIndex & parent) const @@ -37,6 +42,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return QVariant(QString::fromStdString(addrProxy.ToStringPort())); case Fee: return QVariant(nTransactionFee); + case DisplayUnit: + return QVariant(nDisplayUnit); default: return QVariant(); } @@ -108,6 +115,12 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in walletdb.WriteSetting("nTransactionFee", nTransactionFee); } break; + case DisplayUnit: { + int unit = value.toInt(); + nDisplayUnit = unit; + walletdb.WriteSetting("nDisplayUnit", nDisplayUnit); + emit displayUnitChanged(unit); + } default: break; } @@ -131,3 +144,8 @@ bool OptionsModel::getMinimizeOnClose() { return fMinimizeOnClose; } + +int OptionsModel::getDisplayUnit() +{ + return nDisplayUnit; +} diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 4ba44dc23f..ed26f83d55 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -26,6 +26,7 @@ public: ProxyIP, // QString ProxyPort, // QString Fee, // qint64 + DisplayUnit, // BitcoinUnits::Unit OptionIDRowCount }; @@ -37,10 +38,13 @@ public: qint64 getTransactionFee(); bool getMinimizeToTray(); bool getMinimizeOnClose(); + int getDisplayUnit(); private: // Wallet stores persistent options CWallet *wallet; + int nDisplayUnit; signals: + void displayUnitChanged(int unit); public slots: diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 9515117c21..c04bbf6008 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -3,10 +3,15 @@ #include "walletmodel.h" #include "bitcoinunits.h" +#include "optionsmodel.h" + +#include OverviewPage::OverviewPage(QWidget *parent) : QWidget(parent), - ui(new Ui::OverviewPage) + ui(new Ui::OverviewPage), + currentBalance(-1), + currentUnconfirmedBalance(-1) { ui->setupUi(this); @@ -34,8 +39,11 @@ OverviewPage::~OverviewPage() void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance) { - ui->labelBalance->setText(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, balance)); - ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, unconfirmedBalance)); + int unit = model->getOptionsModel()->getDisplayUnit(); + currentBalance = balance; + currentUnconfirmedBalance = unconfirmedBalance; + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance)); + ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance)); } void OverviewPage::setNumTransactions(int count) @@ -54,4 +62,11 @@ void OverviewPage::setModel(WalletModel *model) setNumTransactions(model->getNumTransactions()); connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(displayUnitChanged())); +} + +void OverviewPage::displayUnitChanged() +{ + if(currentBalance != -1) + setBalance(currentBalance, currentUnconfirmedBalance); } diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index acf83c720f..c54dda323a 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -25,7 +25,11 @@ public slots: private: Ui::OverviewPage *ui; WalletModel *model; + qint64 currentBalance; + qint64 currentUnconfirmedBalance; +private slots: + void displayUnitChanged(); }; #endif // OVERVIEWPAGE_H diff --git a/src/qt/qvaluecombobox.cpp b/src/qt/qvaluecombobox.cpp new file mode 100644 index 0000000000..c0ad8c12e5 --- /dev/null +++ b/src/qt/qvaluecombobox.cpp @@ -0,0 +1,27 @@ +#include "qvaluecombobox.h" + +QValueComboBox::QValueComboBox(QWidget *parent) : + QComboBox(parent), role(Qt::UserRole) +{ + connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(handleSelectionChanged(int))); +} + +int QValueComboBox::value() const +{ + return itemData(currentIndex(), role).toInt(); +} + +void QValueComboBox::setValue(int value) +{ + setCurrentIndex(findData(value, role)); +} + +void QValueComboBox::setRole(int role) +{ + this->role = role; +} + +void QValueComboBox::handleSelectionChanged(int idx) +{ + emit valueChanged(); +} diff --git a/src/qt/qvaluecombobox.h b/src/qt/qvaluecombobox.h new file mode 100644 index 0000000000..2a3533da9e --- /dev/null +++ b/src/qt/qvaluecombobox.h @@ -0,0 +1,33 @@ +#ifndef QVALUECOMBOBOX_H +#define QVALUECOMBOBOX_H + +#include + +// QComboBox that can be used with QDataWidgetMapper to select +// ordinal values from a model. +class QValueComboBox : public QComboBox +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged USER true); +public: + explicit QValueComboBox(QWidget *parent = 0); + + int value() const; + void setValue(int value); + + // Model role to use as value + void setRole(int role); + +signals: + void valueChanged(); + +public slots: + +private: + int role; + +private slots: + void handleSelectionChanged(int idx); +}; + +#endif // QVALUECOMBOBOX_H diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index f3847f1693..abdbc81bdc 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -4,6 +4,7 @@ #include "bitcoinunits.h" #include "addressbookpage.h" #include "walletmodel.h" +#include "optionsmodel.h" #include "addresstablemodel.h" #include "qapplication.h" @@ -71,6 +72,10 @@ void SendCoinsEntry::clear() ui->addAsLabel->clear(); ui->payAmount->clear(); ui->payTo->setFocus(); + if(model) + { + ui->payAmount->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); + } } void SendCoinsEntry::on_deleteButton_clicked() diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 2b8fe0b471..99f2d5806e 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -4,6 +4,7 @@ #include "guiconstants.h" #include "transactiondesc.h" #include "walletmodel.h" +#include "optionsmodel.h" #include "addresstablemodel.h" #include "bitcoinunits.h" @@ -398,7 +399,7 @@ QVariant TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx) QVariant TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const { - QString str = BitcoinUnits::format(BitcoinUnits::BTC, wtx->credit + wtx->debit); + QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit); if(showUnconfirmed) { if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 9c38934ad5..12fdc947d1 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -9,6 +9,7 @@ #include "csvmodelwriter.h" #include "transactiondescdialog.h" #include "editaddressdialog.h" +#include "optionsmodel.h" #include #include @@ -227,7 +228,7 @@ void TransactionView::changedPrefix(const QString &prefix) void TransactionView::changedAmount(const QString &amount) { qint64 amount_parsed = 0; - if(BitcoinUnits::parse(BitcoinUnits::BTC, amount, &amount_parsed)) + if(BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amount, &amount_parsed)) { transactionProxyModel->setMinAmount(amount_parsed); } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 732472c1a9..4d8d6fe450 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -9,8 +9,8 @@ #include #include -WalletModel::WalletModel(CWallet *wallet, QObject *parent) : - QObject(parent), wallet(wallet), optionsModel(0), addressTableModel(0), +WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : + QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0), transactionTableModel(0), cachedBalance(0), cachedUnconfirmedBalance(0), cachedNumTransactions(0) { @@ -20,7 +20,6 @@ WalletModel::WalletModel(CWallet *wallet, QObject *parent) : connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(MODEL_UPDATE_DELAY); - optionsModel = new OptionsModel(wallet, this); addressTableModel = new AddressTableModel(wallet, this); transactionTableModel = new TransactionTableModel(wallet, this); } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 668d44632f..c989e7fbbe 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -20,7 +20,7 @@ class WalletModel : public QObject { Q_OBJECT public: - explicit WalletModel(CWallet *wallet, QObject *parent = 0); + explicit WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0); enum StatusCode { -- cgit v1.2.3 From 3b59297b36f795e7cdaaab74daefa89cee6dd24d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 29 Jul 2011 16:16:12 +0200 Subject: Remove no longer valid comment --- src/qt/clientmodel.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 544334e47a..845ad10dec 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -17,8 +17,6 @@ class ClientModel : public QObject { Q_OBJECT public: - // The only reason that this constructor takes a wallet is because - // the global client settings are stored in the main wallet. explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0); OptionsModel *getOptionsModel(); -- cgit v1.2.3 From 19fba3cd24641407308c5c90abcff6a43365b55a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 29 Jul 2011 23:11:40 +0200 Subject: Make debug info more interesting (show SHA160 addresses for inputs) --- src/qt/transactiondesc.cpp | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 625b8e3f5e..9aeee5deba 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -271,36 +271,48 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) // if (fDebug) { - strHTML += "

debug print

"; + strHTML += "

Debug information

"; BOOST_FOREACH(const CTxIn& txin, wtx.vin) if(wallet->IsMine(txin)) - strHTML += "Debit: " + FormatMoney(-wallet->IsMine(txin)) + "
"; + strHTML += "Debit: " + FormatMoney(-wallet->GetDebit(txin)) + "
"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if(wallet->IsMine(txout)) - strHTML += "Credit: " + FormatMoney(wallet->IsMine(txout)) + "
"; + strHTML += "Credit: " + FormatMoney(wallet->GetCredit(txout)) + "
"; strHTML += "
Transaction:
"; strHTML += HtmlEscape(wtx.ToString(), true); - strHTML += "
Inputs:
"; + CTxDB txdb("r"); // To fetch source txouts + + strHTML += "
Inputs:"; + strHTML += "
    "; CRITICAL_BLOCK(wallet->cs_mapWallet) { BOOST_FOREACH(const CTxIn& txin, wtx.vin) { COutPoint prevout = txin.prevout; - map::iterator mi = wallet->mapWallet.find(prevout.hash); - if (mi != wallet->mapWallet.end()) + + CTransaction prev; + if(txdb.ReadDiskTx(prevout.hash, prev)) { - const CWalletTx& prev = (*mi).second; if (prevout.n < prev.vout.size()) { - strHTML += HtmlEscape(prev.ToString(), true); - strHTML += "    " + FormatTxStatus(prev) + ", "; - strHTML = strHTML + "IsMine=" + (wallet->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "
    "; + strHTML += "
  • "; + const CTxOut &vout = prev.vout[prevout.n]; + CBitcoinAddress address; + if (ExtractAddress(vout.scriptPubKey, 0, address)) + { + if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty()) + strHTML += wallet->mapAddressBook[address] + " "; + strHTML += address.ToString(); + } + strHTML = strHTML + " Amount=" + FormatMoney(vout.nValue); + strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) ? "true" : "false") + "
  • "; } } } } + strHTML += "
"; } -- cgit v1.2.3 From 1aafe34a0839153d7027fdd0251edc32bd8001aa Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 30 Jul 2011 17:01:05 +0200 Subject: Make dot in amount field more apparent --- src/qt/bitcoinamountfield.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 73498050ad..a9c89334ea 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -27,7 +27,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): QHBoxLayout *layout = new QHBoxLayout(this); layout->setSpacing(0); layout->addWidget(amount); - layout->addWidget(new QLabel(QString("."))); + layout->addWidget(new QLabel(QString("."))); layout->addWidget(decimals); unit = new QValueComboBox(this); unit->setModel(new BitcoinUnits(this)); -- cgit v1.2.3 From 2f5d380943c4f56114f81a6aee81a57579492103 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 30 Jul 2011 17:42:02 +0200 Subject: Hide addresses in transaction overview by default, they can be re-shown as a configuration option --- src/qt/guiconstants.h | 2 ++ src/qt/optionsdialog.cpp | 7 ++++++ src/qt/optionsmodel.cpp | 15 ++++++++++- src/qt/optionsmodel.h | 3 +++ src/qt/transactiontablemodel.cpp | 54 ++++++++++++++++++++++++++++------------ src/qt/transactiontablemodel.h | 5 ++-- 6 files changed, 67 insertions(+), 19 deletions(-) diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index deef3e3484..7fbf7fcd3a 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -11,5 +11,7 @@ static const int MODEL_UPDATE_DELAY = 500; #define COLOR_UNCONFIRMED QColor(128, 128, 128) /* Transaction list -- negative amount */ #define COLOR_NEGATIVE QColor(255, 0, 0) +/* Transaction list -- bare address (without label) */ +#define COLOR_BAREADDRESS QColor(140, 140, 140) #endif // GUICONSTANTS_H diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index a923f3ead5..e922209f01 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -52,6 +52,7 @@ public: void setMapper(MonitoredDataMapper *mapper); private: QValueComboBox *unit; + QCheckBox *display_addresses; signals: public slots: @@ -248,6 +249,7 @@ DisplayOptionsPage::DisplayOptionsPage(QWidget *parent): QWidget(parent) { QVBoxLayout *layout = new QVBoxLayout(); + QHBoxLayout *unit_hbox = new QHBoxLayout(); unit_hbox->addSpacing(18); QLabel *unit_label = new QLabel(tr("&Unit to show amounts in: ")); @@ -260,6 +262,10 @@ DisplayOptionsPage::DisplayOptionsPage(QWidget *parent): unit_hbox->addWidget(unit); layout->addLayout(unit_hbox); + + display_addresses = new QCheckBox(tr("Display addresses in transaction list"), this); + layout->addWidget(display_addresses); + layout->addStretch(); setLayout(layout); @@ -268,4 +274,5 @@ DisplayOptionsPage::DisplayOptionsPage(QWidget *parent): void DisplayOptionsPage::setMapper(MonitoredDataMapper *mapper) { mapper->addMapping(unit, OptionsModel::DisplayUnit); + mapper->addMapping(display_addresses, OptionsModel::DisplayAddresses); } diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index d72a0e9e91..4656ad08d5 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -8,11 +8,13 @@ OptionsModel::OptionsModel(CWallet *wallet, QObject *parent) : QAbstractListModel(parent), wallet(wallet), - nDisplayUnit(BitcoinUnits::BTC) + nDisplayUnit(BitcoinUnits::BTC), + bDisplayAddresses(false) { // Read our specific settings from the wallet db CWalletDB walletdb(wallet->strWalletFile); walletdb.ReadSetting("nDisplayUnit", nDisplayUnit); + walletdb.ReadSetting("bDisplayAddresses", bDisplayAddresses); } int OptionsModel::rowCount(const QModelIndex & parent) const @@ -44,6 +46,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return QVariant(nTransactionFee); case DisplayUnit: return QVariant(nDisplayUnit); + case DisplayAddresses: + return QVariant(bDisplayAddresses); default: return QVariant(); } @@ -121,6 +125,10 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in walletdb.WriteSetting("nDisplayUnit", nDisplayUnit); emit displayUnitChanged(unit); } + case DisplayAddresses: { + bDisplayAddresses = value.toBool(); + walletdb.WriteSetting("bDisplayAddresses", bDisplayAddresses); + } default: break; } @@ -149,3 +157,8 @@ int OptionsModel::getDisplayUnit() { return nDisplayUnit; } + +bool OptionsModel::getDisplayAddresses() +{ + return bDisplayAddresses; +} diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index ed26f83d55..7f489c5014 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -27,6 +27,7 @@ public: ProxyPort, // QString Fee, // qint64 DisplayUnit, // BitcoinUnits::Unit + DisplayAddresses, // bool OptionIDRowCount }; @@ -39,10 +40,12 @@ public: bool getMinimizeToTray(); bool getMinimizeOnClose(); int getDisplayUnit(); + bool getDisplayAddresses(); private: // Wallet stores persistent options CWallet *wallet; int nDisplayUnit; + bool bDisplayAddresses; signals: void displayUnitChanged(int unit); diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 99f2d5806e..27e85cebc5 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -322,21 +322,20 @@ QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const } } -/* Look up address in address book, if found return - address (label) - otherwise just return address +/* Look up address in address book, if found return label (address) + otherwise just return (address) */ -QString TransactionTableModel::lookupAddress(const std::string &address) const +QString TransactionTableModel::lookupAddress(const std::string &address, bool tooltip) const { QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address)); QString description; - if(label.isEmpty()) + if(!label.isEmpty()) { - description = QString::fromStdString(address); + description += label + QString(" "); } - else + if(label.isEmpty() || walletModel->getOptionsModel()->getDisplayAddresses() || tooltip) { - description = label + QString(" (") + QString::fromStdString(address) + QString(")"); + description += QString("(") + QString::fromStdString(address) + QString(")"); } return description; } @@ -369,20 +368,18 @@ QVariant TransactionTableModel::formatTxType(const TransactionRecord *wtx) const return QVariant(description); } -QVariant TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx) const +QVariant TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const { QString description; switch(wtx->type) { - case TransactionRecord::RecvWithAddress: - description = lookupAddress(wtx->address); - break; case TransactionRecord::RecvFromIP: description = QString::fromStdString(wtx->address); break; + case TransactionRecord::RecvWithAddress: case TransactionRecord::SendToAddress: - description = lookupAddress(wtx->address); + description = lookupAddress(wtx->address, tooltip); break; case TransactionRecord::SendToIP: description = QString::fromStdString(wtx->address); @@ -397,6 +394,24 @@ QVariant TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx) return QVariant(description); } +QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const +{ + // Show addresses without label in a less visible color + switch(wtx->type) + { + case TransactionRecord::RecvWithAddress: + case TransactionRecord::SendToAddress: + { + QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(wtx->address)); + if(label.isEmpty()) + return COLOR_BAREADDRESS; + } break; + default: + break; + } + return QVariant(); +} + QVariant TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const { QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit); @@ -478,7 +493,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case Type: return formatTxType(rec); case ToAddress: - return formatTxToAddress(rec); + return formatTxToAddress(rec, false); case Amount: return formatTxAmount(rec); } @@ -495,16 +510,19 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case Type: return formatTxType(rec); case ToAddress: - return formatTxToAddress(rec); + return formatTxToAddress(rec, true); case Amount: return rec->credit + rec->debit; } } else if (role == Qt::ToolTipRole) { - if(index.column() == Status) + switch(index.column()) { + case Status: return formatTxStatus(rec); + case ToAddress: + return formatTxToAddress(rec, true); } } else if (role == Qt::TextAlignmentRole) @@ -522,6 +540,10 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { return COLOR_NEGATIVE; } + if(index.column() == ToAddress) + { + return addressColor(rec); + } } else if (role == TypeRole) { diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 85bfeebcb3..3322ff4af1 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -59,11 +59,12 @@ private: QStringList columns; TransactionTablePriv *priv; - QString lookupAddress(const std::string &address) const; + QString lookupAddress(const std::string &address, bool tooltip) const; + QVariant addressColor(const TransactionRecord *wtx) const; QVariant formatTxStatus(const TransactionRecord *wtx) const; QVariant formatTxDate(const TransactionRecord *wtx) const; QVariant formatTxType(const TransactionRecord *wtx) const; - QVariant formatTxToAddress(const TransactionRecord *wtx) const; + QVariant formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const; QVariant formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true) const; QVariant formatTxDecoration(const TransactionRecord *wtx) const; -- cgit v1.2.3 From 04f38adf73b18ffb2bfb3e3cca76f0de951100b0 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 30 Jul 2011 18:48:05 +0200 Subject: Remove unused variable --- src/qt/addresstablemodel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index da086b2784..c8329bb2fd 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -46,7 +46,6 @@ struct AddressTablePriv { const CBitcoinAddress& address = item.first; const std::string& strName = item.second; - uint160 hash160; bool fMine = wallet->HaveKey(address); cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending, QString::fromStdString(strName), -- cgit v1.2.3 From f0ec774d9c3a95142d680a396dd4430bf301e78d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 30 Jul 2011 19:21:46 +0200 Subject: make sure address book model is up to date after sending coins --- src/qt/walletmodel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 4d8d6fe450..53555cc08a 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -159,6 +159,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QListSetAddressBookName(strAddress, rcp.label.toStdString()); } } + addressTableModel->updateList(); return SendCoinsReturn(OK, 0, hex); } -- cgit v1.2.3 From a5e1325879de3b7dbe604da574f9962408bc7575 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 30 Jul 2011 19:28:41 +0200 Subject: comment update --- src/qt/walletmodel.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 53555cc08a..d8139e9f09 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -159,6 +159,8 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QListSetAddressBookName(strAddress, rcp.label.toStdString()); } } + + // Update our model of the address table addressTableModel->updateList(); return SendCoinsReturn(OK, 0, hex); -- cgit v1.2.3 From dedf83a19bd0a021a937de47316a5e93d4062f15 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 31 Jul 2011 12:56:46 +0200 Subject: Properly html-escape labels --- src/qt/sendcoinsdialog.cpp | 4 ++-- src/qt/transactiondesc.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index d5f15e3663..54cae21a1b 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include SendCoinsDialog::SendCoinsDialog(QWidget *parent) : QDialog(parent), @@ -71,7 +71,7 @@ void SendCoinsDialog::on_sendButton_clicked() QStringList formatted; foreach(const SendCoinsRecipient &rcp, recipients) { - formatted.append(tr("%1 to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), rcp.label, rcp.address)); + formatted.append(tr("%1 to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), Qt::escape(rcp.label), rcp.address)); } QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 9aeee5deba..88dc2d8d67 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -134,7 +134,7 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += _("To: "); strHTML += HtmlEscape(address.ToString()); if (!wallet->mapAddressBook[address].empty()) - strHTML += _(" (yours, label: ") + wallet->mapAddressBook[address] + ")"; + strHTML += _(" (yours, label: ") + HtmlEscape(wallet->mapAddressBook[address]) + ")"; else strHTML += _(" (yours)"); strHTML += "
"; @@ -157,7 +157,7 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strAddress = wtx.mapValue["to"]; strHTML += _("To: "); if (wallet->mapAddressBook.count(strAddress) && !wallet->mapAddressBook[strAddress].empty()) - strHTML += wallet->mapAddressBook[strAddress] + " "; + strHTML += HtmlEscape(wallet->mapAddressBook[strAddress]) + " "; strHTML += HtmlEscape(strAddress) + "
"; } @@ -215,8 +215,8 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) { strHTML += _("To: "); if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty()) - strHTML += wallet->mapAddressBook[address] + " "; - strHTML += address.ToString(); + strHTML += HtmlEscape(wallet->mapAddressBook[address]) + " "; + strHTML += HtmlEscape(address.ToString()); strHTML += "
"; } } @@ -303,7 +303,7 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) if (ExtractAddress(vout.scriptPubKey, 0, address)) { if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty()) - strHTML += wallet->mapAddressBook[address] + " "; + strHTML += HtmlEscape(wallet->mapAddressBook[address]) + " "; strHTML += address.ToString(); } strHTML = strHTML + " Amount=" + FormatMoney(vout.nValue); -- cgit v1.2.3 From 05bcf7089e0da090db0b09a35b25f7a87c8ca1dd Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 31 Jul 2011 17:05:34 +0200 Subject: address icons --- doc/assets-attribution.txt | 3 +- src/qt/bitcoin.qrc | 4 + src/qt/res/icons/bitcoin.svg | 115 ----------------- src/qt/res/icons/clock1.svg | 261 ------------------------------------- src/qt/res/icons/clock2.svg | 262 -------------------------------------- src/qt/res/icons/clock3.svg | 261 ------------------------------------- src/qt/res/icons/clock4.svg | 261 ------------------------------------- src/qt/res/icons/clock5.svg | 262 -------------------------------------- src/qt/res/icons/clock_green.svg | 262 -------------------------------------- src/qt/res/icons/questionmark.svg | 159 ----------------------- src/qt/res/icons/tx_inout.png | Bin 0 -> 631 bytes src/qt/res/icons/tx_input.png | Bin 0 -> 594 bytes src/qt/res/icons/tx_mined.png | Bin 0 -> 754 bytes src/qt/res/icons/tx_output.png | Bin 0 -> 593 bytes src/qt/res/src/bitcoin.svg | 115 +++++++++++++++++ src/qt/res/src/clock1.svg | 261 +++++++++++++++++++++++++++++++++++++ src/qt/res/src/clock2.svg | 262 ++++++++++++++++++++++++++++++++++++++ src/qt/res/src/clock3.svg | 261 +++++++++++++++++++++++++++++++++++++ src/qt/res/src/clock4.svg | 261 +++++++++++++++++++++++++++++++++++++ src/qt/res/src/clock5.svg | 262 ++++++++++++++++++++++++++++++++++++++ src/qt/res/src/clock_green.svg | 262 ++++++++++++++++++++++++++++++++++++++ src/qt/res/src/inout.svg | 122 ++++++++++++++++++ src/qt/res/src/questionmark.svg | 159 +++++++++++++++++++++++ src/qt/transactiontablemodel.cpp | 69 +++++----- src/qt/transactiontablemodel.h | 5 +- 25 files changed, 2012 insertions(+), 1877 deletions(-) delete mode 100644 src/qt/res/icons/bitcoin.svg delete mode 100644 src/qt/res/icons/clock1.svg delete mode 100644 src/qt/res/icons/clock2.svg delete mode 100644 src/qt/res/icons/clock3.svg delete mode 100644 src/qt/res/icons/clock4.svg delete mode 100644 src/qt/res/icons/clock5.svg delete mode 100644 src/qt/res/icons/clock_green.svg delete mode 100644 src/qt/res/icons/questionmark.svg create mode 100644 src/qt/res/icons/tx_inout.png create mode 100644 src/qt/res/icons/tx_input.png create mode 100644 src/qt/res/icons/tx_mined.png create mode 100644 src/qt/res/icons/tx_output.png create mode 100644 src/qt/res/src/bitcoin.svg create mode 100644 src/qt/res/src/clock1.svg create mode 100644 src/qt/res/src/clock2.svg create mode 100644 src/qt/res/src/clock3.svg create mode 100644 src/qt/res/src/clock4.svg create mode 100644 src/qt/res/src/clock5.svg create mode 100644 src/qt/res/src/clock_green.svg create mode 100644 src/qt/res/src/inout.svg create mode 100644 src/qt/res/src/questionmark.svg diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index c03233790e..0a719f1715 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -1,4 +1,5 @@ -Icon: src/qt/res/icons/clock*.png +Icon: src/qt/res/icons/clock*.png, src/qt/res/icons/tx*.png, + src/qt/res/src/*.svg Designer: Wladimir van der Laan License: Creative Commons Attribution diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 5199a8ea31..8d4bab5462 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -30,6 +30,10 @@ res/icons/export.png res/icons/synced.png res/icons/remove.png + res/icons/tx_mined.png + res/icons/tx_input.png + res/icons/tx_output.png + res/icons/tx_inout.png res/images/about.png diff --git a/src/qt/res/icons/bitcoin.svg b/src/qt/res/icons/bitcoin.svg deleted file mode 100644 index 96f10178a2..0000000000 --- a/src/qt/res/icons/bitcoin.svg +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/qt/res/icons/clock1.svg b/src/qt/res/icons/clock1.svg deleted file mode 100644 index 793dc7f91c..0000000000 --- a/src/qt/res/icons/clock1.svg +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/qt/res/icons/clock2.svg b/src/qt/res/icons/clock2.svg deleted file mode 100644 index 6a78adf700..0000000000 --- a/src/qt/res/icons/clock2.svg +++ /dev/null @@ -1,262 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/qt/res/icons/clock3.svg b/src/qt/res/icons/clock3.svg deleted file mode 100644 index 09ccc2549f..0000000000 --- a/src/qt/res/icons/clock3.svg +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/qt/res/icons/clock4.svg b/src/qt/res/icons/clock4.svg deleted file mode 100644 index 7d9dc37acb..0000000000 --- a/src/qt/res/icons/clock4.svg +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/qt/res/icons/clock5.svg b/src/qt/res/icons/clock5.svg deleted file mode 100644 index 9fd58d9d97..0000000000 --- a/src/qt/res/icons/clock5.svg +++ /dev/null @@ -1,262 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/qt/res/icons/clock_green.svg b/src/qt/res/icons/clock_green.svg deleted file mode 100644 index e31f0e7995..0000000000 --- a/src/qt/res/icons/clock_green.svg +++ /dev/null @@ -1,262 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/qt/res/icons/questionmark.svg b/src/qt/res/icons/questionmark.svg deleted file mode 100644 index c03c159a5f..0000000000 --- a/src/qt/res/icons/questionmark.svg +++ /dev/null @@ -1,159 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - ? - ? - - - ? - - diff --git a/src/qt/res/icons/tx_inout.png b/src/qt/res/icons/tx_inout.png new file mode 100644 index 0000000000..ff6bb1c5c3 Binary files /dev/null and b/src/qt/res/icons/tx_inout.png differ diff --git a/src/qt/res/icons/tx_input.png b/src/qt/res/icons/tx_input.png new file mode 100644 index 0000000000..1673d06ad3 Binary files /dev/null and b/src/qt/res/icons/tx_input.png differ diff --git a/src/qt/res/icons/tx_mined.png b/src/qt/res/icons/tx_mined.png new file mode 100644 index 0000000000..a336868e8a Binary files /dev/null and b/src/qt/res/icons/tx_mined.png differ diff --git a/src/qt/res/icons/tx_output.png b/src/qt/res/icons/tx_output.png new file mode 100644 index 0000000000..0617239e14 Binary files /dev/null and b/src/qt/res/icons/tx_output.png differ diff --git a/src/qt/res/src/bitcoin.svg b/src/qt/res/src/bitcoin.svg new file mode 100644 index 0000000000..96f10178a2 --- /dev/null +++ b/src/qt/res/src/bitcoin.svg @@ -0,0 +1,115 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock1.svg b/src/qt/res/src/clock1.svg new file mode 100644 index 0000000000..793dc7f91c --- /dev/null +++ b/src/qt/res/src/clock1.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock2.svg b/src/qt/res/src/clock2.svg new file mode 100644 index 0000000000..6a78adf700 --- /dev/null +++ b/src/qt/res/src/clock2.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock3.svg b/src/qt/res/src/clock3.svg new file mode 100644 index 0000000000..09ccc2549f --- /dev/null +++ b/src/qt/res/src/clock3.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock4.svg b/src/qt/res/src/clock4.svg new file mode 100644 index 0000000000..7d9dc37acb --- /dev/null +++ b/src/qt/res/src/clock4.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock5.svg b/src/qt/res/src/clock5.svg new file mode 100644 index 0000000000..9fd58d9d97 --- /dev/null +++ b/src/qt/res/src/clock5.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock_green.svg b/src/qt/res/src/clock_green.svg new file mode 100644 index 0000000000..e31f0e7995 --- /dev/null +++ b/src/qt/res/src/clock_green.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/inout.svg b/src/qt/res/src/inout.svg new file mode 100644 index 0000000000..bfab8ef6ab --- /dev/null +++ b/src/qt/res/src/inout.svg @@ -0,0 +1,122 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/questionmark.svg b/src/qt/res/src/questionmark.svg new file mode 100644 index 0000000000..c03c159a5f --- /dev/null +++ b/src/qt/res/src/questionmark.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + ? + ? + + + ? + + diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 27e85cebc5..58ec2c7ace 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -340,58 +340,62 @@ QString TransactionTableModel::lookupAddress(const std::string &address, bool to return description; } -QVariant TransactionTableModel::formatTxType(const TransactionRecord *wtx) const +QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const { - QString description; - switch(wtx->type) { case TransactionRecord::RecvWithAddress: - description = tr("Received with"); - break; + return tr("Received with"); case TransactionRecord::RecvFromIP: - description = tr("Received from IP"); - break; + return tr("Received from IP"); case TransactionRecord::SendToAddress: - description = tr("Sent to"); - break; + return tr("Sent to"); case TransactionRecord::SendToIP: - description = tr("Sent to IP"); - break; + return tr("Sent to IP"); case TransactionRecord::SendToSelf: - description = tr("Payment to yourself"); - break; + return tr("Payment to yourself"); case TransactionRecord::Generated: - description = tr("Mined"); - break; + return tr("Mined"); + default: + return QString(); } - return QVariant(description); } -QVariant TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const +QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx) const { - QString description; + switch(wtx->type) + { + case TransactionRecord::Generated: + return QIcon(":/icons/tx_mined"); + case TransactionRecord::RecvWithAddress: + case TransactionRecord::RecvFromIP: + return QIcon(":/icons/tx_input"); + case TransactionRecord::SendToAddress: + case TransactionRecord::SendToIP: + return QIcon(":/icons/tx_output"); + default: + return QIcon(":/icons/tx_inout"); + } + return QVariant(); +} +QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const +{ switch(wtx->type) { case TransactionRecord::RecvFromIP: - description = QString::fromStdString(wtx->address); - break; + return QString::fromStdString(wtx->address); case TransactionRecord::RecvWithAddress: case TransactionRecord::SendToAddress: - description = lookupAddress(wtx->address, tooltip); - break; + return lookupAddress(wtx->address, tooltip); case TransactionRecord::SendToIP: - description = QString::fromStdString(wtx->address); - break; + return QString::fromStdString(wtx->address); case TransactionRecord::SendToSelf: - description = QString(); - break; + return QString(); case TransactionRecord::Generated: - description = QString(); - break; + default: + return QString(); } - return QVariant(description); } QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const @@ -478,9 +482,12 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const if(role == Qt::DecorationRole) { - if(index.column() == Status) + switch(index.column()) { + case Status: return formatTxDecoration(rec); + case ToAddress: + return txAddressDecoration(rec); } } else if(role == Qt::DisplayRole) @@ -522,7 +529,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case Status: return formatTxStatus(rec); case ToAddress: - return formatTxToAddress(rec, true); + return formatTxType(rec) + QString(" ") + formatTxToAddress(rec, true); } } else if (role == Qt::TextAlignmentRole) diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 3322ff4af1..71b0644110 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -63,10 +63,11 @@ private: QVariant addressColor(const TransactionRecord *wtx) const; QVariant formatTxStatus(const TransactionRecord *wtx) const; QVariant formatTxDate(const TransactionRecord *wtx) const; - QVariant formatTxType(const TransactionRecord *wtx) const; - QVariant formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const; + QString formatTxType(const TransactionRecord *wtx) const; + QString formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const; QVariant formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true) const; QVariant formatTxDecoration(const TransactionRecord *wtx) const; + QVariant txAddressDecoration(const TransactionRecord *wtx) const; private slots: void update(); -- cgit v1.2.3 From d8f5c59a594f25d2e03616284068a1034fc5875b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 31 Jul 2011 17:43:46 +0200 Subject: show n/a for mined transactions (and send to self) instead of empty field --- src/qt/transactiontablemodel.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 58ec2c7ace..1606df9f5a 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -391,10 +391,9 @@ QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, b case TransactionRecord::SendToIP: return QString::fromStdString(wtx->address); case TransactionRecord::SendToSelf: - return QString(); case TransactionRecord::Generated: default: - return QString(); + return tr("(n/a)"); } } @@ -410,6 +409,9 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const if(label.isEmpty()) return COLOR_BAREADDRESS; } break; + case TransactionRecord::SendToSelf: + case TransactionRecord::Generated: + return COLOR_BAREADDRESS; default: break; } -- cgit v1.2.3 From 10d680cff4d0086bd9621438e5ac04740a38d106 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 2 Aug 2011 21:48:59 +0200 Subject: add splash screen --- src/init.cpp | 5 +++++ src/qt/bitcoin.cpp | 20 ++++++++++++++++++++ src/qt/bitcoin.qrc | 1 + src/qt/res/images/splash2.jpg | Bin 0 -> 5816 bytes src/qtui.h | 1 + 5 files changed, 27 insertions(+) create mode 100644 src/qt/res/images/splash2.jpg diff --git a/src/init.cpp b/src/init.cpp index fcb0c83340..c328ca37d7 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -370,18 +370,21 @@ bool AppInit2(int argc, char* argv[]) strErrors = ""; int64 nStart; + InitMessage("Loading addresses..."); printf("Loading addresses...\n"); nStart = GetTimeMillis(); if (!LoadAddresses()) strErrors += _("Error loading addr.dat \n"); printf(" addresses %15"PRI64d"ms\n", GetTimeMillis() - nStart); + InitMessage("Loading block index..."); printf("Loading block index...\n"); nStart = GetTimeMillis(); if (!LoadBlockIndex()) strErrors += _("Error loading blkindex.dat \n"); printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart); + InitMessage("Loading wallet..."); printf("Loading wallet...\n"); nStart = GetTimeMillis(); bool fFirstRun; @@ -412,12 +415,14 @@ bool AppInit2(int argc, char* argv[]) } if (pindexBest != pindexRescan) { + InitMessage("Rescanning..."); printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); nStart = GetTimeMillis(); pwalletMain->ScanForWalletTransactions(pindexRescan, true); printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); } + InitMessage("Done loading"); printf("Done loading\n"); //// debug print diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index bc652d31b0..749afb4bc2 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -15,9 +15,12 @@ #include #include #include +#include +#include // Need a global reference for the notifications to find the GUI BitcoinGUI *guiref; +QSplashScreen *splashref; int MyMessageBox(const std::string& message, const std::string& caption, int style, wxWindow* parent, int x, int y) { @@ -90,6 +93,15 @@ void MainFrameRepaint() { } +void InitMessage(const std::string &message) +{ + if(splashref) + { + splashref->showMessage(QString::fromStdString(message), Qt::AlignBottom, QColor(255,255,255)); + QApplication::instance()->processEvents(); + } +} + /* Translate string to current locale using Qt. */ @@ -109,6 +121,13 @@ int main(int argc, char *argv[]) translator.load("bitcoin_"+locale); app.installTranslator(&translator); + QSplashScreen splash(QPixmap(":/images/splash"), Qt::WindowStaysOnTopHint); + splash.show(); + splash.setAutoFillBackground(true); + splashref = &splash; + + app.processEvents(); + app.setQuitOnLastWindowClosed(false); try @@ -119,6 +138,7 @@ int main(int argc, char *argv[]) // Put this in a block, so that BitcoinGUI is cleaned up properly before // calling shutdown. BitcoinGUI window; + splash.finish(&window); OptionsModel optionsModel(pwalletMain); ClientModel clientModel(&optionsModel); WalletModel walletModel(pwalletMain, &optionsModel); diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 8d4bab5462..e56536199b 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -37,6 +37,7 @@ res/images/about.png + res/images/splash2.jpg res/movies/update_spinner.mng diff --git a/src/qt/res/images/splash2.jpg b/src/qt/res/images/splash2.jpg new file mode 100644 index 0000000000..3846e6f68d Binary files /dev/null and b/src/qt/res/images/splash2.jpg differ diff --git a/src/qtui.h b/src/qtui.h index a3b9eb0148..17fc44e94b 100644 --- a/src/qtui.h +++ b/src/qtui.h @@ -43,6 +43,7 @@ extern bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, extern void CalledSetStatusBar(const std::string& strText, int nField); extern void UIThreadCall(boost::function0 fn); extern void MainFrameRepaint(); +extern void InitMessage(const std::string &message); extern std::string _(const char* psz); #endif -- cgit v1.2.3 From 5762295ec3ce77cda5bfe68cac4cff83ddf4603b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 2 Aug 2011 22:03:41 +0200 Subject: update readme and splash screen text --- README.rst | 4 ++++ src/qt/bitcoin.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 3ab370018f..afe8c72036 100644 --- a/README.rst +++ b/README.rst @@ -14,6 +14,8 @@ This has been implemented: - All functionality of the original client, including taskbar icon/menu +- Splash screen + - Tabbed interface - Overview page with current balance, unconfirmed balance, and such @@ -34,6 +36,8 @@ This has been implemented: - Support for English, German and Dutch languages +- Address books and transaction table can be sorted by any column + This has to be done: - Start at system start diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 749afb4bc2..8ae3762f68 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -97,7 +97,7 @@ void InitMessage(const std::string &message) { if(splashref) { - splashref->showMessage(QString::fromStdString(message), Qt::AlignBottom, QColor(255,255,255)); + splashref->showMessage(QString::fromStdString(message), Qt::AlignBottom|Qt::AlignHCenter, QColor(255,255,200)); QApplication::instance()->processEvents(); } } -- cgit v1.2.3 From 6e903b0b3250b40c918af55acafa8ebf6afd9161 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 2 Aug 2011 22:08:59 +0200 Subject: add attribution for wallet image --- doc/assets-attribution.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index 0a719f1715..e4a00fa87a 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -58,3 +58,11 @@ Icon Pack: Kids Designer: Everaldo (Everaldo Coelho) License: GNU/GPL Site: http://findicons.com/icon/17102/reload?id=17102 + +Image: src/qt/res/images/splash2.jpg (Wallet image) +Designer: Crobbo (forum) +Site: https://bitcointalk.org/index.php?topic=32273.0 +License: Public domain + + + -- cgit v1.2.3 From 2566b30c38cb1b1955118216c687a8a1063cc853 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 3 Aug 2011 14:06:13 +0200 Subject: make amount field the same width as decimals field --- src/qt/bitcoinamountfield.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index a9c89334ea..e25cefad6e 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -18,7 +18,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): amount->setValidator(new QRegExpValidator(QRegExp("[0-9]*"), this)); amount->setAlignment(Qt::AlignRight|Qt::AlignVCenter); amount->installEventFilter(this); - amount->setMaximumWidth(100); + amount->setMaximumWidth(75); decimals = new QValidatedLineEdit(this); decimals->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this)); decimals->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); -- cgit v1.2.3 From a99ac8d3f483d8a839ea3c4e0f400eaa64338acd Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 3 Aug 2011 20:52:18 +0200 Subject: show last few transactions on overview page --- src/qt/forms/overviewpage.ui | 204 +++++++++++++++++++++++++------------- src/qt/guiutil.cpp | 6 +- src/qt/guiutil.h | 2 + src/qt/overviewpage.cpp | 98 +++++++++++++++++- src/qt/overviewpage.h | 3 + src/qt/res/icons/tx_inout.png | Bin 631 -> 2442 bytes src/qt/res/icons/tx_input.png | Bin 594 -> 2152 bytes src/qt/res/icons/tx_mined.png | Bin 754 -> 3287 bytes src/qt/res/icons/tx_output.png | Bin 593 -> 2129 bytes src/qt/transactionfilterproxy.cpp | 20 +++- src/qt/transactionfilterproxy.h | 5 + src/qt/transactiontablemodel.cpp | 4 + src/qt/transactiontablemodel.h | 2 + 13 files changed, 268 insertions(+), 76 deletions(-) diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index d8362a7b25..cc67fae533 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -13,82 +13,146 @@ Form - + - - - QFrame::StyledPanel - - - QFrame::Raised - - - - QFormLayout::AllNonFixedFieldsGrow - - - 12 - - - 12 - - - - - Balance: + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + QFormLayout::AllNonFixedFieldsGrow - - - - - - 123.456 BTC + + 12 - - - - - - Number of transactions: + + 12 - - - - - - 0 - - - - - - - Unconfirmed: - - - - - - - 0 BTC - - - - - + + + + Balance: + + + + + + + 123.456 BTC + + + + + + + Number of transactions: + + + + + + + 0 + + + + + + + Unconfirmed: + + + + + + + 0 BTC + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Wallet</span></p></body></html> + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + +
- - - Qt::Vertical - - - - 20 - 40 - - - + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + <b>Recent transactions</b> + + + + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + +
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 308a6ba9bc..ece069072f 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -11,7 +11,11 @@ QString GUIUtil::DateTimeStr(qint64 nTime) { - QDateTime date = QDateTime::fromTime_t((qint32)nTime); + return DateTimeStr(QDateTime::fromTime_t((qint32)nTime)); +} + +QString GUIUtil::DateTimeStr(const QDateTime &date) +{ return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm"); } diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 26a1a03712..fb5c575ab5 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -7,12 +7,14 @@ QT_BEGIN_NAMESPACE class QFont; class QLineEdit; class QWidget; +class QDateTime; QT_END_NAMESPACE class GUIUtil { public: static QString DateTimeStr(qint64 nTime); + static QString DateTimeStr(const QDateTime &datetime); // Render bitcoin addresses in monospace font static QFont bitcoinAddressFont(); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index c04bbf6008..f79e1f3e9a 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -4,14 +4,87 @@ #include "walletmodel.h" #include "bitcoinunits.h" #include "optionsmodel.h" +#include "transactiontablemodel.h" +#include "transactionfilterproxy.h" +#include "guiutil.h" +#include "guiconstants.h" #include +#include +#include + +#define DECORATION_SIZE 64 +class TxViewDelegate : public QItemDelegate +{ + //Q_OBJECT +public: + TxViewDelegate(): QItemDelegate(), unit(BitcoinUnits::BTC) + { + + } + + inline void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index ) const + { + //QItemDelegate::paint(painter, option, index); + painter->save(); + + QIcon icon = qvariant_cast(index.data(Qt::DecorationRole)); + QRect mainRect = option.rect; + QRect decorationRect(mainRect.topLeft(), QSize(DECORATION_SIZE, DECORATION_SIZE)); + int xspace = DECORATION_SIZE + 8; + int ypad = 6; + int halfheight = (mainRect.height() - 2*ypad)/2; + QRect amountRect(mainRect.left() + xspace, mainRect.top()+ypad, mainRect.width() - xspace, halfheight); + QRect addressRect(mainRect.left() + xspace, mainRect.top()+ypad+halfheight, mainRect.width() - xspace, halfheight); + icon.paint(painter, decorationRect); + + QDateTime date = index.data(TransactionTableModel::DateRole).toDateTime(); + QString address = index.data(Qt::DisplayRole).toString(); + qint64 amount = index.data(TransactionTableModel::AmountRole).toLongLong(); + bool confirmed = index.data(TransactionTableModel::ConfirmedRole).toBool(); + QVariant value = index.data(Qt::ForegroundRole); + QColor foreground = option.palette.color(QPalette::Text); + if(qVariantCanConvert(value)) + { + foreground = qvariant_cast(value); + } + + painter->setPen(foreground); + painter->drawText(addressRect, Qt::AlignLeft|Qt::AlignVCenter, address); + + if(amount < 0) + { + foreground = COLOR_NEGATIVE; + } + else + { + foreground = option.palette.color(QPalette::Text); + } + painter->setPen(foreground); + QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true); + if(!confirmed) + { + amountText = QString("[") + amountText + QString("]"); + } + painter->drawText(amountRect, Qt::AlignRight|Qt::AlignVCenter, amountText); + + painter->setPen(option.palette.color(QPalette::Text)); + painter->drawText(amountRect, Qt::AlignLeft|Qt::AlignVCenter, GUIUtil::DateTimeStr(date)); + + painter->restore(); + } + + int unit; + +}; OverviewPage::OverviewPage(QWidget *parent) : QWidget(parent), ui(new Ui::OverviewPage), currentBalance(-1), - currentUnconfirmedBalance(-1) + currentUnconfirmedBalance(-1), + txdelegate(new TxViewDelegate()) { ui->setupUi(this); @@ -27,9 +100,11 @@ OverviewPage::OverviewPage(QWidget *parent) : ui->labelNumTransactions->setToolTip(tr("Total number of transactions in wallet")); - // Overview page should show: - // Last received transaction(s) - // Last sent transaction(s) + // Recent transactions + ui->listTransactions->setStyleSheet("background:transparent"); + ui->listTransactions->setItemDelegate(txdelegate); + ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE)); + ui->listTransactions->setSelectionMode(QAbstractItemView::NoSelection); } OverviewPage::~OverviewPage() @@ -55,6 +130,18 @@ void OverviewPage::setModel(WalletModel *model) { this->model = model; + // Set up transaction list + + TransactionFilterProxy *filter = new TransactionFilterProxy(); + filter->setSourceModel(model->getTransactionTableModel()); + filter->setLimit(3); + filter->setDynamicSortFilter(true); + filter->setSortRole(Qt::EditRole); + filter->sort(TransactionTableModel::Status, Qt::DescendingOrder); + + ui->listTransactions->setModel(filter); + ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); + // Keep up to date with wallet setBalance(model->getBalance(), model->getUnconfirmedBalance()); connect(model, SIGNAL(balanceChanged(qint64, qint64)), this, SLOT(setBalance(qint64, qint64))); @@ -69,4 +156,7 @@ void OverviewPage::displayUnitChanged() { if(currentBalance != -1) setBalance(currentBalance, currentUnconfirmedBalance); + + txdelegate->unit = model->getOptionsModel()->getDisplayUnit(); + ui->listTransactions->update(); } diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index c54dda323a..2abddf16a6 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -7,6 +7,7 @@ namespace Ui { class OverviewPage; } class WalletModel; +class TxViewDelegate; class OverviewPage : public QWidget { @@ -28,6 +29,8 @@ private: qint64 currentBalance; qint64 currentUnconfirmedBalance; + TxViewDelegate *txdelegate; + private slots: void displayUnitChanged(); }; diff --git a/src/qt/res/icons/tx_inout.png b/src/qt/res/icons/tx_inout.png index ff6bb1c5c3..5f092f97aa 100644 Binary files a/src/qt/res/icons/tx_inout.png and b/src/qt/res/icons/tx_inout.png differ diff --git a/src/qt/res/icons/tx_input.png b/src/qt/res/icons/tx_input.png index 1673d06ad3..0f5fea3a84 100644 Binary files a/src/qt/res/icons/tx_input.png and b/src/qt/res/icons/tx_input.png differ diff --git a/src/qt/res/icons/tx_mined.png b/src/qt/res/icons/tx_mined.png index a336868e8a..613f30fecc 100644 Binary files a/src/qt/res/icons/tx_mined.png and b/src/qt/res/icons/tx_mined.png differ diff --git a/src/qt/res/icons/tx_output.png b/src/qt/res/icons/tx_output.png index 0617239e14..9ae39fb329 100644 Binary files a/src/qt/res/icons/tx_output.png and b/src/qt/res/icons/tx_output.png differ diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index cd1194d992..5a66f85121 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -15,7 +15,8 @@ TransactionFilterProxy::TransactionFilterProxy(QObject *parent) : dateTo(MAX_DATE), addrPrefix(), typeFilter(ALL_TYPES), - minAmount(0) + minAmount(0), + limitRows(-1) { } @@ -65,3 +66,20 @@ void TransactionFilterProxy::setMinAmount(qint64 minimum) this->minAmount = minimum; invalidateFilter(); } + +void TransactionFilterProxy::setLimit(int limit) +{ + this->limitRows = limit; +} + +int TransactionFilterProxy::rowCount(const QModelIndex &parent) const +{ + if(limitRows != -1) + { + return std::min(QSortFilterProxyModel::rowCount(parent), limitRows); + } + else + { + return QSortFilterProxyModel::rowCount(parent); + } +} diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h index a44c9c4df7..4dd2a8e5c6 100644 --- a/src/qt/transactionfilterproxy.h +++ b/src/qt/transactionfilterproxy.h @@ -26,6 +26,10 @@ public: void setTypeFilter(quint32 modes); void setMinAmount(qint64 minimum); + // Set maximum number of rows returned, -1 if unlimited + void setLimit(int limit); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; protected: bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const; @@ -35,6 +39,7 @@ private: QString addrPrefix; quint32 typeFilter; qint64 minAmount; + int limitRows; signals: diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 1606df9f5a..458341c0f5 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -578,6 +578,10 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { return llabs(rec->credit + rec->debit); } + else if (role == AmountRole) + { + return rec->credit + rec->debit; + } else if (role == TxIDRole) { return QString::fromStdString(rec->getTxID()); diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 71b0644110..0daa5f6aaa 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -39,6 +39,8 @@ public: LabelRole, // Absolute net amount of transaction, for filtering AbsoluteAmountRole, + // Net amount of transaction + AmountRole, // Unique identifier TxIDRole, // Is transaction confirmed? -- cgit v1.2.3 From 82303fc3ca8762e5d3b9afe521069df81190b9ab Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 3 Aug 2011 21:04:15 +0200 Subject: unconfirmed amount = grey --- src/qt/overviewpage.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index f79e1f3e9a..98f0476ba6 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -14,6 +14,8 @@ #include #define DECORATION_SIZE 64 +#define NUM_ITEMS 3 + class TxViewDelegate : public QItemDelegate { //Q_OBJECT @@ -57,6 +59,10 @@ public: { foreground = COLOR_NEGATIVE; } + else if(!confirmed) + { + foreground = COLOR_UNCONFIRMED; + } else { foreground = option.palette.color(QPalette::Text); @@ -105,6 +111,7 @@ OverviewPage::OverviewPage(QWidget *parent) : ui->listTransactions->setItemDelegate(txdelegate); ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE)); ui->listTransactions->setSelectionMode(QAbstractItemView::NoSelection); + ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2)); } OverviewPage::~OverviewPage() @@ -134,7 +141,7 @@ void OverviewPage::setModel(WalletModel *model) TransactionFilterProxy *filter = new TransactionFilterProxy(); filter->setSourceModel(model->getTransactionTableModel()); - filter->setLimit(3); + filter->setLimit(NUM_ITEMS); filter->setDynamicSortFilter(true); filter->setSortRole(Qt::EditRole); filter->sort(TransactionTableModel::Status, Qt::DescendingOrder); -- cgit v1.2.3 From 2ccd47596b14111f8be4984bde469adbc316db0d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 3 Aug 2011 21:28:11 +0200 Subject: fix drawing on gtk --- src/qt/bitcoin.qrc | 2 +- src/qt/overviewpage.cpp | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index e56536199b..629349c65d 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -1,7 +1,7 @@ - res/icons/address-book.png res/icons/bitcoin.png + res/icons/address-book.png res/icons/quit.png res/icons/send.png res/icons/toolbar.png diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 98f0476ba6..8aa3bb860c 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -10,17 +10,17 @@ #include "guiconstants.h" #include -#include +#include #include #define DECORATION_SIZE 64 #define NUM_ITEMS 3 -class TxViewDelegate : public QItemDelegate +class TxViewDelegate : public QAbstractItemDelegate { //Q_OBJECT public: - TxViewDelegate(): QItemDelegate(), unit(BitcoinUnits::BTC) + TxViewDelegate(): QAbstractItemDelegate(), unit(BitcoinUnits::BTC) { } @@ -28,7 +28,6 @@ public: inline void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const { - //QItemDelegate::paint(painter, option, index); painter->save(); QIcon icon = qvariant_cast(index.data(Qt::DecorationRole)); @@ -81,6 +80,11 @@ public: painter->restore(); } + inline QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const + { + return QSize(DECORATION_SIZE, DECORATION_SIZE); + } + int unit; }; -- cgit v1.2.3 From 186f3e2f0c21dc1cef68cb4b36e4bf65e2e7d7de Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 4 Aug 2011 04:40:01 +0200 Subject: Clarity: change definition of "confirmed" to "counts towards balance" --- src/qt/transactiontablemodel.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 458341c0f5..988a91ee90 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -588,7 +588,9 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const } else if (role == ConfirmedRole) { - return rec->status.status == TransactionStatus::HaveConfirmations; + // Return True if transaction counts for balance + return rec->status.confirmed && !(rec->type == TransactionRecord::Generated && + rec->status.maturity != TransactionStatus::Mature); } else if (role == FormattedAmountRole) { -- cgit v1.2.3 From 1b392019664239e68d7f529465abe2bdef230989 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 4 Aug 2011 04:41:01 +0200 Subject: when clicking a transaction on the overview page, send the user to the transactions page --- src/qt/bitcoingui.cpp | 5 ++++- src/qt/overviewpage.cpp | 7 ++++--- src/qt/overviewpage.h | 7 +++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 655ae5ca77..6e7606549a 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -154,7 +154,10 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this); - gotoOverviewPage(); + // Clicking on a transaction simply sends you to transaction history page + connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage())); + + gotoOverviewPage(); } void BitcoinGUI::createActions() diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 8aa3bb860c..778ee037af 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -103,9 +103,9 @@ OverviewPage::OverviewPage(QWidget *parent) : ui->labelBalance->setToolTip(tr("Your current balance")); ui->labelBalance->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard); - // Balance: + // Unconfirmed balance: ui->labelUnconfirmed->setFont(QFont("Monospace", -1, QFont::Bold)); - ui->labelUnconfirmed->setToolTip(tr("Balance of transactions that have yet to be confirmed")); + ui->labelUnconfirmed->setToolTip(tr("Total of transactions that have yet to be confirmed, and do not yet count toward the current balance")); ui->labelUnconfirmed->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard); ui->labelNumTransactions->setToolTip(tr("Total number of transactions in wallet")); @@ -116,6 +116,8 @@ OverviewPage::OverviewPage(QWidget *parent) : ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE)); ui->listTransactions->setSelectionMode(QAbstractItemView::NoSelection); ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2)); + + connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SIGNAL(transactionClicked(QModelIndex))); } OverviewPage::~OverviewPage() @@ -142,7 +144,6 @@ void OverviewPage::setModel(WalletModel *model) this->model = model; // Set up transaction list - TransactionFilterProxy *filter = new TransactionFilterProxy(); filter->setSourceModel(model->getTransactionTableModel()); filter->setLimit(NUM_ITEMS); diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 2abddf16a6..4b4cc922ca 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -3,6 +3,10 @@ #include +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE + namespace Ui { class OverviewPage; } @@ -23,6 +27,9 @@ public slots: void setBalance(qint64 balance, qint64 unconfirmedBalance); void setNumTransactions(int count); +signals: + void transactionClicked(const QModelIndex &index); + private: Ui::OverviewPage *ui; WalletModel *model; -- cgit v1.2.3 From 2351a3fc9f32568a3e90b01f6d9ee9d0cc6b281e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 4 Aug 2011 19:04:42 +0200 Subject: minimize amount of text in status bar; show only icons, if the user wants explanation they can view the tooltip --- src/qt/bitcoingui.cpp | 41 ++++++++++++++--------------------------- src/qt/bitcoingui.h | 2 -- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6e7606549a..c9feca5d9f 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -108,34 +108,21 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Create status bar statusBar(); - // Status bar "Connections" notification - QFrame *frameConnections = new QFrame(); - frameConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); - frameConnections->setMinimumWidth(150); - frameConnections->setMaximumWidth(150); - QHBoxLayout *frameConnectionsLayout = new QHBoxLayout(frameConnections); - frameConnectionsLayout->setContentsMargins(3,0,3,0); - frameConnectionsLayout->setSpacing(3); - labelConnectionsIcon = new QLabel(); - labelConnectionsIcon->setToolTip(tr("Number of connections to other clients")); - frameConnectionsLayout->addWidget(labelConnectionsIcon); - labelConnections = new QLabel(); - labelConnections->setToolTip(tr("Number of connections to other clients")); - frameConnectionsLayout->addWidget(labelConnections); - frameConnectionsLayout->addStretch(); - // Status bar "Blocks" notification QFrame *frameBlocks = new QFrame(); - frameBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); - frameBlocks->setMinimumWidth(150); - frameBlocks->setMaximumWidth(150); + //frameBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); + frameBlocks->setContentsMargins(0,0,0,0); + frameBlocks->setMinimumWidth(56); + frameBlocks->setMaximumWidth(56); QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); frameBlocksLayout->setContentsMargins(3,0,3,0); frameBlocksLayout->setSpacing(3); + labelConnectionsIcon = new QLabel(); labelBlocksIcon = new QLabel(); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelConnectionsIcon); + frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelBlocksIcon); - labelBlocks = new QLabel(); - frameBlocksLayout->addWidget(labelBlocks); frameBlocksLayout->addStretch(); // Progress bar for blocks download @@ -147,7 +134,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): statusBar()->addWidget(progressBarLabel); statusBar()->addWidget(progressBar); - statusBar()->addPermanentWidget(frameConnections); statusBar()->addPermanentWidget(frameBlocks); createTrayIcon(); @@ -312,7 +298,7 @@ void BitcoinGUI::setNumConnections(int count) default: icon = ":/icons/connect_4"; break; } labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(16,16)); - labelConnections->setText(tr("%n connection(s)", "", count)); + labelConnectionsIcon->setToolTip(tr("%n active connections to Bitcoin network", "", count)); } void BitcoinGUI::setNumBlocks(int count) @@ -359,13 +345,16 @@ void BitcoinGUI::setNumBlocks(int count) } // In the label we want to be less specific - QString labelText = text; bool spinning = true; if(secs < 30*60) { - labelText = "Up to date"; + tooltip = tr("Up to date") + QString("\n") + tooltip; spinning = false; } + else + { + tooltip = tr("Catching up...") + QString("\n") + tooltip; + } tooltip += QString("\n"); tooltip += tr("Last received block was generated %1.").arg(text); @@ -379,10 +368,8 @@ void BitcoinGUI::setNumBlocks(int count) { labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(16,16)); } - labelBlocks->setText(labelText); labelBlocksIcon->setToolTip(tooltip); - labelBlocks->setToolTip(tooltip); progressBarLabel->setToolTip(tooltip); progressBar->setToolTip(tooltip); } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 4fc17dd36f..c48fa8cfd8 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -54,9 +54,7 @@ private: AddressBookPage *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; - QLabel *labelConnections; QLabel *labelConnectionsIcon; - QLabel *labelBlocks; QLabel *labelBlocksIcon; QLabel *progressBarLabel; QProgressBar *progressBar; -- cgit v1.2.3 From ffccb56914bd317c438bf055a32bc89dce690913 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 4 Aug 2011 21:31:47 +0200 Subject: select new address immediately after creation --- src/qt/addressbookpage.cpp | 14 +++++++++++++- src/qt/editaddressdialog.cpp | 11 +++++++---- src/qt/editaddressdialog.h | 5 ++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 063e510c30..a8ca635edd 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -116,7 +116,19 @@ void AddressBookPage::on_newAddressButton_clicked() EditAddressDialog::NewSendingAddress : EditAddressDialog::NewReceivingAddress); dlg.setModel(model); - dlg.exec(); + if(dlg.exec()) + { + // Select row for newly created address + QString address = dlg.getAddress(); + QModelIndexList lst = proxyModel->match(proxyModel->index(0, + AddressTableModel::Address, QModelIndex()), + Qt::EditRole, address, 1, Qt::MatchExactly); + if(!lst.isEmpty()) + { + ui->tableView->setFocus(); + ui->tableView->selectRow(lst.at(0).row()); + } + } } void AddressBookPage::on_deleteButton_clicked() diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index a0b27e83bb..2b3d9bf0f0 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -54,9 +54,8 @@ void EditAddressDialog::loadRow(int row) mapper->setCurrentIndex(row); } -QString EditAddressDialog::saveCurrentRow() +bool EditAddressDialog::saveCurrentRow() { - QString address; switch(mode) { case NewReceivingAddress: @@ -74,12 +73,12 @@ QString EditAddressDialog::saveCurrentRow() } break; } - return address; + return !address.isEmpty(); } void EditAddressDialog::accept() { - if(saveCurrentRow().isEmpty()) + if(!saveCurrentRow()) { switch(model->getEditStatus()) { @@ -100,3 +99,7 @@ void EditAddressDialog::accept() QDialog::accept(); } +QString EditAddressDialog::getAddress() const +{ + return address; +} diff --git a/src/qt/editaddressdialog.h b/src/qt/editaddressdialog.h index 6219961161..81086a45a8 100644 --- a/src/qt/editaddressdialog.h +++ b/src/qt/editaddressdialog.h @@ -32,13 +32,16 @@ public: void accept(); + QString getAddress() const; private: - QString saveCurrentRow(); + bool saveCurrentRow(); Ui::EditAddressDialog *ui; QDataWidgetMapper *mapper; Mode mode; AddressTableModel *model; + + QString address; }; #endif // EDITADDRESSDIALOG_H -- cgit v1.2.3 From 126185aaa70839dfbb14e56884b4747e75942ab4 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 5 Aug 2011 15:35:52 +0200 Subject: improve tooltip over transactions --- src/qt/transactionfilterproxy.cpp | 2 +- src/qt/transactiontablemodel.cpp | 40 +++++++++++++++++++++++---------------- src/qt/transactiontablemodel.h | 11 +++++------ 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index 5a66f85121..456043afa8 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -28,7 +28,7 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & QDateTime datetime = index.data(TransactionTableModel::DateRole).toDateTime(); QString address = index.data(TransactionTableModel::AddressRole).toString(); QString label = index.data(TransactionTableModel::LabelRole).toString(); - qint64 amount = index.data(TransactionTableModel::AbsoluteAmountRole).toLongLong(); + qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong()); if(!(TYPE(type) & typeFilter)) return false; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 988a91ee90..f418a2bce3 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -265,7 +265,7 @@ int TransactionTableModel::columnCount(const QModelIndex &parent) const return columns.length(); } -QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const +QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const { QString status; @@ -289,7 +289,7 @@ QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) con } if(wtx->type == TransactionRecord::Generated) { - status += "\n\n"; + status += "\n"; switch(wtx->status.maturity) { case TransactionStatus::Immature: @@ -307,18 +307,18 @@ QVariant TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) con } } - return QVariant(status); + return status; } -QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const +QString TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const { if(wtx->time) { - return QVariant(GUIUtil::DateTimeStr(wtx->time)); + return GUIUtil::DateTimeStr(wtx->time); } else { - return QVariant(); + return QString(); } } @@ -418,7 +418,7 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const return QVariant(); } -QVariant TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const +QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const { QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit); if(showUnconfirmed) @@ -428,10 +428,10 @@ QVariant TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, boo str = QString("[") + str + QString("]"); } } - return QVariant(str); + return QString(str); } -QVariant TransactionTableModel::formatTxDecoration(const TransactionRecord *wtx) const +QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const { if(wtx->type == TransactionRecord::Generated) { @@ -476,6 +476,18 @@ QVariant TransactionTableModel::formatTxDecoration(const TransactionRecord *wtx) return QColor(0,0,0); } +QString TransactionTableModel::formatTooltip(const TransactionRecord *rec) const +{ + QString tooltip = formatTxType(rec); + if(rec->type==TransactionRecord::RecvFromIP || rec->type==TransactionRecord::SendToIP || + rec->type==TransactionRecord::SendToAddress || rec->type==TransactionRecord::RecvWithAddress) + { + tooltip += QString(" ") + formatTxToAddress(rec, true); + } + tooltip += QString("\n") + formatTxStatus(rec); + return tooltip; +} + QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { if(!index.isValid()) @@ -487,7 +499,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const switch(index.column()) { case Status: - return formatTxDecoration(rec); + return txStatusDecoration(rec); case ToAddress: return txAddressDecoration(rec); } @@ -530,8 +542,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { case Status: return formatTxStatus(rec); - case ToAddress: - return formatTxType(rec) + QString(" ") + formatTxToAddress(rec, true); + default: + return formatTooltip(rec); } } else if (role == Qt::TextAlignmentRole) @@ -574,10 +586,6 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { return walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address)); } - else if (role == AbsoluteAmountRole) - { - return llabs(rec->credit + rec->debit); - } else if (role == AmountRole) { return rec->credit + rec->debit; diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 0daa5f6aaa..17bfeccdb2 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -37,8 +37,6 @@ public: AddressRole, // Label of address related to transaction LabelRole, - // Absolute net amount of transaction, for filtering - AbsoluteAmountRole, // Net amount of transaction AmountRole, // Unique identifier @@ -63,12 +61,13 @@ private: QString lookupAddress(const std::string &address, bool tooltip) const; QVariant addressColor(const TransactionRecord *wtx) const; - QVariant formatTxStatus(const TransactionRecord *wtx) const; - QVariant formatTxDate(const TransactionRecord *wtx) const; + QString formatTxStatus(const TransactionRecord *wtx) const; + QString formatTxDate(const TransactionRecord *wtx) const; QString formatTxType(const TransactionRecord *wtx) const; QString formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const; - QVariant formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true) const; - QVariant formatTxDecoration(const TransactionRecord *wtx) const; + QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true) const; + QString formatTooltip(const TransactionRecord *rec) const; + QVariant txStatusDecoration(const TransactionRecord *wtx) const; QVariant txAddressDecoration(const TransactionRecord *wtx) const; private slots: -- cgit v1.2.3 From 00f4f8d54c94a9d0d8d56e2b501caa6699d6ddb4 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 5 Aug 2011 15:37:49 +0200 Subject: speling fix --- src/qt/bitcoingui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index c9feca5d9f..84d8fe4635 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -298,7 +298,7 @@ void BitcoinGUI::setNumConnections(int count) default: icon = ":/icons/connect_4"; break; } labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(16,16)); - labelConnectionsIcon->setToolTip(tr("%n active connections to Bitcoin network", "", count)); + labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count)); } void BitcoinGUI::setNumBlocks(int count) -- cgit v1.2.3 From d4e3cb4c03d81d02a348e72ec0f95b8233d80bfd Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 5 Aug 2011 20:25:45 +0200 Subject: improve sync spinner --- scripts/img/reload.xcf | Bin 28597 -> 25292 bytes scripts/img/reload_scaled.png | Bin 905 -> 0 bytes scripts/make_spinner.py | 2 ++ src/qt/res/movies/update_spinner.mng | Bin 27707 -> 27817 bytes 4 files changed, 2 insertions(+) delete mode 100644 scripts/img/reload_scaled.png diff --git a/scripts/img/reload.xcf b/scripts/img/reload.xcf index c3ce165adb..dc8be62831 100644 Binary files a/scripts/img/reload.xcf and b/scripts/img/reload.xcf differ diff --git a/scripts/img/reload_scaled.png b/scripts/img/reload_scaled.png deleted file mode 100644 index 9a45b1bd1d..0000000000 Binary files a/scripts/img/reload_scaled.png and /dev/null differ diff --git a/scripts/make_spinner.py b/scripts/make_spinner.py index c1f94c12c2..1d4ee0202d 100755 --- a/scripts/make_spinner.py +++ b/scripts/make_spinner.py @@ -15,6 +15,7 @@ NUMFRAMES=35 FRAMERATE=10.0 CONVERT='convert' CLOCKWISE=True +DSIZE=(16,16) im_src = Image.open(SRC) @@ -30,6 +31,7 @@ for frame in xrange(NUMFRAMES): if CLOCKWISE: rotation = -rotation im_new = im_src.rotate(rotation, Image.BICUBIC) + im_new.thumbnail(DSIZE, Image.ANTIALIAS) outfile = frame_to_filename(frame) im_new.save(outfile, 'png') frame_files.append(outfile) diff --git a/src/qt/res/movies/update_spinner.mng b/src/qt/res/movies/update_spinner.mng index 99b1b14135..7df3baac6f 100644 Binary files a/src/qt/res/movies/update_spinner.mng and b/src/qt/res/movies/update_spinner.mng differ -- cgit v1.2.3 From e74e8a184a70287e1f438e81a7b8ffc2bf09c46d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 6 Aug 2011 18:37:41 +0200 Subject: reorganize transaction model data function, and transaction tooltip --- src/qt/transactiontablemodel.cpp | 81 +++++++++++++--------------------------- 1 file changed, 25 insertions(+), 56 deletions(-) diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index f418a2bce3..02bd733b14 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -281,7 +281,7 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons status = tr("Offline (%1 confirmations)").arg(wtx->status.depth); break; case TransactionStatus::Unconfirmed: - status = tr("Unconfirmed (%1 of %2 confirmations required)").arg(wtx->status.depth).arg(TransactionRecord::NumConfirmations); + status = tr("Unconfirmed (%1 of %2 confirmations)").arg(wtx->status.depth).arg(TransactionRecord::NumConfirmations); break; case TransactionStatus::HaveConfirmations: status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth); @@ -478,13 +478,12 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) QString TransactionTableModel::formatTooltip(const TransactionRecord *rec) const { - QString tooltip = formatTxType(rec); + QString tooltip = formatTxStatus(rec) + QString("\n") + formatTxType(rec); if(rec->type==TransactionRecord::RecvFromIP || rec->type==TransactionRecord::SendToIP || rec->type==TransactionRecord::SendToAddress || rec->type==TransactionRecord::RecvWithAddress) { tooltip += QString(" ") + formatTxToAddress(rec, true); } - tooltip += QString("\n") + formatTxStatus(rec); return tooltip; } @@ -494,8 +493,9 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return QVariant(); TransactionRecord *rec = static_cast(index.internalPointer()); - if(role == Qt::DecorationRole) + switch(role) { + case Qt::DecorationRole: switch(index.column()) { case Status: @@ -503,10 +503,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case ToAddress: return txAddressDecoration(rec); } - } - else if(role == Qt::DisplayRole) - { - // Delegate to specific column handlers + break; + case Qt::DisplayRole: switch(index.column()) { case Date: @@ -518,10 +516,9 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case Amount: return formatTxAmount(rec); } - } - else if(role == Qt::EditRole) - { - // Edit role is used for sorting so return the real values + break; + case Qt::EditRole: + // Edit role is used for sorting, so return the unformatted values switch(index.column()) { case Status: @@ -535,24 +532,13 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case Amount: return rec->credit + rec->debit; } - } - else if (role == Qt::ToolTipRole) - { - switch(index.column()) - { - case Status: - return formatTxStatus(rec); - default: - return formatTooltip(rec); - } - } - else if (role == Qt::TextAlignmentRole) - { + break; + case Qt::ToolTipRole: + return formatTooltip(rec); + case Qt::TextAlignmentRole: return column_alignments[index.column()]; - } - else if (role == Qt::ForegroundRole) - { - /* Non-confirmed transactions are grey */ + case Qt::ForegroundRole: + // Non-confirmed transactions are grey if(!rec->status.confirmed) { return COLOR_UNCONFIRMED; @@ -565,43 +551,26 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { return addressColor(rec); } - } - else if (role == TypeRole) - { + break; + case TypeRole: return rec->type; - } - else if (role == DateRole) - { + case DateRole: return QDateTime::fromTime_t(static_cast(rec->time)); - } - else if (role == LongDescriptionRole) - { + case LongDescriptionRole: return priv->describe(rec); - } - else if (role == AddressRole) - { + case AddressRole: return QString::fromStdString(rec->address); - } - else if (role == LabelRole) - { + case LabelRole: return walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address)); - } - else if (role == AmountRole) - { + case AmountRole: return rec->credit + rec->debit; - } - else if (role == TxIDRole) - { + case TxIDRole: return QString::fromStdString(rec->getTxID()); - } - else if (role == ConfirmedRole) - { + case ConfirmedRole: // Return True if transaction counts for balance return rec->status.confirmed && !(rec->type == TransactionRecord::Generated && rec->status.maturity != TransactionStatus::Mature); - } - else if (role == FormattedAmountRole) - { + case FormattedAmountRole: return formatTxAmount(rec, false); } return QVariant(); -- cgit v1.2.3 From db7f023417eeeb96eed35c9d06541544abcd7033 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 7 Aug 2011 16:04:48 +0200 Subject: Accept "bitcoin:" URL drops from browsers --- src/qt/bitcoingui.cpp | 35 ++++++++++++++++++++++++++++++++-- src/qt/bitcoingui.h | 3 +++ src/qt/guiutil.cpp | 22 ++++++++++++++++++++++ src/qt/guiutil.h | 8 +++++++- src/qt/sendcoinsdialog.cpp | 47 ++++++++++++++++++++++++++++++++++++---------- src/qt/sendcoinsdialog.h | 10 +++++++++- src/qt/sendcoinsentry.cpp | 14 +++++++++++++- src/qt/sendcoinsentry.h | 6 ++++++ 8 files changed, 130 insertions(+), 15 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 84d8fe4635..b20f633ffd 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -38,6 +38,9 @@ #include #include +#include +#include + #include #include @@ -143,7 +146,9 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Clicking on a transaction simply sends you to transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage())); - gotoOverviewPage(); + setAcceptDrops(true); + + gotoOverviewPage(); } void BitcoinGUI::createActions() @@ -502,10 +507,36 @@ void BitcoinGUI::gotoReceiveCoinsPage() void BitcoinGUI::gotoSendCoinsPage() { sendCoinsAction->setChecked(true); - sendCoinsPage->clear(); + if(centralWidget->currentWidget() != sendCoinsPage) + { + // Clear the current contents if we arrived from another tab + sendCoinsPage->clear(); + } centralWidget->setCurrentWidget(sendCoinsPage); exportAction->setEnabled(false); disconnect(exportAction, SIGNAL(triggered()), 0, 0); } +void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event) +{ + // Accept only URLs + if(event->mimeData()->hasUrls()) + event->acceptProposedAction(); +} + +void BitcoinGUI::dropEvent(QDropEvent *event) +{ + if(event->mimeData()->hasUrls()) + { + gotoSendCoinsPage(); + QList urls = event->mimeData()->urls(); + foreach(const QUrl &url, urls) + { + sendCoinsPage->handleURL(&url); + } + } + + event->acceptProposedAction(); +} + diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index c48fa8cfd8..377da72611 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -20,6 +20,7 @@ class QAbstractItemModel; class QModelIndex; class QProgressBar; class QStackedWidget; +class QUrl; QT_END_NAMESPACE class BitcoinGUI : public QMainWindow @@ -41,6 +42,8 @@ public: protected: void changeEvent(QEvent *e); void closeEvent(QCloseEvent *event); + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); private: ClientModel *clientModel; diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index ece069072f..3516d4f83a 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1,5 +1,7 @@ #include "guiutil.h" #include "bitcoinaddressvalidator.h" +#include "walletmodel.h" +#include "bitcoinunits.h" #include "headers.h" @@ -8,6 +10,7 @@ #include #include #include +#include QString GUIUtil::DateTimeStr(qint64 nTime) { @@ -41,3 +44,22 @@ void GUIUtil::setupAmountWidget(QLineEdit *widget, QWidget *parent) widget->setValidator(amountValidator); widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter); } + +bool GUIUtil::parseBitcoinURL(const QUrl *url, SendCoinsRecipient *out) +{ + if(url->scheme() != QString("bitcoin")) + return false; + + SendCoinsRecipient rv; + rv.address = url->path(); + rv.label = url->queryItemValue("label"); + if(!BitcoinUnits::parse(BitcoinUnits::BTC, url->queryItemValue("amount"), &rv.amount)) + { + return false; + } + if(out) + { + *out = rv; + } + return true; +} diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index fb5c575ab5..012e49754c 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -8,20 +8,26 @@ class QFont; class QLineEdit; class QWidget; class QDateTime; +class QUrl; QT_END_NAMESPACE +class SendCoinsRecipient; class GUIUtil { public: + // Create human-readable string from date static QString DateTimeStr(qint64 nTime); static QString DateTimeStr(const QDateTime &datetime); // Render bitcoin addresses in monospace font static QFont bitcoinAddressFont(); + // Set up widgets for address and amounts static void setupAddressWidget(QLineEdit *widget, QWidget *parent); - static void setupAmountWidget(QLineEdit *widget, QWidget *parent); + + // Parse "bitcoin:" URL into recipient object, return true on succesful parsing + static bool parseBitcoinURL(const QUrl *url, SendCoinsRecipient *out); }; #endif // GUIUTIL_H diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 54cae21a1b..4d315fb794 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -5,11 +5,12 @@ #include "addressbookpage.h" #include "optionsmodel.h" #include "sendcoinsentry.h" - +#include "guiutil.h" #include #include #include +#include SendCoinsDialog::SendCoinsDialog(QWidget *parent) : QDialog(parent), @@ -133,17 +134,11 @@ void SendCoinsDialog::on_sendButton_clicked() void SendCoinsDialog::clear() { // Remove entries until only one left - while(ui->entries->count() > 1) + while(ui->entries->count()) { delete ui->entries->takeAt(0)->widget(); } - - // Reset the entry that is left to empty - SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(0)->widget()); - if(entry) - { - entry->clear(); - } + addEntry(); updateRemoveEnabled(); @@ -160,7 +155,7 @@ void SendCoinsDialog::accept() clear(); } -void SendCoinsDialog::addEntry() +SendCoinsEntry *SendCoinsDialog::addEntry() { SendCoinsEntry *entry = new SendCoinsEntry(this); entry->setModel(model); @@ -171,6 +166,7 @@ void SendCoinsDialog::addEntry() // Focus the field, so that entry can start immediately entry->clear(); + return entry; } void SendCoinsDialog::updateRemoveEnabled() @@ -208,3 +204,34 @@ QWidget *SendCoinsDialog::setupTabChain(QWidget *prev) QWidget::setTabOrder(ui->addButton, ui->sendButton); return ui->sendButton; } + +void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv) +{ + SendCoinsEntry *entry = 0; + // Replace the first entry if it is still unused + if(ui->entries->count() == 1) + { + SendCoinsEntry *first = qobject_cast(ui->entries->itemAt(0)->widget()); + if(first->isClear()) + { + entry = first; + } + } + if(!entry) + { + entry = addEntry(); + } + + entry->setValue(rv); +} + + +void SendCoinsDialog::handleURL(const QUrl *url) +{ + SendCoinsRecipient rv; + if(!GUIUtil::parseBitcoinURL(url, &rv)) + { + return; + } + pasteEntry(rv); +} diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 0f90be8165..9c56e51811 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -8,6 +8,11 @@ namespace Ui { } class WalletModel; class SendCoinsEntry; +class SendCoinsRecipient; + +QT_BEGIN_NAMESPACE +class QUrl; +QT_END_NAMESPACE class SendCoinsDialog : public QDialog { @@ -23,11 +28,14 @@ public: // Hence we have to set it up manually QWidget *setupTabChain(QWidget *prev); + void pasteEntry(const SendCoinsRecipient &rv); + void handleURL(const QUrl *url); + public slots: void clear(); void reject(); void accept(); - void addEntry(); + SendCoinsEntry *addEntry(); void updateRemoveEnabled(); private: diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index abdbc81bdc..e97f675fde 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -102,7 +102,6 @@ bool SendCoinsEntry::validate() } } - if(!ui->payTo->hasAcceptableInput() || (model && !model->validateAddress(ui->payTo->text()))) { @@ -133,3 +132,16 @@ QWidget *SendCoinsEntry::setupTabChain(QWidget *prev) QWidget::setTabOrder(ui->deleteButton, ui->addAsLabel); return ui->payAmount->setupTabChain(ui->addAsLabel); } + +void SendCoinsEntry::setValue(const SendCoinsRecipient &value) +{ + ui->payTo->setText(value.address); + ui->addAsLabel->setText(value.label); + ui->payAmount->setValue(value.amount); +} + +bool SendCoinsEntry::isClear() +{ + return ui->payTo->text().isEmpty(); +} + diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 55fd12a14b..ccc223b5f5 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -20,6 +20,12 @@ public: void setModel(WalletModel *model); bool validate(); SendCoinsRecipient getValue(); + + // Return true if the entry is still empty and unedited + bool isClear(); + + void setValue(const SendCoinsRecipient &value); + // Qt messes up the tab chain by default in some cases (issue http://bugreports.qt.nokia.com/browse/QTBUG-10907) // Hence we have to set it up manually QWidget *setupTabChain(QWidget *prev); -- cgit v1.2.3 From 856aacf388b2dd4c8213e699ff6377203a9194cc Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 7 Aug 2011 16:09:49 +0200 Subject: don't include --- src/qt/addressbookpage.cpp | 1 - src/qt/bitcoin.cpp | 1 - src/qt/bitcoinamountfield.cpp | 1 - src/qt/bitcoingui.cpp | 2 -- src/qt/monitoreddatamapper.cpp | 2 -- src/qt/optionsdialog.cpp | 1 - src/qt/optionsmodel.cpp | 2 -- src/qt/overviewpage.cpp | 1 - src/qt/sendcoinsdialog.cpp | 1 - src/qt/sendcoinsentry.cpp | 6 ++---- src/qt/transactionfilterproxy.cpp | 3 ++- src/qt/transactiontablemodel.cpp | 1 - src/qt/transactionview.cpp | 2 -- 13 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index a8ca635edd..ee64cc2c80 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -9,7 +9,6 @@ #include #include #include -#include AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : QDialog(parent), diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 8ae3762f68..a21983b7e3 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -16,7 +16,6 @@ #include #include #include -#include // Need a global reference for the notifications to find the GUI BitcoinGUI *guiref; diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index e25cefad6e..1af582f6cc 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -9,7 +9,6 @@ #include #include #include -#include BitcoinAmountField::BitcoinAmountField(QWidget *parent): QWidget(parent), amount(0), decimals(0), currentUnit(-1) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index b20f633ffd..2c9c25d5b5 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -41,8 +41,6 @@ #include #include -#include - #include BitcoinGUI::BitcoinGUI(QWidget *parent): diff --git a/src/qt/monitoreddatamapper.cpp b/src/qt/monitoreddatamapper.cpp index 7bf4fa6c8f..88948d07bf 100644 --- a/src/qt/monitoreddatamapper.cpp +++ b/src/qt/monitoreddatamapper.cpp @@ -3,8 +3,6 @@ #include #include #include -#include - MonitoredDataMapper::MonitoredDataMapper(QObject *parent) : QDataWidgetMapper(parent) diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index e922209f01..0eeb6f86a5 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -19,7 +19,6 @@ #include #include #include -#include /* First page of options */ class MainOptionsPage : public QWidget diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 4656ad08d5..efc216dab8 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -3,8 +3,6 @@ #include "headers.h" -#include - OptionsModel::OptionsModel(CWallet *wallet, QObject *parent) : QAbstractListModel(parent), wallet(wallet), diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 778ee037af..e8180e0453 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -9,7 +9,6 @@ #include "guiutil.h" #include "guiconstants.h" -#include #include #include diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 4d315fb794..a9a89c282b 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -10,7 +10,6 @@ #include #include #include -#include SendCoinsDialog::SendCoinsDialog(QWidget *parent) : QDialog(parent), diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index e97f675fde..fccef232bb 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -7,10 +7,8 @@ #include "optionsmodel.h" #include "addresstablemodel.h" -#include "qapplication.h" -#include "qclipboard.h" - -#include +#include +#include SendCoinsEntry::SendCoinsEntry(QWidget *parent) : QFrame(parent), diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index 456043afa8..a4c5b37171 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -2,7 +2,8 @@ #include "transactiontablemodel.h" #include -#include + +#include // Earliest date that can be represented (far in the past) const QDateTime TransactionFilterProxy::MIN_DATE = QDateTime::fromTime_t(0); diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 02bd733b14..6343fe50ec 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -11,7 +11,6 @@ #include "headers.h" #include -#include #include #include #include diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 12fdc947d1..0b2a3e6092 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -29,8 +29,6 @@ #include #include -#include - TransactionView::TransactionView(QWidget *parent) : QWidget(parent), model(0), transactionProxyModel(0), transactionView(0) -- cgit v1.2.3 From c359ac9128b9b1a6fda29a2a49da0991f835ad0e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 7 Aug 2011 16:16:49 +0200 Subject: allow empty/missing amounts in URL --- src/qt/guiutil.cpp | 13 +++++++++++-- src/qt/guiutil.h | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 3516d4f83a..6329d51d01 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -53,9 +53,18 @@ bool GUIUtil::parseBitcoinURL(const QUrl *url, SendCoinsRecipient *out) SendCoinsRecipient rv; rv.address = url->path(); rv.label = url->queryItemValue("label"); - if(!BitcoinUnits::parse(BitcoinUnits::BTC, url->queryItemValue("amount"), &rv.amount)) + + QString amount = url->queryItemValue("amount"); + if(amount.isEmpty()) { - return false; + rv.amount = 0; + } + else // Amount is non-empty + { + if(!BitcoinUnits::parse(BitcoinUnits::BTC, amount, &rv.amount)) + { + return false; + } } if(out) { diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 012e49754c..5f63c16e14 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -27,6 +27,7 @@ public: static void setupAmountWidget(QLineEdit *widget, QWidget *parent); // Parse "bitcoin:" URL into recipient object, return true on succesful parsing + // See Bitcoin URL definition discussion here: https://bitcointalk.org/index.php?topic=33490.0 static bool parseBitcoinURL(const QUrl *url, SendCoinsRecipient *out); }; -- cgit v1.2.3 From f839f965780ade5d13106b4b4ca46abb94a0759d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 7 Aug 2011 16:21:09 +0200 Subject: update readme --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index afe8c72036..283c83625a 100644 --- a/README.rst +++ b/README.rst @@ -38,6 +38,8 @@ This has been implemented: - Address books and transaction table can be sorted by any column +- Accepts "bitcoin:" URLs from browsers through drag and drop + This has to be done: - Start at system start -- cgit v1.2.3 From fb390d3505c9d0ad8f88cc9a26a2f8190254eda5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 7 Aug 2011 17:06:34 +0200 Subject: add TODOs in parseBitcoinURL --- src/qt/guiutil.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 6329d51d01..74863e1533 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -61,6 +61,8 @@ bool GUIUtil::parseBitcoinURL(const QUrl *url, SendCoinsRecipient *out) } else // Amount is non-empty { + // TODO: support X (exp = 8-nexp) (https://en.bitcoin.it/wiki/URI_Scheme) + // TODO: support E if(!BitcoinUnits::parse(BitcoinUnits::BTC, amount, &rv.amount)) { return false; -- cgit v1.2.3 From b0849613bf02b61774b23804c8feed54aa88474a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 8 Aug 2011 17:38:17 +0200 Subject: QtUI code cleanup / comment improvements --- src/qt/addresstablemodel.cpp | 9 ++- src/qt/bitcoin.cpp | 2 +- src/qt/bitcoinamountfield.cpp | 3 +- src/qt/bitcoingui.cpp | 47 ++++++-------- src/qt/bitcoinunits.h | 3 +- src/qt/clientmodel.h | 2 +- src/qt/guiutil.cpp | 8 +-- src/qt/guiutil.h | 4 +- src/qt/overviewpage.cpp | 2 +- src/qt/transactiondesc.cpp | 133 +++++++++++++++------------------------ src/qt/transactiondesc.h | 14 ++++- src/qt/transactiontablemodel.cpp | 17 ++--- src/qt/transactiontablemodel.h | 1 - src/qt/transactionview.h | 1 + src/qt/walletmodel.cpp | 2 - src/qt/walletmodel.h | 12 ++-- 16 files changed, 109 insertions(+), 151 deletions(-) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index c8329bb2fd..bd314ba0f0 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -161,13 +161,13 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu rec->label = value.toString(); break; case Address: - // Refuse to set invalid address + // Refuse to set invalid address, set error status and return false if(!walletModel->validateAddress(value.toString())) { editStatus = INVALID_ADDRESS; return false; } - // Double-check that we're not overwriting receiving address + // Double-check that we're not overwriting a receiving address if(rec->type == AddressTableEntry::Sending) { CRITICAL_BLOCK(wallet->cs_mapAddressBook) @@ -234,7 +234,7 @@ QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & pa void AddressTableModel::updateList() { - // Update internal model from Bitcoin core + // Update address book model from Bitcoin core beginResetModel(); priv->refreshAddressTable(); endResetModel(); @@ -247,7 +247,6 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con editStatus = OK; - if(type == Send) { if(!walletModel->validateAddress(address)) @@ -255,7 +254,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con editStatus = INVALID_ADDRESS; return QString(); } - // Check for duplicate + // Check for duplicate addresses CRITICAL_BLOCK(wallet->cs_mapAddressBook) { if(wallet->mapAddressBook.count(strAddress)) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index a21983b7e3..cdd69e31a5 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -135,7 +135,7 @@ int main(int argc, char *argv[]) { { // Put this in a block, so that BitcoinGUI is cleaned up properly before - // calling shutdown. + // calling Shutdown(). BitcoinGUI window; splash.finish(&window); OptionsModel optionsModel(pwalletMain); diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 1af582f6cc..ea38cc86dd 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -44,7 +44,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): connect(decimals, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged())); connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int))); - // TODO: set default based on configuration + // Set default based on configuration unitChanged(unit->currentIndex()); } @@ -67,7 +67,6 @@ void BitcoinAmountField::clear() { amount->clear(); decimals->clear(); - // TODO: set default based on configuration unit->setCurrentIndex(0); } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 2c9c25d5b5..dd94652e3a 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -52,6 +52,8 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): resize(850, 550); setWindowTitle(tr("Bitcoin Wallet")); setWindowIcon(QIcon(":icons/bitcoin")); + // Accept D&D of URIs + setAcceptDrops(true); createActions(); @@ -68,8 +70,8 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QMenu *help = menuBar()->addMenu("&Help"); help->addAction(aboutAction); - // Toolbar - QToolBar *toolbar = addToolBar("Main toolbar"); + // Toolbars + QToolBar *toolbar = addToolBar(tr("Tabs toolbar")); toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolbar->addAction(overviewAction); toolbar->addAction(sendCoinsAction); @@ -77,19 +79,17 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): toolbar->addAction(historyAction); toolbar->addAction(addressBookAction); - QToolBar *toolbar2 = addToolBar("Transactions toolbar"); + QToolBar *toolbar2 = addToolBar(tr("Actions toolbar")); toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolbar2->addAction(exportAction); - // Overview page + // Create tabs overviewPage = new OverviewPage(); - QVBoxLayout *vbox = new QVBoxLayout(); + transactionsPage = new QWidget(this); + QVBoxLayout *vbox = new QVBoxLayout(); transactionView = new TransactionView(this); - connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); vbox->addWidget(transactionView); - - transactionsPage = new QWidget(this); transactionsPage->setLayout(vbox); addressBookPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab); @@ -109,7 +109,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Create status bar statusBar(); - // Status bar "Blocks" notification + // Status bar notification icons QFrame *frameBlocks = new QFrame(); //frameBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); frameBlocks->setContentsMargins(0,0,0,0); @@ -141,10 +141,11 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this); - // Clicking on a transaction simply sends you to transaction history page + // Clicking on a transaction on the overview page simply sends you to transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage())); - setAcceptDrops(true); + // Doubleclicking on a transaction on the transaction history page shows details + connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); gotoOverviewPage(); } @@ -252,7 +253,6 @@ void BitcoinGUI::createTrayIcon() { QMenu *trayIconMenu = new QMenu(this); trayIconMenu->addAction(openBitcoinAction); - trayIconMenu->addAction(sendCoinsAction); trayIconMenu->addAction(optionsAction); trayIconMenu->addSeparator(); trayIconMenu->addAction(quitAction); @@ -268,7 +268,7 @@ void BitcoinGUI::createTrayIcon() void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { - if(reason == QSystemTrayIcon::DoubleClick) + if(reason == QSystemTrayIcon::Trigger) { // Doubleclick on system tray icon triggers "open bitcoin" openBitcoinAction->trigger(); @@ -347,31 +347,22 @@ void BitcoinGUI::setNumBlocks(int count) text = tr("%n day(s) ago","",secs/(60*60*24)); } - // In the label we want to be less specific - bool spinning = true; + // Set icon state: spinning if catching up, tick otherwise if(secs < 30*60) { tooltip = tr("Up to date") + QString("\n") + tooltip; - spinning = false; + labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(16,16)); } else { tooltip = tr("Catching up...") + QString("\n") + tooltip; + labelBlocksIcon->setMovie(syncIconMovie); + syncIconMovie->start(); } tooltip += QString("\n"); tooltip += tr("Last received block was generated %1.").arg(text); - if(spinning) - { - labelBlocksIcon->setMovie(syncIconMovie); - syncIconMovie->start(); - } - else - { - labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(16,16)); - } - labelBlocksIcon->setToolTip(tooltip); progressBarLabel->setToolTip(tooltip); progressBar->setToolTip(tooltip); @@ -439,12 +430,14 @@ void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) { + if(start == end) + return; TransactionTableModel *ttm = walletModel->getTransactionTableModel(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent) .data(Qt::EditRole).toULongLong(); if(!clientModel->inInitialBlockDownload()) { - // On incoming transaction, make an info balloon + // On new transaction, make an info balloon // Unless the initial block download is in progress, to prevent balloon-spam QString date = ttm->index(start, TransactionTableModel::Date, parent) .data().toString(); diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 4dfdbea044..a7bebbc3e4 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -4,7 +4,8 @@ #include #include -// Bitcoin unit definitions +// Bitcoin unit definitions, encapsulates parsing and formatting +// and serves as list model for dropdown selection boxes. class BitcoinUnits: public QAbstractListModel { public: diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 845ad10dec..15387056c8 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -12,7 +12,7 @@ QT_BEGIN_NAMESPACE class QDateTime; QT_END_NAMESPACE -// Interface to Bitcoin network client +// Model for Bitcoin network client class ClientModel : public QObject { Q_OBJECT diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 74863e1533..158b84a285 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -12,12 +12,12 @@ #include #include -QString GUIUtil::DateTimeStr(qint64 nTime) +QString GUIUtil::dateTimeStr(qint64 nTime) { - return DateTimeStr(QDateTime::fromTime_t((qint32)nTime)); + return dateTimeStr(QDateTime::fromTime_t((qint32)nTime)); } -QString GUIUtil::DateTimeStr(const QDateTime &date) +QString GUIUtil::dateTimeStr(const QDateTime &date) { return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm"); } @@ -61,8 +61,6 @@ bool GUIUtil::parseBitcoinURL(const QUrl *url, SendCoinsRecipient *out) } else // Amount is non-empty { - // TODO: support X (exp = 8-nexp) (https://en.bitcoin.it/wiki/URI_Scheme) - // TODO: support E if(!BitcoinUnits::parse(BitcoinUnits::BTC, amount, &rv.amount)) { return false; diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 5f63c16e14..bc4ddb8aae 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -16,8 +16,8 @@ class GUIUtil { public: // Create human-readable string from date - static QString DateTimeStr(qint64 nTime); - static QString DateTimeStr(const QDateTime &datetime); + static QString dateTimeStr(qint64 nTime); + static QString dateTimeStr(const QDateTime &datetime); // Render bitcoin addresses in monospace font static QFont bitcoinAddressFont(); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index e8180e0453..7bade9a755 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -74,7 +74,7 @@ public: painter->drawText(amountRect, Qt::AlignRight|Qt::AlignVCenter, amountText); painter->setPen(option.palette.color(QPalette::Text)); - painter->drawText(amountRect, Qt::AlignLeft|Qt::AlignVCenter, GUIUtil::DateTimeStr(date)); + painter->drawText(amountRect, Qt::AlignLeft|Qt::AlignVCenter, GUIUtil::dateTimeStr(date)); painter->restore(); } diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 88dc2d8d67..612b5d8974 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -1,79 +1,55 @@ #include #include "guiutil.h" +#include "bitcoinunits.h" #include "headers.h" #include "qtui.h" #include - -// Taken straight from ui.cpp -// TODO: Convert to use QStrings, Qt::Escape and tr() -// or: refactor and put describeAsHTML() into bitcoin core but that is unneccesary with better -// UI<->core API, no need to put display logic in core. +#include // For Qt::escape using namespace std; -static string HtmlEscape(const char* psz, bool fMultiLine=false) +QString TransactionDesc::HtmlEscape(const QString& str, bool fMultiLine) { - int len = 0; - for (const char* p = psz; *p; p++) + QString escaped = Qt::escape(str); + if(fMultiLine) { - if (*p == '<') len += 4; - else if (*p == '>') len += 4; - else if (*p == '&') len += 5; - else if (*p == '"') len += 6; - else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6; - else if (*p == '\n' && fMultiLine) len += 5; - else - len++; + escaped = escaped.replace("\n", "
\n"); } - string str; - str.reserve(len); - for (const char* p = psz; *p; p++) - { - if (*p == '<') str += "<"; - else if (*p == '>') str += ">"; - else if (*p == '&') str += "&"; - else if (*p == '"') str += """; - else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " "; - else if (*p == '\n' && fMultiLine) str += "
\n"; - else - str += *p; - } - return str; + return escaped; } -static string HtmlEscape(const string& str, bool fMultiLine=false) +QString TransactionDesc::HtmlEscape(const std::string& str, bool fMultiLine) { - return HtmlEscape(str.c_str(), fMultiLine); + return HtmlEscape(QString::fromStdString(str), fMultiLine); } -static string FormatTxStatus(const CWalletTx& wtx) +QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) { - // Status if (!wtx.IsFinal()) { - if (wtx.nLockTime < 500000000) - return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime); + if (wtx.nLockTime < LOCKTIME_THRESHOLD) + return tr("Open for %1 blocks").arg(nBestHeight - wtx.nLockTime); else - return strprintf(_("Open until %s"), GUIUtil::DateTimeStr(wtx.nLockTime).toStdString().c_str()); + return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime)); } else { int nDepth = wtx.GetDepthInMainChain(); if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) - return strprintf(_("%d/offline?"), nDepth); + return tr("%1/offline?").arg(nDepth); else if (nDepth < 6) - return strprintf(_("%d/unconfirmed"), nDepth); + return tr("%1/unconfirmed").arg(nDepth); else - return strprintf(_("%d confirmations"), nDepth); + return tr("%1 confirmations").arg(nDepth); } } -string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) +QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) { - string strHTML; + QString strHTML; CRITICAL_BLOCK(wallet->cs_mapAddressBook) { strHTML.reserve(4000); @@ -84,36 +60,33 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) int64 nDebit = wtx.GetDebit(); int64 nNet = nCredit - nDebit; - - - strHTML += _("Status: ") + FormatTxStatus(wtx); + strHTML += tr("Status: ") + FormatTxStatus(wtx); int nRequests = wtx.GetRequestCount(); if (nRequests != -1) { if (nRequests == 0) - strHTML += _(", has not been successfully broadcast yet"); + strHTML += tr(", has not been successfully broadcast yet"); else if (nRequests == 1) - strHTML += strprintf(_(", broadcast through %d node"), nRequests); + strHTML += tr(", broadcast through %1 node").arg(nRequests); else - strHTML += strprintf(_(", broadcast through %d nodes"), nRequests); + strHTML += tr(", broadcast through %1 nodes").arg(nRequests); } strHTML += "
"; - strHTML += _("Date: ") + (nTime ? GUIUtil::DateTimeStr(nTime).toStdString() : "") + "
"; - + strHTML += tr("Date: ") + (nTime ? GUIUtil::dateTimeStr(nTime) : QString("")) + "
"; // // From // if (wtx.IsCoinBase()) { - strHTML += _("Source: Generated
"); + strHTML += tr("Source: Generated
"); } else if (!wtx.mapValue["from"].empty()) { // Online transaction if (!wtx.mapValue["from"].empty()) - strHTML += _("From: ") + HtmlEscape(wtx.mapValue["from"]) + "
"; + strHTML += tr("From: ") + HtmlEscape(wtx.mapValue["from"]) + "
"; } else { @@ -130,13 +103,13 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) { if (wallet->mapAddressBook.count(address)) { - strHTML += string() + _("From: ") + _("unknown") + "
"; - strHTML += _("To: "); + strHTML += tr("From: ") + tr("unknown") + "
"; + strHTML += tr("To: "); strHTML += HtmlEscape(address.ToString()); if (!wallet->mapAddressBook[address].empty()) - strHTML += _(" (yours, label: ") + HtmlEscape(wallet->mapAddressBook[address]) + ")"; + strHTML += tr(" (yours, label: ") + HtmlEscape(wallet->mapAddressBook[address]) + ")"; else - strHTML += _(" (yours)"); + strHTML += tr(" (yours)"); strHTML += "
"; } } @@ -146,7 +119,6 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) } } - // // To // @@ -155,13 +127,12 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) { // Online transaction strAddress = wtx.mapValue["to"]; - strHTML += _("To: "); + strHTML += tr("To: "); if (wallet->mapAddressBook.count(strAddress) && !wallet->mapAddressBook[strAddress].empty()) strHTML += HtmlEscape(wallet->mapAddressBook[strAddress]) + " "; strHTML += HtmlEscape(strAddress) + "
"; } - // // Amount // @@ -173,11 +144,13 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) int64 nUnmatured = 0; BOOST_FOREACH(const CTxOut& txout, wtx.vout) nUnmatured += wallet->GetCredit(txout); - strHTML += _("Credit: "); + strHTML += tr("Credit: "); if (wtx.IsInMainChain()) - strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity()); + strHTML += tr("(%1 matures in %2 more blocks)") + .arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nUnmatured)) + .arg(wtx.GetBlocksToMaturity()); else - strHTML += _("(not accepted)"); + strHTML += tr("(not accepted)"); strHTML += "
"; } else if (nNet > 0) @@ -185,7 +158,7 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) // // Credit // - strHTML += _("Credit: ") + FormatMoney(nNet) + "
"; + strHTML += tr("Credit: ") + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nNet) + "
"; } else { @@ -213,7 +186,7 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) CBitcoinAddress address; if (ExtractAddress(txout.scriptPubKey, 0, address)) { - strHTML += _("To: "); + strHTML += tr("To: "); if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty()) strHTML += HtmlEscape(wallet->mapAddressBook[address]) + " "; strHTML += HtmlEscape(address.ToString()); @@ -221,7 +194,7 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) } } - strHTML += _("Debit: ") + FormatMoney(-txout.nValue) + "
"; + strHTML += tr("Debit: ") + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -txout.nValue) + "
"; } if (fAllToMe) @@ -229,13 +202,13 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) // Payment to self int64 nChange = wtx.GetChange(); int64 nValue = nCredit - nChange; - strHTML += _("Debit: ") + FormatMoney(-nValue) + "
"; - strHTML += _("Credit: ") + FormatMoney(nValue) + "
"; + strHTML += tr("Debit: ") + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -nValue) + "
"; + strHTML += tr("Credit: ") + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nValue) + "
"; } int64 nTxFee = nDebit - wtx.GetValueOut(); if (nTxFee > 0) - strHTML += _("Transaction fee: ") + FormatMoney(-nTxFee) + "
"; + strHTML += tr("Transaction fee: ") + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC,-nTxFee) + "
"; } else { @@ -244,27 +217,25 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) // BOOST_FOREACH(const CTxIn& txin, wtx.vin) if (wallet->IsMine(txin)) - strHTML += _("Debit: ") + FormatMoney(-wallet->GetDebit(txin)) + "
"; + strHTML += tr("Debit: ") + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC,-wallet->GetDebit(txin)) + "
"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if (wallet->IsMine(txout)) - strHTML += _("Credit: ") + FormatMoney(wallet->GetCredit(txout)) + "
"; + strHTML += tr("Credit: ") + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC,wallet->GetCredit(txout)) + "
"; } } - strHTML += _("Net amount: ") + FormatMoney(nNet, true) + "
"; - + strHTML += tr("Net amount: ") + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC,nNet, true) + "
"; // // Message // if (!wtx.mapValue["message"].empty()) - strHTML += string() + "
" + _("Message:") + "
" + HtmlEscape(wtx.mapValue["message"], true) + "
"; + strHTML += QString("
") + tr("Message:") + "
" + HtmlEscape(wtx.mapValue["message"], true) + "
"; if (!wtx.mapValue["comment"].empty()) - strHTML += string() + "
" + _("Comment:") + "
" + HtmlEscape(wtx.mapValue["comment"], true) + "
"; + strHTML += QString("
") + tr("Comment:") + "
" + HtmlEscape(wtx.mapValue["comment"], true) + "
"; if (wtx.IsCoinBase()) - strHTML += string() + "
" + _("Generated coins must wait 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, it will change to \"not accepted\" and not be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.") + "
"; - + strHTML += QString("
") + tr("Generated coins must wait 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, it will change to \"not accepted\" and not be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.") + "
"; // // Debug view @@ -274,10 +245,10 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += "

Debug information

"; BOOST_FOREACH(const CTxIn& txin, wtx.vin) if(wallet->IsMine(txin)) - strHTML += "Debit: " + FormatMoney(-wallet->GetDebit(txin)) + "
"; + strHTML += "Debit: " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC,-wallet->GetDebit(txin)) + "
"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if(wallet->IsMine(txout)) - strHTML += "Credit: " + FormatMoney(wallet->GetCredit(txout)) + "
"; + strHTML += "Credit: " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC,wallet->GetCredit(txout)) + "
"; strHTML += "
Transaction:
"; strHTML += HtmlEscape(wtx.ToString(), true); @@ -304,9 +275,9 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) { if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty()) strHTML += HtmlEscape(wallet->mapAddressBook[address]) + " "; - strHTML += address.ToString(); + strHTML += QString::fromStdString(address.ToString()); } - strHTML = strHTML + " Amount=" + FormatMoney(vout.nValue); + strHTML = strHTML + " Amount=" + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC,vout.nValue); strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) ? "true" : "false") + ""; } } @@ -315,8 +286,6 @@ string TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += ""; } - - strHTML += "
"; } return strHTML; diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h index fde861b6fb..257b2cbb89 100644 --- a/src/qt/transactiondesc.h +++ b/src/qt/transactiondesc.h @@ -1,16 +1,24 @@ #ifndef TRANSACTIONDESC_H #define TRANSACTIONDESC_H +#include +#include #include class CWallet; class CWalletTx; -class TransactionDesc +class TransactionDesc: public QObject { public: - /* Provide human-readable extended HTML description of a transaction */ - static std::string toHTML(CWallet *wallet, CWalletTx &wtx); + // Provide human-readable extended HTML description of a transaction + static QString toHTML(CWallet *wallet, CWalletTx &wtx); +private: + TransactionDesc() {} + + static QString HtmlEscape(const QString& str, bool fMultiLine=false); + static QString HtmlEscape(const std::string &str, bool fMultiLine=false); + static QString FormatTxStatus(const CWalletTx& wtx); }; #endif // TRANSACTIONDESC_H diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 6343fe50ec..353cd79145 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -18,7 +18,7 @@ #include #include -// Credit and Debit columns are right-aligned as they contain numbers +// Amount column is right-aligned it contains numbers static int column_alignments[] = { Qt::AlignLeft|Qt::AlignVCenter, Qt::AlignLeft|Qt::AlignVCenter, @@ -100,10 +100,10 @@ struct TransactionTablePriv for(int update_idx = updated_sorted.size()-1; update_idx >= 0; --update_idx) { const uint256 &hash = updated_sorted.at(update_idx); - /* Find transaction in wallet */ + // Find transaction in wallet std::map::iterator mi = wallet->mapWallet.find(hash); bool inWallet = mi != wallet->mapWallet.end(); - /* Find bounds of this transaction in model */ + // Find bounds of this transaction in model QList::iterator lower = qLowerBound( cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); QList::iterator upper = qUpperBound( @@ -196,7 +196,7 @@ struct TransactionTablePriv std::map::iterator mi = wallet->mapWallet.find(rec->hash); if(mi != wallet->mapWallet.end()) { - return QString::fromStdString(TransactionDesc::toHTML(wallet, mi->second)); + return TransactionDesc::toHTML(wallet, mi->second); } } return QString(""); @@ -274,7 +274,7 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons status = tr("Open for %n block(s)","",wtx->status.open_for); break; case TransactionStatus::OpenUntilDate: - status = tr("Open until %1").arg(GUIUtil::DateTimeStr(wtx->status.open_for)); + status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for)); break; case TransactionStatus::Offline: status = tr("Offline (%1 confirmations)").arg(wtx->status.depth); @@ -313,7 +313,7 @@ QString TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const { if(wtx->time) { - return GUIUtil::DateTimeStr(wtx->time); + return GUIUtil::dateTimeStr(wtx->time); } else { @@ -606,11 +606,6 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat return QVariant(); } -Qt::ItemFlags TransactionTableModel::flags(const QModelIndex &index) const -{ - return QAbstractTableModel::flags(index); -} - QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent); diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 17bfeccdb2..da55495e1e 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -51,7 +51,6 @@ public: int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; - Qt::ItemFlags flags(const QModelIndex &index) const; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; private: CWallet* wallet; diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 54925f2855..f4f815b162 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -24,6 +24,7 @@ public: void setModel(WalletModel *model); + // Date ranges for filter enum DateEnum { All, diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index d8139e9f09..10b3738c67 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -83,8 +83,6 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList &recipients); private: CWallet *wallet; -- cgit v1.2.3 From 8c4738d5a7c9c1e6f29c558c49d7948fc357b9e3 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 16 Aug 2011 11:18:27 +0200 Subject: fix issue #13 --- src/qt/bitcoin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index cdd69e31a5..9b4d88d91f 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -120,7 +120,7 @@ int main(int argc, char *argv[]) translator.load("bitcoin_"+locale); app.installTranslator(&translator); - QSplashScreen splash(QPixmap(":/images/splash"), Qt::WindowStaysOnTopHint); + QSplashScreen splash(QPixmap(":/images/splash"), 0); splash.show(); splash.setAutoFillBackground(true); splashref = &splash; -- cgit v1.2.3 From 317c733572535469f0f7645f6334fc82fdbe2ad1 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 16 Aug 2011 17:30:58 +0200 Subject: add russian translation by msva --- bitcoin-qt.pro | 5 +- src/qt/locale/bitcoin_ru.ts | 1479 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1483 insertions(+), 1 deletion(-) create mode 100644 src/qt/locale/bitcoin_ru.ts diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 03cd592a15..c1493f6bfe 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -149,4 +149,7 @@ FORMS += \ src/qt/forms/sendcoinsentry.ui CODECFORTR = UTF-8 -TRANSLATIONS = src/qt/locale/bitcoin_nl.ts src/qt/locale/bitcoin_de.ts +# for lrelease/lupdate +TRANSLATIONS = src/qt/locale/bitcoin_nl.ts src/qt/locale/bitcoin_de.ts \ + src/qt/locale/bitcoin_ru.ts + diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts new file mode 100644 index 0000000000..df9a25911c --- /dev/null +++ b/src/qt/locale/bitcoin_ru.ts @@ -0,0 +1,1479 @@ + + + + + AboutDialog + + + About Bitcoin + О Bitcoin'е + + + + <b>Bitcoin</b> version + Версия Bitcoin'а + + + + Copyright © 2009-2011 Bitcoin Developers + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file license.txt or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + Copyright © 2009-2011 Разработчики сети Bitcoin + +ВНИМАНИЕ: этот софт является экспериментальным! + +Распространяется под лицензией MIT/X11, за дополнительной информацией обращайтесь к прилагающемуся файлу license.txt или документу по данной ссылке: http://www.opensource.org/licenses/mit-license.php. + +Данный продукт включает в себя разработки проекта OpenSSL (http://www.openssl.org/), криптографические функции и алгоритмы, написанные Эриком Янгом (eay@cryptsoft.com) и функции для работы с UPnP за авторством Томаса Бернарда. + + + + AddressBookPage + + + Address Book + Адресная книга + + + + These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Здесь перечислены Ваши адреса для получения платежей. Вы можете использовать их для того, чтобы давать разным людям разные адреса и таким образом иметь возможность отслеживать кто и сколько Вам платил, а так же поддерживать бо́льшую анонимность.. + + + + Double-click to edit address or label + Для того, чтобы изменить адрес или метку давжды кликните по изменяемому объекту + + + + Create a new address + Создать новый адрес + + + + &New Address... + &Создать адрес... + + + + Copy the currently selected address to the system clipboard + Копировать текущий выделенный адрес в буфер обмена + + + + &Copy to Clipboard + &Kопировать + + + + Delete the currently selected address from the list. Only sending addresses can be deleted. + Удалить выделенный адрес из списка (могут быть удалены только записи из адресной книги). + + + + &Delete + &Удалить + + + + Export Address Book Data + Экспортировать адресную книгу + + + + Comma separated file (*.csv) + Текст, разделённый запятыми (*.csv) + + + + Error exporting + Ошибка экспорта + + + + Could not write to file %1. + Невозможно записать в файл %1. + + + + AddressTableModel + + + Label + Метка + + + + Address + Адрес + + + + (no label) + [нет метки] + + + + BitcoinGUI + + + Bitcoin Wallet + Bitcoin-бумажник + + + + Tabs toolbar + Панель вкладок + + + + Actions toolbar + Панель действий + + + + Synchronizing with network... + Синхронизация с сетью... + + + + Block chain synchronization in progress + Идёт синхронизация цепочки блоков + + + + &Overview + О&бзор + + + + Show general overview of wallet + Показать общий обзор действий с бумажником + + + + &Transactions + &Транзакции + + + + Browse transaction history + Показать историю транзакций + + + + &Address Book + &Адресная книга + + + + Edit the list of stored addresses and labels + Изменить список сохранённых адресов и меток к ним + + + + &Receive coins + &Получение + + + + Show the list of addresses for receiving payments + Показать список адресов для получения платежей + + + + &Send coins + Отп&равка + + + + Send coins to a bitcoin address + Отправить монеты на указанный адрес + + + + &Exit + Вы&ход + + + + Quit application + Закрыть приложение + + + + &About + &Информация + + + + Show information about Bitcoin + Показать информацию о Bitcoin'е + + + + &Options... + Оп&ции... + + + + Modify configuration options for bitcoin + Изменить настройки + + + + Open &Bitcoin + &Показать бумажник + + + + Show the Bitcoin window + Показать окно бумажника + + + + &Export... + &Экспорт... + + + + Export the current view to a file + Экспортировать в файл + + + + [testnet] + [тестовая сеть] + + + + %n active connection(s) to Bitcoin network + + %n активное соединение с сетью + %n активных соединений с сетью + %n активных соединений с сетью + + + + + Downloaded %1 of %2 blocks of transaction history. + Загружено %1 из %2 блоков истории транзакций. + + + + Downloaded %1 blocks of transaction history. + Загружено %1 блоков истории транзакций. + + + + %n second(s) ago + + %n секунду назад + %n секунды назад + %n секунд назад + + + + + %n minute(s) ago + + %n минуту назад + %n минуты назад + %n минут назад + + + + + %n hour(s) ago + + %n час назад + %n часа назад + %n часов назад + + + + + %n day(s) ago + + %n день назад + %n дня назад + %n дней назад + + + + + Up to date + Синхронизированно + + + + Catching up... + Синхронизируется... + + + + Last received block was generated %1. + Последний полученный блок был сгенерирован %1. + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Данная транзакция превышает предельно допустимый размер. Но Вы можете всё равно совершить ей, добавив комиссию в %1, которая отправится тем узлам, которые обработают Вашу транзакцию и поможет поддержать сеть. Вы хотите добавить комиссию? + + + + Sending... + Отправка... + + + + Sent transaction + Исходящая транзакция + + + + Incoming transaction + Входящая транзакция + + + + Date: + Дата: + + + + Amount: + Количество: + + + + Type: + Тип: + + + + Address: + Адрес: + + + + DisplayOptionsPage + + + &Unit to show amounts in: + &Измерять монеты в: + + + + Choose the default subdivision unit to show in the interface, and when sending coins + Единица измерения количества монет при отображении и при отправке + + + + Display addresses in transaction list + Показывать адреса в списке транзакций + + + + EditAddressDialog + + + Edit Address + Изменить адрес + + + + &Label + &Метка + + + + The label associated with this address book entry + Метка, связанная с данной записью + + + + &Address + &Адрес + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Адрес, связанный с данной записью. + + + + New receiving address + Новый адрес для получения + + + + New sending address + Новый адрес для отправки + + + + Edit receiving address + Изменение адреса для получения + + + + Edit sending address + Изменение адреса для отправки + + + + The entered address "%1" is already in the address book. + Введённый адрес «%1» уже находится в адресной книге. + + + + The entered address "%1" is not a valid bitcoin address. + Введённый адрес «%1» не является правильным Bitcoin-адресом. + + + + MainOptionsPage + + + &Start Bitcoin on window system startup + &Запускать бумажник при входе в систему + + + + Automatically start Bitcoin after the computer is turned on + Автоматически запускать бумажник, когда включается компьютер + + + + &Minimize to the tray instead of the taskbar + &Cворачивать в системный лоток вместо панели задач + + + + Show only a tray icon after minimizing the window + Показывать только иконку в системном лотке при сворачивании окна + + + + Map port using &UPnP + Пробросить порт через &UPnP + + + + Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled. + Автоматически открыть порт для Bitcoin-клиента на роутере. Работает ТОЛЬКО если Ваш роутер поддерживает UPnP и данная функция включена. + + + + M&inimize on close + С&ворачивать вместо закрытия + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Сворачивать вместо закрытия. Если данная опция будет выбрана — приложение закроется только после выбора соответствующего пункта в меню. + + + + &Connect through SOCKS4 proxy: + &Подключаться через SOCKS4 прокси: + + + + Connect to the Bitcon network through a SOCKS4 proxy (e.g. when connecting through Tor) + Подключаться к сети Bitcoin через SOCKS4 прокси (например, при использовании Tor) + + + + Proxy &IP: + &IP Прокси: + + + + IP address of the proxy (e.g. 127.0.0.1) + IP-адрес прокси (например 127.0.0.1) + + + + &Port: + По&рт: + + + + Port of the proxy (e.g. 1234) + Порт прокси-сервера (например 1234) + + + + Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended. + Опциональная комиссия за кадый KB транзакции, которое позволяет быть уверенным, что Ваша транзакция будет обработана быстро. Большинство транщакций занимают 1 KB. Рекомендованная комиссия: 0.01 BTC. + + + + Pay transaction &fee + Добавлять ко&миссию + + + + Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended. + Опциональная комиссия за кадый KB транзакции, которая позволяет быть уверенным, что Ваша транзакция будет обработана быстро. Большинство транзакций занимают 1 KB. Рекомендованная комиссия: 0.01 BTC. + + + + OptionsDialog + + + Main + Основное + + + + Display + Отображение + + + + Options + Опции + + + + OverviewPage + + + Form + Форма + + + + Balance: + Баланс: + + + + 123.456 BTC + 123.456 BTC + + + + Number of transactions: + Количество транзакций: + + + + 0 + 0 + + + + Unconfirmed: + Не подтверждено: + + + + 0 BTC + 0 BTC + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Wallet</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Бумажник</span></p></body></html> + + + + <b>Recent transactions</b> + <b>Последние транзакции</b> + + + + Your current balance + Ваш текущий баланс + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Общая сумма всех транзакций, которые до сих пор не подтверждены, и до сих пор не учитываются в текущем балансе + + + + Total number of transactions in wallet + Общая количество транзакций в Вашем бумажнике + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Отправка + + + + Send to multiple recipients at once + Отправить нескольким получателям одновременно + + + + &Add recipient... + &Добавить получателя... + + + + Confirm the send action + Подтвердить отправку + + + + &Send + &Отправить + + + + <b>%1</b> to %2 (%3) + <b>%1</b> адресату %2 (%3) + + + + Confirm send coins + Подтвердите отправку монет + + + + Are you sure you want to send %1? + Вы уверены, что хотите отправить %1? + + + + and + и + + + + The recepient address is not valid, please recheck. + Адрес получателя неверный, пожалуйста, перепроверьте. + + + + The amount to pay must be larger than 0. + Количество монет для отправки должно быть больше 0. + + + + Amount exceeds your balance + Количество отправляемых монет превышает Ваш баланс + + + + Total exceeds your balance when the %1 transaction fee is included + Сумма превысит Ваш баланс, если комиссия в %1 будет добавлена к транзакции + + + + Duplicate address found, can only send to each address once in one send operation + Обнаружен дублирующийся адрес. Отправка на один и тот же адрес возможна только один раз за одну операцию отправки + + + + Error: Transaction creation failed + Ошибка: Создание транзакции не удалось + + + + Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here. + Ошибка: В транзакции отказано. Такое может произойти, если некоторые монеты уже были потрачены, например, если Вы используете одну копию бумажника (wallet.dat), а монеты были потрачены из другой копии, но не были отмечены как потраченные в этой. Или в случае кражи (компрометации) Вашего бумажника. + + + + SendCoinsEntry + + + Form + Форма + + + + A&mount: + Ко&личество: + + + + Pay &To: + Полу&чатель: + + + + + Enter a label for this address to add it to your address book + Введите метку для данного адреса (для добавления в адресную книгу) + + + + &Label: + &Метка: + + + + The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + Адрес получателя платежа (например 1LA5FtQhnnWnkK6zjFfutR7Stiit4wKd63) + + + + Choose adress from address book + Выбрать адрес из адресной книги + + + + Alt+A + + + + + Paste address from clipboard + Вставить адрес из буфера обмена + + + + Alt+P + + + + + Remove this recipient + Удалить этого получателя + + + + Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + Введите Bitcoin-адрес (например 1LA5FtQhnnWnkK6zjFfutR7Stiit4wKd63) + + + + TransactionDesc + + + Open for %1 blocks + + + + + Open until %1 + + + + + %1/offline? + %1/оффлайн? + + + + %1/unconfirmed + %1/не подтверждено + + + + %1 confirmations + %1 подтверждений + + + + <b>Status:</b> + <b>Статус:</b> + + + + , has not been successfully broadcast yet + , ещё не было успешно разослано + + + + , broadcast through %1 node + , разослано через %1 узел + + + + , broadcast through %1 nodes + , разослано через %1 узлов + + + + <b>Date:</b> + <b>Дата:</b> + + + + <b>Source:</b> Generated<br> + <b>Источник:</b> [сгенерированно]<br> + + + + + <b>From:</b> + <b>Отправитель:</b> + + + + unknown + неизвестно + + + + + + <b>To:</b> + <b>Получатель:</b> + + + + (yours, label: + (Ваш, метка: + + + + (yours) + (ваш) + + + + + + + <b>Credit:</b> + <b>Кредит:</b> + + + + (%1 matures in %2 more blocks) + (%1 «созреет» через %2 блоков) + + + + (not accepted) + (не принято) + + + + + + <b>Debit:</b> + <b>Дебет:</b> + + + + <b>Transaction fee:</b> + <b>Комиссия:</b> + + + + <b>Net amount:</b> + <b>Общая сумма:</b> + + + + Message: + Сообщение: + + + + Comment: + Комментарий: + + + + Generated coins must wait 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, it will change to "not accepted" and not be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + TransactionDescDialog + + + Transaction details + Детали транзакции + + + + This pane shows a detailed description of the transaction + Данный диалог показывает детализированную статистику по выбранной транзакции + + + + TransactionTableModel + + + Date + Дата + + + + Type + Тип + + + + Address + Адрес + + + + Amount + Количество + + + + Open for %n block(s) + + + + + + + + + Open until %1 + + + + + Offline (%1 confirmations) + Оффлайн (%1 подтверждений) + + + + Unconfirmed (%1 of %2 confirmations) + Не подтверждено (%1 из %2 подтверждений) + + + + Confirmed (%1 confirmations) + Подтверждено (%1 подтверждений) + + + + Mined balance will be available in %n more blocks + + Добытыми монетами можно будет воспользоваться через %n блок + Добытыми монетами можно будет воспользоваться через %n блока + Добытыми монетами можно будет воспользоваться через %n блоков + + + + + This block was not received by any other nodes and will probably not be accepted! + Этот блок не был получен другими узлами и, возможно, не будет принят! + + + + Generated but not accepted + Сгенерированно, но не подтверждено + + + + Received with + Получено + + + + Received from IP + Получено с IP-адреса + + + + Sent to + Отправлено + + + + Sent to IP + Отправлено на IP-адрес + + + + Payment to yourself + Отправлено себе + + + + Mined + Добыто + + + + (n/a) + [не доступно] + + + + Transaction status. Hover over this field to show number of confirmations. + Статус транзакции. Подведите курсор к нужному полю для того, чтобы увидеть количество подтверждений. + + + + Date and time that the transaction was received. + Дата и время, когда транзакция была получена. + + + + Type of transaction. + Тип транзакции. + + + + Destination address of transaction. + Адрес назначения транзакции. + + + + Amount removed from or added to balance. + Сумма, добавленная, или снятая с баланса. + + + + TransactionView + + + + All + Все + + + + Today + Сегодня + + + + This week + На этой неделе + + + + This month + В этом месяце + + + + Last month + За последний месяц + + + + This year + В этом году + + + + Range... + Промежуток... + + + + Received with + Получено на + + + + Sent to + Отправлено на + + + + To yourself + Отправленные себе + + + + Mined + Добытые + + + + Other + Другое + + + + Export Transaction Data + Экспортировать данные транзакций + + + + Comma separated file (*.csv) + Текс, разделённый запятыми (*.csv) + + + + Error exporting + Ошибка экспорта + + + + Could not write to file %1. + Невозможно записать в файл %1. + + + + WalletModel + + + Sending... + Отправка.... + + + + bitcoin-core + + + Bitcoin version + + + + + Usage: + + + + + Send command to -server or bitcoind + + + + + + List commands + + + + + + Get help for a command + + + + + + Options: + + + + + + Specify configuration file (default: bitcoin.conf) + + + + + + Specify pid file (default: bitcoind.pid) + + + + + + Generate coins + + + + + + Don't generate coins + + + + + + Start minimized + + + + + + Specify data directory + + + + + + Specify connection timeout (in milliseconds) + + + + + + Connect through socks4 proxy + + + + + + Allow DNS lookups for addnode and connect + + + + + + Add a node to connect to + + + + + + Connect only to the specified node + + + + + + Don't accept connections from outside + + + + + + Don't attempt to use UPnP to map the listening port + + + + + + Attempt to use UPnP to map the listening port + + + + + + Fee per KB to add to transactions you send + + + + + + Accept command line and JSON-RPC commands + + + + + + Run in the background as a daemon and accept commands + + + + + + Use the test network + + + + + + Username for JSON-RPC connections + + + + + + Password for JSON-RPC connections + + + + + + Listen for JSON-RPC connections on <port> (default: 8332) + + + + + + Allow JSON-RPC connections from specified IP address + + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + + Set key pool size to <n> (default: 100) + + + + + + Rescan the block chain for missing wallet transactions + + + + + + +SSL options: (see the Bitcoin Wiki for SSL setup instructions) + + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + + Server certificate file (default: server.cert) + + + + + + Server private key (default: server.pem) + + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + + This help message + + + + + + Cannot obtain a lock on data directory %s. Bitcoin is probably already running. + + + + + Error loading addr.dat + + + + + + Error loading blkindex.dat + + + + + + Error loading wallet.dat + + + + + + Invalid -proxy address + + + + + Invalid amount for -paytxfee=<amount> + + + + + Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Disk space is low + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds + + + + + Error: Transaction creation failed + + + + + Sending... + + + + + Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here. + + + + + Invalid amount + + + + + Insufficient funds + + + + + Invalid bitcoin address + + + + + Unable to bind to port %d on this computer. Bitcoin is probably already running. + + + + + To use the %s option + + + + + Warning: %s, you must set rpcpassword=<password> +in the configuration file: %s +If the file does not exist, create it with owner-readable-only file permissions. + + + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + + Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly. + + + + + -beta + + + + -- cgit v1.2.3 From 872b1f3e4c15de16fd821ab0adde9697a9e8d925 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 16 Aug 2011 17:37:01 +0200 Subject: update README for issue #15 --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 283c83625a..45fb5a3a50 100644 --- a/README.rst +++ b/README.rst @@ -34,7 +34,7 @@ This has been implemented: - Multiple unit support, can show subdivided bitcoins (uBTC, mBTC) for users that like large numbers -- Support for English, German and Dutch languages +- Support for English, German, Russian and Dutch languages - Address books and transaction table can be sorted by any column -- cgit v1.2.3 From ae8adeb90abb334b8e5712124e62461eca77c12f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 23 Aug 2011 20:08:42 +0200 Subject: Wallet encryption part 1: show wallet encryption status --- doc/assets-attribution.txt | 2 +- src/qt/bitcoin.qrc | 2 ++ src/qt/bitcoingui.cpp | 30 ++++++++++++++++++++++++++++-- src/qt/bitcoingui.h | 3 +++ src/qt/guiconstants.h | 5 ++++- src/qt/res/icons/lock_closed.png | Bin 0 -> 1237 bytes src/qt/res/icons/lock_open.png | Bin 0 -> 1442 bytes src/qt/walletmodel.cpp | 23 +++++++++++++++++++++-- src/qt/walletmodel.h | 12 ++++++++++++ 9 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 src/qt/res/icons/lock_closed.png create mode 100644 src/qt/res/icons/lock_open.png diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index e4a00fa87a..d498e8b4a8 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -15,7 +15,7 @@ Designer: FatCow Web Hosting License: Creative Commons Attribution (by) Site: http://findicons.com/icon/163938/book_open -Icon: src/qt/res/icons/connect*.png, src/qt/res/icons/synced.png +Icon: src/qt/res/icons/connect*.png, src/qt/res/icons/synced.png, src/qt/res/icons/lock_*.png Icon Pack: Human-O2 Designer: schollidesign License: GNU/GPL diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 629349c65d..1d5a58a4af 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -34,6 +34,8 @@ res/icons/tx_input.png res/icons/tx_output.png res/icons/tx_inout.png + res/icons/lock_closed.png + res/icons/lock_open.png res/images/about.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index dd94652e3a..22987267ec 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -18,6 +18,7 @@ #include "transactionview.h" #include "overviewpage.h" #include "bitcoinunits.h" +#include "guiconstants.h" #include #include @@ -118,9 +119,12 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); frameBlocksLayout->setContentsMargins(3,0,3,0); frameBlocksLayout->setSpacing(3); + labelEncryptionIcon = new QLabel(); labelConnectionsIcon = new QLabel(); labelBlocksIcon = new QLabel(); frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelEncryptionIcon); + frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelConnectionsIcon); frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelBlocksIcon); @@ -244,6 +248,9 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) receiveCoinsPage->setModel(walletModel->getAddressTableModel()); sendCoinsPage->setModel(walletModel); + setEncryptionStatus(walletModel->getEncryptionStatus()); + connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SLOT(setEncryptionStatus(int))); + // Balloon popup for new transaction connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(incomingTransaction(QModelIndex,int,int))); @@ -300,7 +307,7 @@ void BitcoinGUI::setNumConnections(int count) case 7: case 8: case 9: icon = ":/icons/connect_3"; break; default: icon = ":/icons/connect_4"; break; } - labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(16,16)); + labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count)); } @@ -351,7 +358,7 @@ void BitcoinGUI::setNumBlocks(int count) if(secs < 30*60) { tooltip = tr("Up to date") + QString("\n") + tooltip; - labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(16,16)); + labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); } else { @@ -531,3 +538,22 @@ void BitcoinGUI::dropEvent(QDropEvent *event) event->acceptProposedAction(); } +void BitcoinGUI::setEncryptionStatus(int status) +{ + switch(status) + { + case WalletModel::Unencrypted: + labelEncryptionIcon->hide(); + break; + case WalletModel::Unlocked: + labelEncryptionIcon->show(); + labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently unlocked")); + break; + case WalletModel::Locked: + labelEncryptionIcon->show(); + labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently locked")); + break; + } +} diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 377da72611..4b7131710c 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -57,6 +57,7 @@ private: AddressBookPage *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; + QLabel *labelEncryptionIcon; QLabel *labelConnectionsIcon; QLabel *labelBlocksIcon; QLabel *progressBarLabel; @@ -85,6 +86,8 @@ private: public slots: void setNumConnections(int count); void setNumBlocks(int count); + void setEncryptionStatus(int status); + void error(const QString &title, const QString &message); /* It is currently not possible to pass a return value to another thread through BlockingQueuedConnection, so use an indirected pointer. diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 7fbf7fcd3a..b7870199bb 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -1,9 +1,12 @@ #ifndef GUICONSTANTS_H #define GUICONSTANTS_H -/* milliseconds between model updates */ +/* Milliseconds between model updates */ static const int MODEL_UPDATE_DELAY = 500; +/* Size of icons in status bar */ +static const int STATUSBAR_ICONSIZE = 16; + /* Invalid field background style */ #define STYLE_INVALID "background:#FF8080" diff --git a/src/qt/res/icons/lock_closed.png b/src/qt/res/icons/lock_closed.png new file mode 100644 index 0000000000..ce8da0bec7 Binary files /dev/null and b/src/qt/res/icons/lock_closed.png differ diff --git a/src/qt/res/icons/lock_open.png b/src/qt/res/icons/lock_open.png new file mode 100644 index 0000000000..6a3a8edb23 Binary files /dev/null and b/src/qt/res/icons/lock_open.png differ diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 10b3738c67..9a7b56d33e 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -12,7 +12,8 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0), transactionTableModel(0), - cachedBalance(0), cachedUnconfirmedBalance(0), cachedNumTransactions(0) + cachedBalance(0), cachedUnconfirmedBalance(0), cachedNumTransactions(0), + cachedEncryptionStatus(Unencrypted) { // Until signal notifications is built into the bitcoin core, // simply update everything after polling using a timer. @@ -49,6 +50,7 @@ void WalletModel::update() qint64 newBalance = getBalance(); qint64 newUnconfirmedBalance = getUnconfirmedBalance(); int newNumTransactions = getNumTransactions(); + EncryptionStatus newEncryptionStatus = getEncryptionStatus(); if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance) emit balanceChanged(newBalance, newUnconfirmedBalance); @@ -56,6 +58,9 @@ void WalletModel::update() if(cachedNumTransactions != newNumTransactions) emit numTransactionsChanged(newNumTransactions); + if(cachedEncryptionStatus != newEncryptionStatus) + emit encryptionStatusChanged(newEncryptionStatus); + cachedBalance = newBalance; cachedUnconfirmedBalance = newUnconfirmedBalance; cachedNumTransactions = newNumTransactions; @@ -179,4 +184,18 @@ TransactionTableModel *WalletModel::getTransactionTableModel() return transactionTableModel; } - +WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const +{ + if(!wallet->IsCrypted()) + { + return Unencrypted; + } + else if(wallet->IsLocked()) + { + return Locked; + } + else + { + return Unlocked; + } +} diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index bb1c6e85d3..a585f8d8d6 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -36,6 +36,13 @@ public: MiscError }; + enum EncryptionStatus + { + Unencrypted, // !wallet->IsCrypted() + Locked, // wallet->IsCrypted() && wallet->IsLocked() + Unlocked // wallet->IsCrypted() && !wallet->IsLocked() + }; + OptionsModel *getOptionsModel(); AddressTableModel *getAddressTableModel(); TransactionTableModel *getTransactionTableModel(); @@ -43,6 +50,9 @@ public: qint64 getBalance() const; qint64 getUnconfirmedBalance() const; int getNumTransactions() const; + EncryptionStatus getEncryptionStatus() const; + + bool isEncrypted() const; // Check address for validity bool validateAddress(const QString &address); @@ -74,10 +84,12 @@ private: qint64 cachedBalance; qint64 cachedUnconfirmedBalance; qint64 cachedNumTransactions; + EncryptionStatus cachedEncryptionStatus; signals: void balanceChanged(qint64 balance, qint64 unconfirmedBalance); void numTransactionsChanged(int count); + void encryptionStatusChanged(int status); // Asynchronous error notification void error(const QString &title, const QString &message); -- cgit v1.2.3 From 3f0816e3d926e0ea78ac7b6cd43efe62355885c8 Mon Sep 17 00:00:00 2001 From: Misbakh-Soloviev Vadim A Date: Sun, 28 Aug 2011 14:12:26 +0200 Subject: add russian translation and add unicode compatibility (merges pull request #20) --- .gitignore | 9 + bitcoin-qt.pro | 1 - scripts/extract_strings_qt.py | 2 +- src/init.cpp | 12 +- src/qt/bitcoin.cpp | 6 +- src/qt/bitcoingui.cpp | 8 +- src/qt/bitcoinstrings.cpp | 173 +++++++- src/qt/locale/bitcoin_ru.ts | 892 +++++++++++++++++++++++++++++++++++++----- src/qt/transactionview.cpp | 30 +- 9 files changed, 985 insertions(+), 148 deletions(-) diff --git a/.gitignore b/.gitignore index aeeef170e6..537064d30e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,12 @@ src/bitcoin src/bitcoind .*.swp *.*~* +#compilation and Qt preprocessor part +*.o +ui_*.h +*.qm +moc_* +Makefile +bitcoin-qt +#resources cpp +qrc_*.cpp \ No newline at end of file diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index c1493f6bfe..e3dea66f10 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -152,4 +152,3 @@ CODECFORTR = UTF-8 # for lrelease/lupdate TRANSLATIONS = src/qt/locale/bitcoin_nl.ts src/qt/locale/bitcoin_de.ts \ src/qt/locale/bitcoin_ru.ts - diff --git a/scripts/extract_strings_qt.py b/scripts/extract_strings_qt.py index 56f47654c1..6627de4abf 100755 --- a/scripts/extract_strings_qt.py +++ b/scripts/extract_strings_qt.py @@ -44,7 +44,7 @@ def parse_po(text): return messages -files = ['src/base58.h', 'src/bignum.h', 'src/db.cpp', 'src/db.h', 'src/externui.h', 'src/headers.h', 'src/init.cpp', 'src/init.h', 'src/irc.cpp', 'src/irc.h', 'src/key.h', 'src/main.cpp', 'src/main.h', 'src/net.cpp', 'src/net.h', 'src/noui.h', 'src/rpc.cpp', 'src/rpc.h', 'src/script.cpp', 'src/script.h', 'src/serialize.h', 'src/strlcpy.h', 'src/uint256.h', 'src/util.cpp', 'src/util.h'] +files = ['src/base58.h', 'src/bignum.h', 'src/db.cpp', 'src/db.h', 'src/headers.h', 'src/init.cpp', 'src/init.h', 'src/irc.cpp', 'src/irc.h', 'src/key.h', 'src/main.cpp', 'src/main.h', 'src/net.cpp', 'src/net.h', 'src/noui.h', 'src/script.cpp', 'src/script.h', 'src/serialize.h', 'src/strlcpy.h', 'src/uint256.h', 'src/util.cpp', 'src/util.h'] # xgettext -n --keyword=_ $FILES child = Popen(['xgettext','--output=-','-n','--keyword=_'] + files, stdout=PIPE) diff --git a/src/init.cpp b/src/init.cpp index c565a92e0b..7d2a14fb97 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -372,21 +372,21 @@ bool AppInit2(int argc, char* argv[]) strErrors = ""; int64 nStart; - InitMessage("Loading addresses..."); + InitMessage(_("Loading addresses...")); printf("Loading addresses...\n"); nStart = GetTimeMillis(); if (!LoadAddresses()) strErrors += _("Error loading addr.dat \n"); printf(" addresses %15"PRI64d"ms\n", GetTimeMillis() - nStart); - InitMessage("Loading block index..."); + InitMessage(_("Loading block index...")); printf("Loading block index...\n"); nStart = GetTimeMillis(); if (!LoadBlockIndex()) strErrors += _("Error loading blkindex.dat \n"); printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart); - InitMessage("Loading wallet..."); + InitMessage(_("Loading wallet...")); printf("Loading wallet...\n"); nStart = GetTimeMillis(); bool fFirstRun; @@ -417,14 +417,14 @@ bool AppInit2(int argc, char* argv[]) } if (pindexBest != pindexRescan) { - InitMessage("Rescanning..."); + InitMessage(_("Rescanning...")); printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); nStart = GetTimeMillis(); pwalletMain->ScanForWalletTransactions(pindexRescan, true); printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); } - InitMessage("Done loading"); + InitMessage(_("Done loading")); printf("Done loading\n"); //// debug print @@ -547,7 +547,7 @@ bool AppInit2(int argc, char* argv[]) RandAddSeedPerfmon(); if (!CreateThread(StartNode, NULL)) - wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin"); + wxMessageBox(_("Error: CreateThread(StartNode) failed"), "Bitcoin"); if (fServer) CreateThread(ThreadRPCServer, NULL); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 9b4d88d91f..6f4815758f 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -111,6 +112,9 @@ std::string _(const char* psz) int main(int argc, char *argv[]) { + QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); + QTextCodec::setCodecForCStrings(QTextCodec::codecForTr()); + Q_INIT_RESOURCE(bitcoin); QApplication app(argc, argv); @@ -148,7 +152,7 @@ int main(int argc, char *argv[]) if (QtWin::isCompositionEnabled()) { -#ifdef Q_WS_WIN32 +#ifdef Q_OS_WIN // Windows-specific customization window.setAttribute(Qt::WA_TranslucentBackground); window.setAttribute(Qt::WA_NoSystemBackground, false); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 22987267ec..6aa14dcf88 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -59,16 +59,16 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): createActions(); // Menus - QMenu *file = menuBar()->addMenu("&File"); + QMenu *file = menuBar()->addMenu(tr("&File")); file->addAction(sendCoinsAction); file->addAction(receiveCoinsAction); file->addSeparator(); file->addAction(quitAction); - QMenu *settings = menuBar()->addMenu("&Settings"); + QMenu *settings = menuBar()->addMenu(tr("&Settings")); settings->addAction(optionsAction); - QMenu *help = menuBar()->addMenu("&Help"); + QMenu *help = menuBar()->addMenu(tr("&Help")); help->addAction(aboutAction); // Toolbars @@ -106,7 +106,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): centralWidget->addWidget(receiveCoinsPage); centralWidget->addWidget(sendCoinsPage); setCentralWidget(centralWidget); - + // Create status bar statusBar(); diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 2fa8de052b..45aadd49a5 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -44,43 +44,174 @@ QT_TRANSLATE_NOOP("bitcoin-core", "This help message\n"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Cannot obtain a lock on data directory %s. Bitcoin is probably already " "running."), +QT_TRANSLATE_NOOP("bitcoin-core", "Loading addresses..."), QT_TRANSLATE_NOOP("bitcoin-core", "Error loading addr.dat \n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."), QT_TRANSLATE_NOOP("bitcoin-core", "Error loading blkindex.dat \n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Loading wallet..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet corrupted \n"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Error loading wallet.dat: Wallet requires newer version of Bitcoin \n"), QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat \n"), +QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Done loading"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee="), QT_TRANSLATE_NOOP("bitcoin-core", "" "Warning: -paytxfee is set very high. This is the transaction fee you will " "pay if you send a transaction."), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: CreateThread(StartNode) failed"), QT_TRANSLATE_NOOP("bitcoin-core", "Warning: Disk space is low "), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Error: This transaction requires a transaction fee of at least %s because of " -"its amount, complexity, or use of recently received funds "), -QT_TRANSLATE_NOOP("bitcoin-core", "Error: Transaction creation failed "), -QT_TRANSLATE_NOOP("bitcoin-core", "Sending..."), +"Unable to bind to port %d on this computer. Bitcoin is probably already " +"running."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"This transaction is over the size limit. You can still send it for a fee of " +"%s, which goes to the nodes that process your transaction and helps to " +"support the network. Do you want to pay the fee?"), +QT_TRANSLATE_NOOP("bitcoin-core", "Enter the current passphrase to the wallet."), +QT_TRANSLATE_NOOP("bitcoin-core", "Passphrase"), +QT_TRANSLATE_NOOP("bitcoin-core", "Please supply the current wallet decryption passphrase."), +QT_TRANSLATE_NOOP("bitcoin-core", "The passphrase entered for the wallet decryption was incorrect."), +QT_TRANSLATE_NOOP("bitcoin-core", "Status"), +QT_TRANSLATE_NOOP("bitcoin-core", "Date"), +QT_TRANSLATE_NOOP("bitcoin-core", "Description"), +QT_TRANSLATE_NOOP("bitcoin-core", "Debit"), +QT_TRANSLATE_NOOP("bitcoin-core", "Credit"), +QT_TRANSLATE_NOOP("bitcoin-core", "Open for %d blocks"), +QT_TRANSLATE_NOOP("bitcoin-core", "Open until %s"), +QT_TRANSLATE_NOOP("bitcoin-core", "%d/offline?"), +QT_TRANSLATE_NOOP("bitcoin-core", "%d/unconfirmed"), +QT_TRANSLATE_NOOP("bitcoin-core", "%d confirmations"), +QT_TRANSLATE_NOOP("bitcoin-core", "Generated"), +QT_TRANSLATE_NOOP("bitcoin-core", "Generated (%s matures in %d more blocks)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Generated - Warning: This block was not received by any other nodes and will " +"probably not be accepted!"), +QT_TRANSLATE_NOOP("bitcoin-core", "Generated (not accepted)"), +QT_TRANSLATE_NOOP("bitcoin-core", "From: "), +QT_TRANSLATE_NOOP("bitcoin-core", "Received with: "), +QT_TRANSLATE_NOOP("bitcoin-core", "Payment to yourself"), +QT_TRANSLATE_NOOP("bitcoin-core", "To: "), +QT_TRANSLATE_NOOP("bitcoin-core", " Generating"), +QT_TRANSLATE_NOOP("bitcoin-core", "(not connected)"), +QT_TRANSLATE_NOOP("bitcoin-core", " %d connections %d blocks %d transactions"), +QT_TRANSLATE_NOOP("bitcoin-core", "Wallet already encrypted."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Enter the new passphrase to the wallet.\n" +"Please use a passphrase of 10 or more random characters, or eight or more " +"words."), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: The supplied passphrase was too short."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE " +"ALL OF YOUR BITCOINS!\n" +"Are you sure you wish to encrypt your wallet?"), +QT_TRANSLATE_NOOP("bitcoin-core", "Please re-enter your new wallet passphrase."), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: the supplied passphrases didn't match."), +QT_TRANSLATE_NOOP("bitcoin-core", "Wallet encryption failed."), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Error: The transaction was rejected. This might happen if some of the coins " -"in your wallet were already spent, such as if you used a copy of wallet.dat " -"and coins were spent in the copy but not marked as spent here."), -QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount"), +"Wallet Encrypted.\n" +"Remember that encrypting your wallet cannot fully protect your bitcoins from " +"being stolen by malware infecting your computer."), +QT_TRANSLATE_NOOP("bitcoin-core", "Wallet is unencrypted, please encrypt it first."), +QT_TRANSLATE_NOOP("bitcoin-core", "Enter the new passphrase for the wallet."), +QT_TRANSLATE_NOOP("bitcoin-core", "Re-enter the new passphrase for the wallet."), +QT_TRANSLATE_NOOP("bitcoin-core", "Wallet Passphrase Changed."), +QT_TRANSLATE_NOOP("bitcoin-core", "New Receiving Address"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"You should use a new address for each payment you receive.\n" +"\n" +"Label"), +QT_TRANSLATE_NOOP("bitcoin-core", "Status: "), +QT_TRANSLATE_NOOP("bitcoin-core", ", has not been successfully broadcast yet"), +QT_TRANSLATE_NOOP("bitcoin-core", ", broadcast through %d node"), +QT_TRANSLATE_NOOP("bitcoin-core", ", broadcast through %d nodes"), +QT_TRANSLATE_NOOP("bitcoin-core", "Date: "), +QT_TRANSLATE_NOOP("bitcoin-core", "Source: Generated
"), +QT_TRANSLATE_NOOP("bitcoin-core", "From: "), +QT_TRANSLATE_NOOP("bitcoin-core", "unknown"), +QT_TRANSLATE_NOOP("bitcoin-core", "To: "), +QT_TRANSLATE_NOOP("bitcoin-core", " (yours, label: "), +QT_TRANSLATE_NOOP("bitcoin-core", " (yours)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Credit: "), +QT_TRANSLATE_NOOP("bitcoin-core", "(%s matures in %d more blocks)"), +QT_TRANSLATE_NOOP("bitcoin-core", "(not accepted)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Debit: "), +QT_TRANSLATE_NOOP("bitcoin-core", "Transaction fee: "), +QT_TRANSLATE_NOOP("bitcoin-core", "Net amount: "), +QT_TRANSLATE_NOOP("bitcoin-core", "Message:"), +QT_TRANSLATE_NOOP("bitcoin-core", "Comment:"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Generated coins must wait 120 blocks before they can be spent. When you " +"generated this block, it was broadcast to the network to be added to the " +"block chain. If it fails to get into the chain, it will change to \"not " +"accepted\" and not be spendable. This may occasionally happen if another " +"node generates a block within a few seconds of yours."), +QT_TRANSLATE_NOOP("bitcoin-core", "Cannot write autostart/bitcoin.desktop file"), +QT_TRANSLATE_NOOP("bitcoin-core", "Main"), +QT_TRANSLATE_NOOP("bitcoin-core", "&Start Bitcoin on window system startup"), +QT_TRANSLATE_NOOP("bitcoin-core", "&Minimize on close"), +QT_TRANSLATE_NOOP("bitcoin-core", "version %s"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error in amount "), +QT_TRANSLATE_NOOP("bitcoin-core", "Send Coins"), +QT_TRANSLATE_NOOP("bitcoin-core", "Amount exceeds your balance "), +QT_TRANSLATE_NOOP("bitcoin-core", "Total exceeds your balance when the "), +QT_TRANSLATE_NOOP("bitcoin-core", " transaction fee is included "), +QT_TRANSLATE_NOOP("bitcoin-core", "Payment sent "), +QT_TRANSLATE_NOOP("bitcoin-core", "Sending..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid address "), +QT_TRANSLATE_NOOP("bitcoin-core", "Sending %s to %s"), +QT_TRANSLATE_NOOP("bitcoin-core", "CANCELLED"), +QT_TRANSLATE_NOOP("bitcoin-core", "Cancelled"), +QT_TRANSLATE_NOOP("bitcoin-core", "Transfer cancelled "), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: "), QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"), -QT_TRANSLATE_NOOP("bitcoin-core", "Invalid bitcoin address"), +QT_TRANSLATE_NOOP("bitcoin-core", "Connecting..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Unable to connect"), +QT_TRANSLATE_NOOP("bitcoin-core", "Requesting public key..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Received public key..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Recipient is not accepting transactions sent by IP address"), +QT_TRANSLATE_NOOP("bitcoin-core", "Transfer was not accepted"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid response received"), +QT_TRANSLATE_NOOP("bitcoin-core", "Creating transaction..."), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Unable to bind to port %d on this computer. Bitcoin is probably already " -"running."), -QT_TRANSLATE_NOOP("bitcoin-core", "To use the %s option"), +"This transaction requires a transaction fee of at least %s because of its " +"amount, complexity, or use of recently received funds"), +QT_TRANSLATE_NOOP("bitcoin-core", "Transaction creation failed"), +QT_TRANSLATE_NOOP("bitcoin-core", "Transaction aborted"), +QT_TRANSLATE_NOOP("bitcoin-core", "Lost connection, transaction cancelled"), +QT_TRANSLATE_NOOP("bitcoin-core", "Sending payment..."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"The transaction was rejected. This might happen if some of the coins in " +"your wallet were already spent, such as if you used a copy of wallet.dat and " +"coins were spent in the copy but not marked as spent here."), +QT_TRANSLATE_NOOP("bitcoin-core", "Waiting for confirmation..."), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Warning: %s, you must set rpcpassword=\n" -"in the configuration file: %s\n" -"If the file does not exist, create it with owner-readable-only file " -"permissions.\n"), +"The payment was sent, but the recipient was unable to verify it.\n" +"The transaction is recorded and will credit to the recipient,\n" +"but the comment information will be blank."), +QT_TRANSLATE_NOOP("bitcoin-core", "Payment was sent, but an invalid response was received"), +QT_TRANSLATE_NOOP("bitcoin-core", "Payment completed"), +QT_TRANSLATE_NOOP("bitcoin-core", "Name"), +QT_TRANSLATE_NOOP("bitcoin-core", "Address"), +QT_TRANSLATE_NOOP("bitcoin-core", "Label"), +QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin Address"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"You must set rpcpassword= in the configuration file:\n" -"%s\n" -"If the file does not exist, create it with owner-readable-only file " -"permissions."), +"This is one of your own addresses for receiving payments and cannot be " +"entered in the address book. "), +QT_TRANSLATE_NOOP("bitcoin-core", "Edit Address"), +QT_TRANSLATE_NOOP("bitcoin-core", "Edit Address Label"), +QT_TRANSLATE_NOOP("bitcoin-core", "Add Address"), +QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin"), +QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin - Generating"), +QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin - (not connected)"), +QT_TRANSLATE_NOOP("bitcoin-core", "&Open Bitcoin"), +QT_TRANSLATE_NOOP("bitcoin-core", "&Send Bitcoins"), +QT_TRANSLATE_NOOP("bitcoin-core", "O&ptions..."), +QT_TRANSLATE_NOOP("bitcoin-core", "E&xit"), +QT_TRANSLATE_NOOP("bitcoin-core", "Program has crashed and will terminate. "), QT_TRANSLATE_NOOP("bitcoin-core", "" "Warning: Please check that your computer's date and time are correct. If " "your clock is wrong Bitcoin will not work properly."), -QT_TRANSLATE_NOOP("bitcoin-core", "-beta"), +QT_TRANSLATE_NOOP("bitcoin-core", "beta"), }; \ No newline at end of file diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts index df9a25911c..6085602f9c 100644 --- a/src/qt/locale/bitcoin_ru.ts +++ b/src/qt/locale/bitcoin_ru.ts @@ -124,6 +124,21 @@ This product includes software developed by the OpenSSL Project for use in the O Bitcoin Wallet Bitcoin-бумажник + + + &File + &Файл + + + + &Settings + &Настройки + + + + &Help + &Помощь + Tabs toolbar @@ -772,12 +787,12 @@ p, li { white-space: pre-wrap; } Open for %1 blocks - + Открыто до получения %1 блоков Open until %1 - + Открыто до %1 @@ -942,15 +957,15 @@ p, li { white-space: pre-wrap; } Open for %n block(s) - - - + Открыто до получения %n блока + Открыто до получения %n блоков + Открыто до получения %n блоков Open until %1 - + Открыто до %1 @@ -970,7 +985,7 @@ p, li { white-space: pre-wrap; } Mined balance will be available in %n more blocks - + Добытыми монетами можно будет воспользоваться через %n блок Добытыми монетами можно будет воспользоваться через %n блока Добытыми монетами можно будет воспользоваться через %n блоков @@ -1110,6 +1125,36 @@ p, li { white-space: pre-wrap; } Other Другое + + + Enter address or label to search + Введите адрес или метку для поиска + + + + Min amount + Мин. сумма + + + + Copy address + Копировать адрес + + + + Copy label + Копировать метку + + + + Edit label + Изменить метку + + + + Show details... + Показать детали... + Export Transaction Data @@ -1120,6 +1165,41 @@ p, li { white-space: pre-wrap; } Comma separated file (*.csv) Текс, разделённый запятыми (*.csv) + + + Confirmed + Подтверждено + + + + Date + Дата + + + + Type + Тип + + + + Label + Метка + + + + Address + Адрес + + + + Amount + Количество + + + + ID + + Error exporting @@ -1130,6 +1210,16 @@ p, li { white-space: pre-wrap; } Could not write to file %1. Невозможно записать в файл %1. + + + Range: + Промежуток от: + + + + to + до + WalletModel @@ -1144,336 +1234,940 @@ p, li { white-space: pre-wrap; } Bitcoin version - + Версия Usage: - + Использование: Send command to -server or bitcoind - + Отправить команду на сервер ( -server ) или демону + List commands - + Список команд + Get help for a command - + Получить помощь по команде Options: - + Опции: + Specify configuration file (default: bitcoin.conf) - + Указать конфигурационный файл вместо используемого по умолчанию (bitcoin.conf) + Specify pid file (default: bitcoind.pid) - + Указать pid-файл вместо используемого по умолчанию (bitcoin.pid) + Generate coins - + Включить добычу монет + Don't generate coins - + Выключить добычу монет + Start minimized - + Запускать минимизированным + Specify data directory - + Указать рабочую директорию + Specify connection timeout (in milliseconds) - + Указать таймаут соединения (в миллисекундах) + Connect through socks4 proxy - + Соединяться через socks4-прокси + Allow DNS lookups for addnode and connect - + Разрешить поиск в DNS для комманд "addnode" и "connect" + Add a node to connect to - + Добавить узел для соединения + Connect only to the specified node - + Соединяться только с указанным узлом + Don't accept connections from outside - + Не принимать внешние соединения + Don't attempt to use UPnP to map the listening port - + Не пытаться использовать UPnP + Attempt to use UPnP to map the listening port - + Попытаться использовать UPnP для проброса прослушиваемого порта на роутере + Fee per KB to add to transactions you send - + Комиссия (за каждый KB транзакции) + Accept command line and JSON-RPC commands - + Принимать команды из командной строки и через JSON-RPC + Run in the background as a daemon and accept commands - + Запустить в бекграунде (как демон) и принимать команды + Use the test network - + Использовать тестовую сеть + Username for JSON-RPC connections - + Имя пользователя для JSON-RPC соединений + Password for JSON-RPC connections - + Пароль для JSON-RPC соединений + Listen for JSON-RPC connections on <port> (default: 8332) - + Слушать <порт> для JSON-RPC соединений (по умолчанию: 8332) + Allow JSON-RPC connections from specified IP address - + Разрешить JSON-RPC соединения с указанного адреса + Send commands to node running on <ip> (default: 127.0.0.1) - + Отправлять команды на узел,запущенный на <IP> (по умолчанию: 127.0.0.1) + Set key pool size to <n> (default: 100) - + Установить размер key pool'а в <n> (по умолчанию: 100) + Rescan the block chain for missing wallet transactions - + Просканировать цепочку блоков в поисках пропущенных транзакций для бумажника + SSL options: (see the Bitcoin Wiki for SSL setup instructions) - + Опции SSL: (см. Bitcoin Wiki для инструкций) + Use OpenSSL (https) for JSON-RPC connections - + Использовать OpenSSL (https) для JSON-RPC соединений + Server certificate file (default: server.cert) - + Сертификат (публичный ключ) сервера (по умолчанию: server.cert) + Server private key (default: server.pem) - + Закрытый ключ сервера (по умолчанию: server.pem) + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) - + Допустимые Cipher'ы для сервера (по умолчанию: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + This help message - + Данная справка + Cannot obtain a lock on data directory %s. Bitcoin is probably already running. - + Невозможно установить блокировку на рабочую директорию %s. Возможно, бумажник уже запущен. - Error loading addr.dat - - + Loading addresses... + Загрузка адресов... - Error loading blkindex.dat + Error loading addr.dat - + Ошибка при загрузке addr.dat + - Error loading wallet.dat - - + Loading block index... + Загрузка индекса блоков... - Invalid -proxy address - + Error loading blkindex.dat + + Ошибка при загрузке blkindex.dat + - Invalid amount for -paytxfee=<amount> - + Loading wallet... + Загрузка бумажника... - Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction. - + Error loading wallet.dat: Wallet corrupted + + Ошибка загрузки wallet.dat: Бумажник повреждён + + + + Error loading wallet.dat: Wallet requires newer version of Bitcoin + + Ошибка загрузки wallet.dat: Для данного бумажника требуется более новая версия Bitcoin + - Warning: Disk space is low - + Error loading wallet.dat + + Ошибка при загрузке wallet.dat + - Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds - + Rescanning... + Сканирование... + + + + Done loading + Загрузка завершена + + + + Invalid -proxy address + Ошибка в адресе прокси - Error: Transaction creation failed - + Invalid amount for -paytxfee=<amount> + Ошибка в сумме комиссии - Sending... - + Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction. + ВНИМАНИЕ: Установлена слишком большая комиссия (-paytxfee=). Данный параметр отвечает за комиссию, которую Вы будете добавлять к сумме при осуществлении транзакций. - - Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here. - + + Error: CreateThread(StartNode) failed + Ошибка: Созданиние потока (запуск узла) не удался - - Invalid amount - + + Warning: Disk space is low + ВНИМАНИЕ: На диске заканчивается свободное пространство - - Insufficient funds - + + This transaction is over the size limit. You can still send it for a fee of %s, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Данная транзакция превышает предельно допустимый размер. Но Вы можете всё равно совершить ей, добавив комиссию в %s, которая отправится тем узлам, которые обработают Вашу транзакцию и поможет поддержать сеть. Вы хотите добавить комиссию? - - Invalid bitcoin address - + + Enter the current passphrase to the wallet. + Введите текущий пароль от бумажника. - - Unable to bind to port %d on this computer. Bitcoin is probably already running. - + + Passphrase + Пароль - - To use the %s option - + + Please supply the current wallet decryption passphrase. + Пожалуйста, укажите текущий пароль для расшифровки бумажника. - - Warning: %s, you must set rpcpassword=<password> -in the configuration file: %s -If the file does not exist, create it with owner-readable-only file permissions. - - + + The passphrase entered for the wallet decryption was incorrect. + Указанный пароль не подходит. + + + + Status + Статус - You must set rpcpassword=<password> in the configuration file: -%s -If the file does not exist, create it with owner-readable-only file permissions. - + Date + Дата + + + + Description + Описание + + + + Debit + Дебет + + + + Credit + Кредит + + + + Open for %d blocks + Открыто до получения %d блоков - Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly. - + Open until %s + Открыто до %s + + + + %d/offline? + %d/оффлайн? + + + + %d/unconfirmed + %d/не подтверждено - -beta - + %d confirmations + %d подтверждений + + + + Generated + Сгенерированно + + + + Generated (%s matures in %d more blocks) + Сгенерированно (%s «созреет» через %d блоков) + + + + Generated - Warning: This block was not received by any other nodes and will probably not be accepted! + Сгенерированно - ВНИМАНИЕ: Данный блок не был получен ни одним другим узлом и, возможно, не будет подтверждён! + + + + Generated (not accepted) + Сгенерированно (не подтверждено) + + + + From: + Отправитель: + + + + Received with: + Получатель: + + + + Payment to yourself + Отправлено себе + + + + To: + Получатель: + + + + Generating + Генерация + + + + (not connected) + (не подключено) + + + + %d connections %d blocks %d transactions + %d подключений %d блоков %d транзакций + + + + Wallet already encrypted. + Бумажник уже зашифрован. + + + + Enter the new passphrase to the wallet. +Please use a passphrase of 10 or more random characters, or eight or more words. + Введите новый пароль для бумажника. +Пожалуйста, используейте пароль из 10 и более случайных символов или из 8 и более слов. + + + + Error: The supplied passphrase was too short. + ОШИБКА: Указанный пароль слишком короткий. + + + + WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR BITCOINS! +Are you sure you wish to encrypt your wallet? + ВНИМАНИЕ: Если Вы зашифруете Ваш бумажник и потеряете Ваш пароль — Вы ПОТЕРЯЕТЕ ВСЕ ВАШИ БИТКОИНЫ!!! +Вы уверены, что хотите зашифровать бумажник? + + + + Please re-enter your new wallet passphrase. + Пожалуйста, повторите ввод нового пароля. + + + + Error: the supplied passphrases didn't match. + ОШИБКА: указанные пароли не совпадают. + + + + Wallet encryption failed. + Шифрование бумажника не удалось. + + + + Wallet Encrypted. +Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer. + Бумажник зашифрован. +Запомните, что шифрование Вашего бумажника не может ПОЛНОСТЬЮ гарантировать защиту Ваших биткоинов от того, чтобы быть украденными с помощью шпионского ПО на Вашем компьютере. Пожалуйста, следите за безопасностью Вашего компьютера самостоятельно. + + + + Wallet is unencrypted, please encrypt it first. + Бумажник не зашифрован. Сначала зашифруйте его. + + + + Enter the new passphrase for the wallet. + Введите новый пароль для бумажника. + + + + Re-enter the new passphrase for the wallet. + Пожалуйста, повторите ввод нового пароля. + + + + Wallet Passphrase Changed. + Пароль от бумажника изменён. + + + + New Receiving Address + Новый адрес для получения + + + + You should use a new address for each payment you receive. + +Label + Вы должны использовать новый адрес для каждого платежа, который Вы получаете. + +Метка + + + + <b>Status:</b> + <b>Статус:</b> + + + + , has not been successfully broadcast yet + , ещё не было успешно разослано + + + + , broadcast through %d node + , разослано через %d узел + + + + , broadcast through %d nodes + , разослано через %d узлов + + + + <b>Date:</b> + <b>Дата:</b> + + + + <b>Source:</b> Generated<br> + <b>Источник:</b> [сгенерированно]<br> + + + + <b>From:</b> + <b>Отправитель:</b> + + + + unknown + неизвестно + + + + <b>To:</b> + <b>Получатель:</b> + + + + (yours, label: + (Ваш, метка: + + + + (yours) + (ваш) + + + + <b>Credit:</b> + <b>Кредит:</b> + + + + (%s matures in %d more blocks) + (%s «созреет» через %d блоков) + + + + (not accepted) + (не принято) + + + + <b>Debit:</b> + <b>Дебет:</b> + + + + <b>Transaction fee:</b> + <b>Комиссия:</b> + + + + <b>Net amount:</b> + <b>Общая сумма:</b> + + + + Message: + Сообщение: + + + + Comment: + Комментарий: + + + + Generated coins must wait 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, it will change to "not accepted" and not be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Сгенерированные монеты должны подождать 120 блоков прежде, чем они смогут быть отправлены. Когда Вы сгенерировали этот блок он был отправлен в сеть, чтобы он был добавлен к цепочке блоков. Если данная процедура не удастся, статус изменится на «не подтверждено» и монеты будут непередаваемыми. Такое может случайно происходить в случае, если другой узел сгенерирует блок на несколько секунд раньше. + + + + Cannot write autostart/bitcoin.desktop file + Не возможно записать файл autostart/bitcoin.desktop + + + + Main + Основное + + + + &Start Bitcoin on window system startup + &Запускать бумажник при входе в систему + + + + &Minimize on close + С&ворачивать вместо закрытия + + + + version %s + версия %s + + + + Error in amount + Ошибка в количестве + + + + Send Coins + Отправка + + + + Amount exceeds your balance + Сумма превышает Ваш баланс + + + + Total exceeds your balance when the + Общая сумма превысит Ваш баланс, если к транзакции будет добавлено ещё + + + + transaction fee is included + в качестве комиссии + + + + Payment sent + Платёж отправлен + + + + Invalid address + Ошибочный адрес + + + + Sending %s to %s + Отправка %s адресату %s + + + + CANCELLED + ОТМЕНЕНО + + + + Cancelled + Отменено + + + + Transfer cancelled + Транзакция отменена + + + + Error: + ОШИБКА: + + + + Connecting... + Подключение... + + + + Unable to connect + Невозможно подключиться + + + + Requesting public key... + Запрашивается открытый ключ... + + + + Received public key... + Получается публичный ключ... + + + + Recipient is not accepting transactions sent by IP address + Получатель не принимает транзакции, отправленные на IP адрес + + + + Transfer was not accepted + Передача была отвергнута + + + + Invalid response received + Получен неверный ответ + + + + Creating transaction... + Создание транзакции... + + + + This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds + Данная транзакция требует добавления комиссии (по крайней мере в %s) из-за её размера, сложности, или из-за использования недавно полученных монет + + + + Transaction creation failed + Создание транзакции провалилось + + + + Transaction aborted + Транзакция отменена + + + + Lost connection, transaction cancelled + Потеряно соединение, транзакция отменена + + + + Sending payment... + Отправка платежа... + + + + The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here. + В транзакции отказано. Такое может произойти, если некоторые монеты уже были потрачены, например, если Вы используете одну копию бумажника (wallet.dat), а монеты были потрачены из другой копии, но не были отмечены как потраченные в этой. Или в случае кражи (компрометации) Вашего бумажника. + + + + Waiting for confirmation... + Ожидание подтверждения... + + + + The payment was sent, but the recipient was unable to verify it. +The transaction is recorded and will credit to the recipient, +but the comment information will be blank. + Платёж был отправлен, но получатель не смог подтвердить его. +Транзакция записана и будет зачислена получателю, +но комментарий к платежу будет пустым. + + + + Payment was sent, but an invalid response was received + Платёж был отправлен, но был получен неверный ответ + + + + Payment completed + Платёж завершён + + + + Name + Имя + + + + Address + Адрес + + + + Label + Метка + + + + Bitcoin Address + Bitcoin-адрес + + + + This is one of your own addresses for receiving payments and cannot be entered in the address book. + Это один из Ваших личных адресов для получения платежей. Он не может быть добавлен в адресную книгу. + + + + Edit Address + Изменить адрес + + + + Edit Address Label + Изменить метку + + + + Add Address + Добавить адрес + + + + Bitcoin + + + + + Bitcoin - Generating + Bitcoin - Генерация + + + + Bitcoin - (not connected) + Bitcoin - (нет связи) + + + + &Open Bitcoin + &Показать бумажник + + + + &Send Bitcoins + Отп&равка + + + + O&ptions... + Оп&ции... + + + + E&xit + Вы&ход + + + + Program has crashed and will terminate. + Программа экстренно завершилась и будет уничтожена. + + + + beta + бета + + + + Sending... + Отправка... + + + + Insufficient funds + Недостаточно монет + + + + Unable to bind to port %d on this computer. Bitcoin is probably already running. + Невозможно забиндить порт %d на данном компьютере. Возможно, бумажник ужк запущен. + + + + Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly. + ВНИМАНИЕ: Проверьте дату и время, установленные на Вашем компьютере. Если Ваши часы идут не правильно Bitcoin может наботать не корректно. diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 0b2a3e6092..b2777b7b20 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -71,13 +71,13 @@ TransactionView::TransactionView(QWidget *parent) : addressWidget = new QLineEdit(this); #if QT_VERSION >= 0x040700 - addressWidget->setPlaceholderText("Enter address or label to search"); + addressWidget->setPlaceholderText(tr("Enter address or label to search")); #endif hlayout->addWidget(addressWidget); amountWidget = new QLineEdit(this); #if QT_VERSION >= 0x040700 - amountWidget->setPlaceholderText("Min amount"); + amountWidget->setPlaceholderText(tr("Min amount")); #endif amountWidget->setMaximumWidth(100); amountWidget->setMinimumWidth(100); @@ -105,10 +105,10 @@ TransactionView::TransactionView(QWidget *parent) : transactionView = view; // Actions - QAction *copyAddressAction = new QAction("Copy address", this); - QAction *copyLabelAction = new QAction("Copy label", this); - QAction *editLabelAction = new QAction("Edit label", this); - QAction *showDetailsAction = new QAction("Show details...", this); + QAction *copyAddressAction = new QAction(tr("Copy address"), this); + QAction *copyLabelAction = new QAction(tr("Copy label"), this); + QAction *editLabelAction = new QAction(tr("Edit label"), this); + QAction *showDetailsAction = new QAction(tr("Show details..."), this); contextMenu = new QMenu(); contextMenu->addAction(copyAddressAction); @@ -251,13 +251,13 @@ void TransactionView::exportClicked() // name, column, role writer.setModel(transactionProxyModel); - writer.addColumn("Confirmed", 0, TransactionTableModel::ConfirmedRole); - writer.addColumn("Date", 0, TransactionTableModel::DateRole); - writer.addColumn("Type", TransactionTableModel::Type, Qt::EditRole); - writer.addColumn("Label", 0, TransactionTableModel::LabelRole); - writer.addColumn("Address", 0, TransactionTableModel::AddressRole); - writer.addColumn("Amount", 0, TransactionTableModel::FormattedAmountRole); - writer.addColumn("ID", 0, TransactionTableModel::TxIDRole); + writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole); + writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole); + writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole); + writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole); + writer.addColumn(tr("Address"), 0, TransactionTableModel::AddressRole); + writer.addColumn(tr("Amount"), 0, TransactionTableModel::FormattedAmountRole); + writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole); if(!writer.write()) { @@ -349,7 +349,7 @@ QWidget *TransactionView::createDateRangeWidget() QHBoxLayout *layout = new QHBoxLayout(dateRangeWidget); layout->setContentsMargins(0,0,0,0); layout->addSpacing(23); - layout->addWidget(new QLabel("Range:")); + layout->addWidget(new QLabel(tr("Range:"))); dateFrom = new QDateTimeEdit(this); dateFrom->setDisplayFormat("dd/MM/yy"); @@ -357,7 +357,7 @@ QWidget *TransactionView::createDateRangeWidget() dateFrom->setMinimumWidth(100); dateFrom->setDate(QDate::currentDate().addDays(-7)); layout->addWidget(dateFrom); - layout->addWidget(new QLabel("to")); + layout->addWidget(new QLabel(tr("to"))); dateTo = new QDateTimeEdit(this); dateTo->setDisplayFormat("dd/MM/yy"); -- cgit v1.2.3 From b7bcaf940d27fa8cfe89422943fbeaab7a350930 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 24 Aug 2011 22:07:26 +0200 Subject: Wallet encryption part 2: ask passphrase when needed, add menu options --- bitcoin-qt.pro | 9 +- doc/assets-attribution.txt | 5 + src/qt/addresstablemodel.cpp | 8 ++ src/qt/addresstablemodel.h | 7 +- src/qt/askpassphrasedialog.cpp | 186 ++++++++++++++++++++++++++++++++++++ src/qt/askpassphrasedialog.h | 40 ++++++++ src/qt/bitcoin.qrc | 1 + src/qt/bitcoingui.cpp | 53 ++++++++++ src/qt/bitcoingui.h | 5 + src/qt/editaddressdialog.cpp | 5 + src/qt/forms/askpassphrasedialog.ui | 148 ++++++++++++++++++++++++++++ src/qt/guiconstants.h | 3 + src/qt/res/icons/key.png | Bin 0 -> 1239 bytes src/qt/sendcoinsdialog.cpp | 9 +- src/qt/walletmodel.cpp | 76 +++++++++++++++ src/qt/walletmodel.h | 36 ++++++- 16 files changed, 582 insertions(+), 9 deletions(-) create mode 100644 src/qt/askpassphrasedialog.cpp create mode 100644 src/qt/askpassphrasedialog.h create mode 100644 src/qt/forms/askpassphrasedialog.ui create mode 100644 src/qt/res/icons/key.png diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index e3dea66f10..28c5a33819 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -90,7 +90,8 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/sendcoinsentry.h \ src/qt/qvalidatedlineedit.h \ src/qt/bitcoinunits.h \ - src/qt/qvaluecombobox.h + src/qt/qvaluecombobox.h \ + src/qt/askpassphrasedialog.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -134,7 +135,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/sendcoinsentry.cpp \ src/qt/qvalidatedlineedit.cpp \ src/qt/bitcoinunits.cpp \ - src/qt/qvaluecombobox.cpp + src/qt/qvaluecombobox.cpp \ + src/qt/askpassphrasedialog.cpp RESOURCES += \ src/qt/bitcoin.qrc @@ -146,7 +148,8 @@ FORMS += \ src/qt/forms/editaddressdialog.ui \ src/qt/forms/transactiondescdialog.ui \ src/qt/forms/overviewpage.ui \ - src/qt/forms/sendcoinsentry.ui + src/qt/forms/sendcoinsentry.ui \ + src/qt/forms/askpassphrasedialog.ui CODECFORTR = UTF-8 # for lrelease/lupdate diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index d498e8b4a8..91d2e65804 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -64,5 +64,10 @@ Designer: Crobbo (forum) Site: https://bitcointalk.org/index.php?topic=32273.0 License: Public domain +Icon: src/qt/res/icons/key.png +Designer: VisualPharm (Ivan Boyko) +Icon Pack: Must Have +Site: http://findicons.com/icon/51009/key?id=51009 +License: Creative Commons Attribution (by) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index bd314ba0f0..6bda1e770c 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -267,6 +267,14 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con else if(type == Receive) { // Generate a new address to associate with given label + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet failed or was cancelled + editStatus = WALLET_UNLOCK_FAILURE; + return QString(); + } + strAddress = CBitcoinAddress(wallet->GetOrReuseKeyFromPool()).ToString(); } else diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 296fa58054..bc505c48f0 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -26,9 +26,10 @@ public: // Return status of last edit/insert operation enum EditStatus { - OK = 0, - INVALID_ADDRESS = 1, - DUPLICATE_ADDRESS = 2 + OK, + INVALID_ADDRESS, + DUPLICATE_ADDRESS, + WALLET_UNLOCK_FAILURE }; static const QString Send; /* Send addres */ diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp new file mode 100644 index 0000000000..a297513a62 --- /dev/null +++ b/src/qt/askpassphrasedialog.cpp @@ -0,0 +1,186 @@ +#include "askpassphrasedialog.h" +#include "ui_askpassphrasedialog.h" + +#include "guiconstants.h" +#include "walletmodel.h" + +#include +#include + +AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : + QDialog(parent), + ui(new Ui::AskPassphraseDialog), + mode(mode), + model(0) +{ + ui->setupUi(this); + ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE); + ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE); + ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE); + + switch(mode) + { + case Encrypt: // Ask passphrase x2 + ui->passLabel1->hide(); + ui->passEdit1->hide(); + ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.
Please use a passphrase of 10 or more random characters, or eight or more words.")); + setWindowTitle(tr("Encrypt wallet")); + break; + case Unlock: // Ask passphrase + ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet.")); + ui->passLabel2->hide(); + ui->passEdit2->hide(); + ui->passLabel3->hide(); + ui->passEdit3->hide(); + setWindowTitle(tr("Unlock wallet")); + break; + case Decrypt: // Ask passphrase + ui->warningLabel->setText(tr("This operation needs your wallet passphrase to decrypt the wallet.")); + ui->passLabel2->hide(); + ui->passEdit2->hide(); + ui->passLabel3->hide(); + ui->passEdit3->hide(); + setWindowTitle(tr("Decrypt wallet")); + break; + case ChangePass: // Ask old passphrase + new passphrase x2 + setWindowTitle(tr("Change passphrase")); + ui->warningLabel->setText(tr("Enter the old and new passphrase to the wallet.")); + break; + } + resize(minimumSize()); // Get rid of extra space in dialog + + textChanged(); + connect(ui->passEdit1, SIGNAL(textChanged(QString)), this, SLOT(textChanged())); + connect(ui->passEdit2, SIGNAL(textChanged(QString)), this, SLOT(textChanged())); + connect(ui->passEdit3, SIGNAL(textChanged(QString)), this, SLOT(textChanged())); +} + +AskPassphraseDialog::~AskPassphraseDialog() +{ + // Attempt to overwrite text so that they do not linger around in memory + ui->passEdit1->setText(QString(" ").repeated(ui->passEdit1->text().size())); + ui->passEdit2->setText(QString(" ").repeated(ui->passEdit2->text().size())); + ui->passEdit3->setText(QString(" ").repeated(ui->passEdit3->text().size())); + delete ui; +} + +void AskPassphraseDialog::setModel(WalletModel *model) +{ + this->model = model; +} + +void AskPassphraseDialog::accept() +{ + std::string oldpass, newpass1, newpass2; + // TODO: mlock memory / munlock on return so they will not be swapped out, really need "mlockedstring" wrapper class to do this safely + oldpass.reserve(MAX_PASSPHRASE_SIZE); + newpass1.reserve(MAX_PASSPHRASE_SIZE); + newpass2.reserve(MAX_PASSPHRASE_SIZE); + oldpass.assign(ui->passEdit1->text().toStdString()); + newpass1.assign(ui->passEdit2->text().toStdString()); + newpass2.assign(ui->passEdit3->text().toStdString()); + + switch(mode) + { + case Encrypt: { + if(newpass1.empty() || newpass2.empty()) + { + // Cannot encrypt with empty passphrase + break; + } + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm wallet encryption"), + tr("WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR BITCOINS!\nAre you sure you wish to encrypt your wallet?"), + QMessageBox::Yes|QMessageBox::Cancel, + QMessageBox::Cancel); + if(retval == QMessageBox::Yes) + { + if(newpass1 == newpass2) + { + if(model->setWalletEncrypted(true, newpass1)) + { + QMessageBox::warning(this, tr("Wallet encrypted"), + tr("Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.")); + } + else + { + QMessageBox::critical(this, tr("Wallet encryption failed"), + tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted.")); + } + QDialog::accept(); // Success + } + else + { + QMessageBox::critical(this, tr("Wallet encryption failed"), + tr("The supplied passphrases do not match.")); + } + } + else + { + QDialog::reject(); // Cancelled + } + } break; + case Unlock: + if(!model->setWalletLocked(false, oldpass)) + { + QMessageBox::critical(this, tr("Wallet unlock failed"), + tr("The passphrase entered for the wallet decryption was incorrect.")); + } + else + { + QDialog::accept(); // Success + } + break; + case Decrypt: + if(!model->setWalletEncrypted(false, oldpass)) + { + QMessageBox::critical(this, tr("Wallet decryption failed"), + tr("The passphrase entered for the wallet decryption was incorrect.")); + } + else + { + QDialog::accept(); // Success + } + break; + case ChangePass: + if(newpass1 == newpass2) + { + if(model->changePassphrase(oldpass, newpass1)) + { + QMessageBox::information(this, tr("Wallet encrypted"), + tr("Wallet passphrase was succesfully changed.")); + QDialog::accept(); // Success + } + else + { + QMessageBox::critical(this, tr("Wallet encryption failed"), + tr("The passphrase entered for the wallet decryption was incorrect.")); + } + } + else + { + QMessageBox::critical(this, tr("Wallet encryption failed"), + tr("The supplied passphrases do not match.")); + } + break; + } +} + +void AskPassphraseDialog::textChanged() +{ + // Validate input, set Ok button to enabled when accepable + bool acceptable = false; + switch(mode) + { + case Encrypt: // New passphrase x2 + acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); + break; + case Unlock: // Old passphrase x1 + case Decrypt: + acceptable = !ui->passEdit1->text().isEmpty(); + break; + case ChangePass: // Old passphrase x1, new passphrase x2 + acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); + break; + } + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable); +} diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h new file mode 100644 index 0000000000..761612cbfd --- /dev/null +++ b/src/qt/askpassphrasedialog.h @@ -0,0 +1,40 @@ +#ifndef ASKPASSPHRASEDIALOG_H +#define ASKPASSPHRASEDIALOG_H + +#include + +namespace Ui { + class AskPassphraseDialog; +} + +class WalletModel; + +class AskPassphraseDialog : public QDialog +{ + Q_OBJECT + +public: + enum Mode { + Encrypt, // Ask passphrase x2 + Unlock, // Ask passphrase + ChangePass, // Ask old passphrase + new passphrase x2 + Decrypt // Ask passphrase + }; + + explicit AskPassphraseDialog(Mode mode, QWidget *parent = 0); + ~AskPassphraseDialog(); + + void accept(); + + void setModel(WalletModel *model); + +private: + Ui::AskPassphraseDialog *ui; + Mode mode; + WalletModel *model; + +private slots: + void textChanged(); +}; + +#endif // ASKPASSPHRASEDIALOG_H diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 1d5a58a4af..be0e4dce61 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -36,6 +36,7 @@ res/icons/tx_inout.png res/icons/lock_closed.png res/icons/lock_open.png + res/icons/key.png
res/images/about.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6aa14dcf88..0c2eaab152 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -19,6 +19,7 @@ #include "overviewpage.h" #include "bitcoinunits.h" #include "guiconstants.h" +#include "askpassphrasedialog.h" #include #include @@ -48,6 +49,8 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QMainWindow(parent), clientModel(0), walletModel(0), + encryptWalletAction(0), + changePassphraseAction(0), trayIcon(0) { resize(850, 550); @@ -66,6 +69,9 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): file->addAction(quitAction); QMenu *settings = menuBar()->addMenu(tr("&Settings")); + settings->addAction(encryptWalletAction); + settings->addAction(changePassphraseAction); + settings->addSeparator(); settings->addAction(optionsAction); QMenu *help = menuBar()->addMenu(tr("&Help")); @@ -199,11 +205,18 @@ void BitcoinGUI::createActions() openBitcoinAction->setToolTip(tr("Show the Bitcoin window")); exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this); exportAction->setToolTip(tr("Export the current view to a file")); + encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet"), this); + encryptWalletAction->setToolTip(tr("Encrypt or decrypt wallet")); + encryptWalletAction->setCheckable(true); + changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase"), this); + changePassphraseAction->setToolTip(tr("Change the passphrase used for wallet encryption")); connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked())); connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); connect(openBitcoinAction, SIGNAL(triggered()), this, SLOT(show())); + connect(encryptWalletAction, SIGNAL(triggered(bool)), this, SLOT(encryptWallet(bool))); + connect(changePassphraseAction, SIGNAL(triggered()), this, SLOT(changePassphrase())); } void BitcoinGUI::setClientModel(ClientModel *clientModel) @@ -254,6 +267,9 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) // Balloon popup for new transaction connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(incomingTransaction(QModelIndex,int,int))); + + // Ask for passphrase if needed + connect(walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet())); } void BitcoinGUI::createTrayIcon() @@ -544,16 +560,53 @@ void BitcoinGUI::setEncryptionStatus(int status) { case WalletModel::Unencrypted: labelEncryptionIcon->hide(); + encryptWalletAction->setChecked(false); + changePassphraseAction->setEnabled(false); + encryptWalletAction->setEnabled(true); break; case WalletModel::Unlocked: labelEncryptionIcon->show(); labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently unlocked")); + encryptWalletAction->setChecked(true); + changePassphraseAction->setEnabled(true); + encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported break; case WalletModel::Locked: labelEncryptionIcon->show(); labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently locked")); + encryptWalletAction->setChecked(true); + changePassphraseAction->setEnabled(true); + encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported break; } } + +void BitcoinGUI::encryptWallet(bool status) +{ + AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt: + AskPassphraseDialog::Decrypt, this); + dlg.setModel(walletModel); + dlg.exec(); + + setEncryptionStatus(walletModel->getEncryptionStatus()); +} + +void BitcoinGUI::changePassphrase() +{ + AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this); + dlg.setModel(walletModel); + dlg.exec(); +} + +void BitcoinGUI::unlockWallet() +{ + // Unlock wallet if needed + if(walletModel->getEncryptionStatus() == WalletModel::Locked) + { + AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this); + dlg.setModel(walletModel); + dlg.exec(); + } +} diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 4b7131710c..484987ca68 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -73,6 +73,8 @@ private: QAction *optionsAction; QAction *openBitcoinAction; QAction *exportAction; + QAction *encryptWalletAction; + QAction *changePassphraseAction; QSystemTrayIcon *trayIcon; TransactionView *transactionView; @@ -108,6 +110,9 @@ private slots: void aboutClicked(); void trayIconActivated(QSystemTrayIcon::ActivationReason reason); void incomingTransaction(const QModelIndex & parent, int start, int end); + void encryptWallet(bool status); + void changePassphrase(); + void unlockWallet(); }; #endif diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 2b3d9bf0f0..06e74db25a 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -92,6 +92,11 @@ void EditAddressDialog::accept() tr("The entered address \"%1\" is not a valid bitcoin address.").arg(ui->addressEdit->text()), QMessageBox::Ok, QMessageBox::Ok); return; + case AddressTableModel::WALLET_UNLOCK_FAILURE: + QMessageBox::critical(this, windowTitle(), + tr("Could not unlock wallet."), + QMessageBox::Ok, QMessageBox::Ok); + return; } return; diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui new file mode 100644 index 0000000000..70d9180e75 --- /dev/null +++ b/src/qt/forms/askpassphrasedialog.ui @@ -0,0 +1,148 @@ + + + AskPassphraseDialog + + + + 0 + 0 + 589 + 228 + + + + + 0 + 0 + + + + + 550 + 0 + + + + Dialog + + + + + + TextLabel + + + Qt::RichText + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Enter passphrase + + + + + + + QLineEdit::Password + + + + + + + New passphrase + + + + + + + QLineEdit::Password + + + + + + + Repeat new passphrase + + + + + + + QLineEdit::Password + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + AskPassphraseDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AskPassphraseDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index b7870199bb..0cb507501a 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -4,6 +4,9 @@ /* Milliseconds between model updates */ static const int MODEL_UPDATE_DELAY = 500; +/* Maximum passphrase length */ +static const int MAX_PASSPHRASE_SIZE = 1024; + /* Size of icons in status bar */ static const int STATUSBAR_ICONSIZE = 16; diff --git a/src/qt/res/icons/key.png b/src/qt/res/icons/key.png new file mode 100644 index 0000000000..757cad47ed Binary files /dev/null and b/src/qt/res/icons/key.png differ diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index a9a89c282b..852d789805 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -6,6 +6,7 @@ #include "optionsmodel.h" #include "sendcoinsentry.h" #include "guiutil.h" +#include "askpassphrasedialog.h" #include #include @@ -84,6 +85,13 @@ void SendCoinsDialog::on_sendButton_clicked() return; } + WalletModel::UnlockContext ctx(model->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet was cancelled + return; + } + WalletModel::SendCoinsReturn sendstatus = model->sendCoins(recipients); switch(sendstatus.status) { @@ -118,7 +126,6 @@ void SendCoinsDialog::on_sendButton_clicked() tr("Error: Transaction creation failed "), QMessageBox::Ok, QMessageBox::Ok); break; - break; case WalletModel::TransactionCommitFailed: QMessageBox::warning(this, tr("Send Coins"), tr("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."), diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 9a7b56d33e..dfededca93 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -199,3 +199,79 @@ WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const return Unlocked; } } + +bool WalletModel::setWalletEncrypted(bool encrypted, const std::string &passphrase) +{ + if(encrypted) + { + // Encrypt + return wallet->EncryptWallet(passphrase); + } + else + { + // Decrypt -- TODO; not supported yet + return false; + } +} + +bool WalletModel::setWalletLocked(bool locked, const std::string &passPhrase) +{ + if(locked) + { + // Lock + return wallet->Lock(); + } + else + { + // Unlock + return wallet->Unlock(passPhrase); + } +} + +bool WalletModel::changePassphrase(const std::string &oldPass, const std::string &newPass) +{ + bool retval; + CRITICAL_BLOCK(wallet->cs_vMasterKey) + { + wallet->Lock(); // Make sure wallet is locked before attempting pass change + retval = wallet->ChangeWalletPassphrase(oldPass, newPass); + } + return retval; +} + +// WalletModel::UnlockContext implementation +WalletModel::UnlockContext WalletModel::requestUnlock() +{ + bool was_locked = getEncryptionStatus() == Locked; + if(was_locked) + { + // Request UI to unlock wallet + 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; +} diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index a585f8d8d6..b141c0762d 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -2,6 +2,7 @@ #define WALLETMODEL_H #include +#include class OptionsModel; class AddressTableModel; @@ -52,8 +53,6 @@ public: int getNumTransactions() const; EncryptionStatus getEncryptionStatus() const; - bool isEncrypted() const; - // Check address for validity bool validateAddress(const QString &address); @@ -71,6 +70,38 @@ public: // Send coins to a list of recipients SendCoinsReturn sendCoins(const QList &recipients); + + // Wallet encryption + bool setWalletEncrypted(bool encrypted, const std::string &passphrase); + // Passphrase only needed when unlocking + bool setWalletLocked(bool locked, const std::string &passPhrase=std::string()); + bool changePassphrase(const std::string &oldPass, const std::string &newPass); + + // RAI object for unlocking wallet, returned by requestUnlock() + class UnlockContext + { + public: + UnlockContext(WalletModel *wallet, bool valid, bool relock); + ~UnlockContext(); + + bool isValid() const { return valid; } + + UnlockContext(const UnlockContext& obj) + { CopyFrom(obj); } + private: + 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(); + private: CWallet *wallet; @@ -90,6 +121,7 @@ signals: void balanceChanged(qint64 balance, qint64 unconfirmedBalance); void numTransactionsChanged(int count); void encryptionStatusChanged(int status); + void requireUnlock(); // Asynchronous error notification void error(const QString &title, const QString &message); -- cgit v1.2.3 From 6c85cbecf1198d1052c9835ff7e23bb2966d9503 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 31 Aug 2011 16:08:31 +0200 Subject: comments and readme update --- README.rst | 4 ++-- src/qt/bitcoinamountfield.cpp | 1 + src/qt/bitcoingui.cpp | 2 +- src/qt/walletmodel.h | 23 +++++++++++++++-------- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/README.rst b/README.rst index 45fb5a3a50..c245bdd1f4 100644 --- a/README.rst +++ b/README.rst @@ -12,7 +12,7 @@ This has been implemented: - Compatibility with Linux (both GNOME and KDE), MacOSX and Windows -- All functionality of the original client, including taskbar icon/menu +- All functionality of the original client, including taskbar icon/menu and wallet encryption - Splash screen @@ -44,7 +44,7 @@ This has to be done: - Start at system start -- Support more languages +- Support more languages (please send translations) Build instructions =================== diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index ea38cc86dd..f1edc62bbe 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -161,6 +161,7 @@ void BitcoinAmountField::unitChanged(int idx) if(valid) { + // If value was valid, re-place it in the widget with the new unit setValue(currentValue); } else diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 0c2eaab152..e15941e7dd 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -602,7 +602,7 @@ void BitcoinGUI::changePassphrase() void BitcoinGUI::unlockWallet() { - // Unlock wallet if needed + // Unlock wallet when requested by wallet model if(walletModel->getEncryptionStatus() == WalletModel::Locked) { AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this); diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index b141c0762d..b7b6973b3b 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -23,7 +23,7 @@ class WalletModel : public QObject public: explicit WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0); - enum StatusCode + enum StatusCode // Returned by sendCoins { OK, InvalidAmount, @@ -31,7 +31,7 @@ public: AmountExceedsBalance, AmountWithFeeExceedsBalance, DuplicateAddress, - TransactionCreationFailed, + TransactionCreationFailed, // Error returned when wallet is still locked TransactionCommitFailed, Aborted, MiscError @@ -86,12 +86,9 @@ public: bool isValid() const { return valid; } - UnlockContext(const UnlockContext& obj) - { CopyFrom(obj); } - private: - UnlockContext& operator=(const UnlockContext& rhs) - { CopyFrom(rhs); return *this; } - + // 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; @@ -112,15 +109,25 @@ private: AddressTableModel *addressTableModel; TransactionTableModel *transactionTableModel; + // Cache some values to be able to detect changes qint64 cachedBalance; qint64 cachedUnconfirmedBalance; qint64 cachedNumTransactions; EncryptionStatus cachedEncryptionStatus; signals: + // Signal that balance in wallet changed void balanceChanged(qint64 balance, qint64 unconfirmedBalance); + + // Number of transactions in wallet changed void numTransactionsChanged(int count); + + // 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(); // Asynchronous error notification -- cgit v1.2.3 From c5aa1b139a983613b1d5acc8f57804a9c66d4ff1 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 2 Sep 2011 18:02:22 +0200 Subject: update to work with new lock system, add protocol.* to build system --- bitcoin-qt.pro | 7 +++++-- src/main.cpp | 11 +++++++++-- src/main.h | 1 + src/qt/addresstablemodel.cpp | 22 +++++++++++++--------- src/qt/addresstablemodel.h | 3 ++- src/qt/clientmodel.cpp | 2 +- src/qt/editaddressdialog.cpp | 5 +++++ src/qt/transactiondesc.cpp | 4 ++-- src/qt/transactiontablemodel.cpp | 10 +++++----- src/qt/walletmodel.cpp | 8 ++++---- src/wallet.cpp | 2 +- 11 files changed, 48 insertions(+), 27 deletions(-) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 28c5a33819..84712e3b0f 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -91,7 +91,9 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/qvalidatedlineedit.h \ src/qt/bitcoinunits.h \ src/qt/qvaluecombobox.h \ - src/qt/askpassphrasedialog.h + src/qt/askpassphrasedialog.h \ + src/protocol.h + SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -136,7 +138,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/qvalidatedlineedit.cpp \ src/qt/bitcoinunits.cpp \ src/qt/qvaluecombobox.cpp \ - src/qt/askpassphrasedialog.cpp + src/qt/askpassphrasedialog.cpp \ + src/protocol.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/src/main.cpp b/src/main.cpp index 5d29492fcb..59a6984288 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,6 +31,7 @@ map mapBlockIndex; uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); const int nTotalBlocksEstimate = 140700; // Conservative estimate of total nr of blocks on main chain +int nMaxBlocksOfOtherNodes = 0; // Maximum amount of blocks that other nodes claim to have const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download" CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; @@ -726,6 +727,12 @@ int GetTotalBlocksEstimate() } } +// Return maximum amount of blocks that other nodes claim to have +int GetMaxBlocksOfOtherNodes() +{ + return nMaxBlocksOfOtherNodes; +} + bool IsInitialBlockDownload() { if (pindexBest == NULL || nBestHeight < (GetTotalBlocksEstimate()-nInitialBlockThreshold)) @@ -1837,9 +1844,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight); - if(pfrom->nStartingHeight > nTotalBlocksEstimate) + if(pfrom->nStartingHeight > nMaxBlocksOfOtherNodes) { - nTotalBlocksEstimate = pfrom->nStartingHeight; + nMaxBlocksOfOtherNodes = pfrom->nStartingHeight; } } diff --git a/src/main.h b/src/main.h index 427067bc94..238cb5d8c2 100644 --- a/src/main.h +++ b/src/main.h @@ -99,6 +99,7 @@ void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); bool CheckProofOfWork(uint256 hash, unsigned int nBits); int GetTotalBlocksEstimate(); +int GetMaxBlocksOfOtherNodes(); bool IsInitialBlockDownload(); std::string GetWarnings(std::string strFor); diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 6bda1e770c..8fd6d52b7e 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -39,8 +39,7 @@ struct AddressTablePriv { cachedAddressTable.clear(); - CRITICAL_BLOCK(wallet->cs_KeyStore) - CRITICAL_BLOCK(wallet->cs_mapAddressBook) + CRITICAL_BLOCK(wallet->cs_wallet) { BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, std::string)& item, wallet->mapAddressBook) { @@ -170,7 +169,7 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu // Double-check that we're not overwriting a receiving address if(rec->type == AddressTableEntry::Sending) { - CRITICAL_BLOCK(wallet->cs_mapAddressBook) + CRITICAL_BLOCK(wallet->cs_wallet) { // Remove old entry wallet->DelAddressBookName(rec->address.toStdString()); @@ -255,7 +254,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con return QString(); } // Check for duplicate addresses - CRITICAL_BLOCK(wallet->cs_mapAddressBook) + CRITICAL_BLOCK(wallet->cs_wallet) { if(wallet->mapAddressBook.count(strAddress)) { @@ -274,15 +273,20 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con editStatus = WALLET_UNLOCK_FAILURE; return QString(); } - - strAddress = CBitcoinAddress(wallet->GetOrReuseKeyFromPool()).ToString(); + std::vector newKey; + if(!wallet->GetKeyFromPool(newKey, true)) + { + editStatus = KEY_GENERATION_FAILURE; + return QString(); + } + strAddress = CBitcoinAddress(newKey).ToString(); } else { return QString(); } // Add entry and update list - CRITICAL_BLOCK(wallet->cs_mapAddressBook) + CRITICAL_BLOCK(wallet->cs_wallet) wallet->SetAddressBookName(strAddress, strLabel); updateList(); return QString::fromStdString(strAddress); @@ -298,7 +302,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex & paren // Also refuse to remove receiving addresses. return false; } - CRITICAL_BLOCK(wallet->cs_mapAddressBook) + CRITICAL_BLOCK(wallet->cs_wallet) { wallet->DelAddressBookName(rec->address.toStdString()); } @@ -315,7 +319,7 @@ void AddressTableModel::update() */ QString AddressTableModel::labelForAddress(const QString &address) const { - CRITICAL_BLOCK(wallet->cs_mapAddressBook) + CRITICAL_BLOCK(wallet->cs_wallet) { CBitcoinAddress address_parsed(address.toStdString()); std::map::iterator mi = wallet->mapAddressBook.find(address_parsed); diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index bc505c48f0..f4a8dad845 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -29,7 +29,8 @@ public: OK, INVALID_ADDRESS, DUPLICATE_ADDRESS, - WALLET_UNLOCK_FAILURE + WALLET_UNLOCK_FAILURE, + KEY_GENERATION_FAILURE }; static const QString Send; /* Send addres */ diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 5cf02eac7d..d307473102 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -61,7 +61,7 @@ bool ClientModel::inInitialBlockDownload() const int ClientModel::getTotalBlocksEstimate() const { - return GetTotalBlocksEstimate(); + return GetMaxBlocksOfOtherNodes(); } OptionsModel *ClientModel::getOptionsModel() diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 06e74db25a..b8e6fe4578 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -97,6 +97,11 @@ void EditAddressDialog::accept() tr("Could not unlock wallet."), QMessageBox::Ok, QMessageBox::Ok); return; + case AddressTableModel::KEY_GENERATION_FAILURE: + QMessageBox::critical(this, windowTitle(), + tr("New key generation failed."), + QMessageBox::Ok, QMessageBox::Ok); + return; } return; diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 612b5d8974..6ca3ac8c4b 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -50,7 +50,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) { QString strHTML; - CRITICAL_BLOCK(wallet->cs_mapAddressBook) + CRITICAL_BLOCK(wallet->cs_wallet) { strHTML.reserve(4000); strHTML += ""; @@ -257,7 +257,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += "
Inputs:"; strHTML += "
    "; - CRITICAL_BLOCK(wallet->cs_mapWallet) + CRITICAL_BLOCK(wallet->cs_wallet) { BOOST_FOREACH(const CTxIn& txin, wtx.vin) { diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 353cd79145..0e1733f156 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -69,7 +69,7 @@ struct TransactionTablePriv qDebug() << "refreshWallet"; #endif cachedWallet.clear(); - CRITICAL_BLOCK(wallet->cs_mapWallet) + CRITICAL_BLOCK(wallet->cs_wallet) { for(std::map::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it) { @@ -95,7 +95,7 @@ struct TransactionTablePriv QList updated_sorted = updated; qSort(updated_sorted); - CRITICAL_BLOCK(wallet->cs_mapWallet) + CRITICAL_BLOCK(wallet->cs_wallet) { for(int update_idx = updated_sorted.size()-1; update_idx >= 0; --update_idx) { @@ -171,7 +171,7 @@ struct TransactionTablePriv // simply re-use the cached status. if(rec->statusUpdateNeeded()) { - CRITICAL_BLOCK(wallet->cs_mapWallet) + CRITICAL_BLOCK(wallet->cs_wallet) { std::map::iterator mi = wallet->mapWallet.find(rec->hash); @@ -191,7 +191,7 @@ struct TransactionTablePriv QString describe(TransactionRecord *rec) { - CRITICAL_BLOCK(wallet->cs_mapWallet) + CRITICAL_BLOCK(wallet->cs_wallet) { std::map::iterator mi = wallet->mapWallet.find(rec->hash); if(mi != wallet->mapWallet.end()) @@ -229,7 +229,7 @@ void TransactionTableModel::update() QList updated; // Check if there are changes to wallet map - TRY_CRITICAL_BLOCK(wallet->cs_mapWallet) + TRY_CRITICAL_BLOCK(wallet->cs_wallet) { if(!wallet->vWalletUpdated.empty()) { diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index dfededca93..2f989661f0 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -38,7 +38,7 @@ qint64 WalletModel::getUnconfirmedBalance() const int WalletModel::getNumTransactions() const { int numTransactions = 0; - CRITICAL_BLOCK(wallet->cs_mapWallet) + CRITICAL_BLOCK(wallet->cs_wallet) { numTransactions = wallet->mapWallet.size(); } @@ -117,7 +117,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QListcs_mapWallet) + CRITICAL_BLOCK(wallet->cs_wallet) { // Sendmany std::vector > vecSend; @@ -156,7 +156,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QListcs_mapAddressBook) + CRITICAL_BLOCK(wallet->cs_wallet) { if (!wallet->mapAddressBook.count(strAddress)) wallet->SetAddressBookName(strAddress, rcp.label.toStdString()); @@ -231,7 +231,7 @@ bool WalletModel::setWalletLocked(bool locked, const std::string &passPhrase) bool WalletModel::changePassphrase(const std::string &oldPass, const std::string &newPass) { bool retval; - CRITICAL_BLOCK(wallet->cs_vMasterKey) + CRITICAL_BLOCK(wallet->cs_wallet) { wallet->Lock(); // Make sure wallet is locked before attempting pass change retval = wallet->ChangeWalletPassphrase(oldPass, newPass); diff --git a/src/wallet.cpp b/src/wallet.cpp index 9c04e37fe6..d78e470396 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -731,7 +731,7 @@ int64 CWallet::GetBalance() const int64 CWallet::GetUnconfirmedBalance() const { int64 nTotal = 0; - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_wallet) { for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { -- cgit v1.2.3 From b2d1129f27a499fa00ab8f5b8f0e9f926ae66362 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 2 Sep 2011 18:05:08 +0200 Subject: bitcoin-qt cannot be used as command line rpc client --- src/init.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/init.cpp b/src/init.cpp index 7d2a14fb97..d2045fd84b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -251,6 +251,7 @@ bool AppInit2(int argc, char* argv[]) fNoListen = GetBoolArg("-nolisten") || fTOR; fLogTimestamps = GetBoolArg("-logtimestamps"); +#ifndef QT_GUI for (int i = 1; i < argc; i++) if (!IsSwitchChar(argv[i][0])) fCommandLine = true; @@ -260,6 +261,7 @@ bool AppInit2(int argc, char* argv[]) int ret = CommandLineRPC(argc, argv); exit(ret); } +#endif #ifndef __WXMSW__ if (fDaemon) -- cgit v1.2.3 From 0aca8577b55d218f72c7383b2464ca07b7f77d0d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 2 Sep 2011 18:33:09 +0200 Subject: support USE_UPNP setting --- bitcoin-qt.pro | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 84712e3b0f..951296891a 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -14,6 +14,14 @@ windows:LIBS += -lboost_system-mgw44-mt-1_43 -lboost_filesystem-mgw44-mt-1_43 -l windows:DEFINES += __WXMSW__ windows:RC_FILE = src/qt/res/bitcoin-qt.rc +# use: qmake "USE_UPNP=1" +# miniupnpc (http://miniupnp.free.fr/files/) must be installed +count(USE_UPNP, 1) { + message(Building with UPNP support) + DEFINES += USE_UPNP=$$USE_UPNP + LIBS += -lminiupnpc +} + # for extra security against potential buffer overflows QMAKE_CXXFLAGS += -fstack-protector QMAKE_LFLAGS += -fstack-protector -- cgit v1.2.3 From 4785e6df74151f91d0c1a90cf65674eb9fed4ab7 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 2 Sep 2011 19:42:59 +0300 Subject: Edited README.rst via GitHub --- README.rst | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/README.rst b/README.rst index c245bdd1f4..ceaebc1bc4 100644 --- a/README.rst +++ b/README.rst @@ -1,19 +1,13 @@ Bitcoin-qt: Qt4 based GUI replacement for Bitcoin ================================================= -**Warning** **Warning** **Warning** +Features +======== -Alpha version! I'm using this client myself on the production network, and I haven't noticed any glitches, but remember: always backup your wallet. -Testing on the testnet is recommended. - -This has been implemented: - -- qmake / QtCreator project (.pro) +- All functionality of the original client, including wallet encryption - Compatibility with Linux (both GNOME and KDE), MacOSX and Windows -- All functionality of the original client, including taskbar icon/menu and wallet encryption - - Splash screen - Tabbed interface @@ -40,12 +34,6 @@ This has been implemented: - Accepts "bitcoin:" URLs from browsers through drag and drop -This has to be done: - -- Start at system start - -- Support more languages (please send translations) - Build instructions =================== @@ -91,6 +79,30 @@ Windows build instructions: .. [#] PGP signature: http://download.visucore.com/bitcoin/qtgui_deps_1.zip.sig (signed with RSA key ID `610945D0`_) .. _`610945D0`: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x610945D0 +UPNnP port forwarding +===================== + +To use UPnP for port forwarding behind a NAT router (recommended, as more connections overall allow for a faster and more stable bitcoin experience), pass the following argument to qmake: + +:: + + qmake "USE_UPNP=1" + +(in **Qt Creator**, you can find the setting for additional qmake arguments under "Projects" -> "Build Settings" -> "Build Steps", then click "Details" next to **qmake**) + +This requires miniupnpc for UPnP port mapping. It can be downloaded from +http://miniupnp.tuxfamily.org/files/. UPnP support is not compiled in by default. + +Set USE_UPNP to a different value to control this: + ++------------+--------------------------------------------------------------+ +| USE_UPNP= | (the default) no UPnP support, miniupnp not required; | ++------------+--------------------------------------------------------------+ +| USE_UPNP=0 | UPnP support turned off by default at runtime; | ++------------+--------------------------------------------------------------+ +| USE_UPNP=1 | UPnP support turned on by default at runtime. | ++------------+--------------------------------------------------------------+ + Berkely DB version warning ========================== -- cgit v1.2.3 From cf9195c8085bade8076e064c043756024fcafa5a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 3 Sep 2011 20:52:54 +0200 Subject: (k)ubuntu 10.04+ notification support (based on @zwierzak his code) --- README.rst | 21 ++++- bitcoin-qt.pro | 12 ++- src/qt/bitcoingui.cpp | 44 +++++----- src/qt/bitcoingui.h | 2 + src/qt/notificator.cpp | 224 +++++++++++++++++++++++++++++++++++++++++++++++++ src/qt/notificator.h | 63 ++++++++++++++ 6 files changed, 337 insertions(+), 29 deletions(-) create mode 100644 src/qt/notificator.cpp create mode 100644 src/qt/notificator.h diff --git a/README.rst b/README.rst index ceaebc1bc4..b9485f4d1f 100644 --- a/README.rst +++ b/README.rst @@ -8,9 +8,9 @@ Features - Compatibility with Linux (both GNOME and KDE), MacOSX and Windows -- Splash screen +- Notification on incoming / outgoing transactions (compatible with FreeDesktop and other desktop notification schemes) -- Tabbed interface +- General interface improvements: Splash screen, tabbed interface - Overview page with current balance, unconfirmed balance, and such @@ -32,7 +32,7 @@ Features - Address books and transaction table can be sorted by any column -- Accepts "bitcoin:" URLs from browsers through drag and drop +- Accepts "bitcoin:" URLs from browsers and other sources through drag and drop Build instructions =================== @@ -79,8 +79,11 @@ Windows build instructions: .. [#] PGP signature: http://download.visucore.com/bitcoin/qtgui_deps_1.zip.sig (signed with RSA key ID `610945D0`_) .. _`610945D0`: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x610945D0 +Build configuration options +============================ + UPNnP port forwarding -===================== +--------------------- To use UPnP for port forwarding behind a NAT router (recommended, as more connections overall allow for a faster and more stable bitcoin experience), pass the following argument to qmake: @@ -103,6 +106,16 @@ Set USE_UPNP to a different value to control this: | USE_UPNP=1 | UPnP support turned on by default at runtime. | +------------+--------------------------------------------------------------+ +Notification support for recent (k)ubuntu versions +--------------------------------------------------- + +To see desktop notifications on (k)ubuntu versions starting from 10.04, enable usage of the +FreeDesktop notification interface through DBUS using the following qmake option: + +:: + + qmake "USE_DBUS=1" + Berkely DB version warning ========================== diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 951296891a..053dc70850 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -22,6 +22,12 @@ count(USE_UPNP, 1) { LIBS += -lminiupnpc } +count(USE_DBUS, 1) { + message(Building with DBUS (Freedesktop notifications) support) + DEFINES += QT_DBUS + QT += dbus +} + # for extra security against potential buffer overflows QMAKE_CXXFLAGS += -fstack-protector QMAKE_LFLAGS += -fstack-protector @@ -100,7 +106,8 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/bitcoinunits.h \ src/qt/qvaluecombobox.h \ src/qt/askpassphrasedialog.h \ - src/protocol.h + src/protocol.h \ + src/qt/notificator.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ @@ -147,7 +154,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/bitcoinunits.cpp \ src/qt/qvaluecombobox.cpp \ src/qt/askpassphrasedialog.cpp \ - src/protocol.cpp + src/protocol.cpp \ + src/qt/notificator.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index e15941e7dd..95646b1df1 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -20,6 +20,7 @@ #include "bitcoinunits.h" #include "guiconstants.h" #include "askpassphrasedialog.h" +#include "notificator.h" #include #include @@ -51,7 +52,8 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): walletModel(0), encryptWalletAction(0), changePassphraseAction(0), - trayIcon(0) + trayIcon(0), + notificator(0) { resize(850, 550); setWindowTitle(tr("Bitcoin Wallet")); @@ -287,6 +289,8 @@ void BitcoinGUI::createTrayIcon() connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); trayIcon->show(); + + notificator = new Notificator(tr("bitcoin-qt"), trayIcon); } void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) @@ -394,18 +398,7 @@ void BitcoinGUI::setNumBlocks(int count) void BitcoinGUI::error(const QString &title, const QString &message) { // Report errors from network/worker thread - if(trayIcon->supportsMessages()) - { - // Show as "balloon" message if possible - trayIcon->showMessage(title, message, QSystemTrayIcon::Critical); - } - else - { - // Fall back to old fashioned popup dialog if not - QMessageBox::critical(this, title, - message, - QMessageBox::Ok, QMessageBox::Ok); - } + notificator->notify(Notificator::Critical, title, message); } void BitcoinGUI::changeEvent(QEvent *e) @@ -453,8 +446,6 @@ void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) { - if(start == end) - return; TransactionTableModel *ttm = walletModel->getTransactionTableModel(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent) .data(Qt::EditRole).toULongLong(); @@ -468,14 +459,21 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int .data().toString(); QString address = ttm->index(start, TransactionTableModel::ToAddress, parent) .data().toString(); - - trayIcon->showMessage((amount)<0 ? tr("Sent transaction") : - tr("Incoming transaction"), - tr("Date: ") + date + "\n" + - tr("Amount: ") + BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true) + "\n" + - tr("Type: ") + type + "\n" + - tr("Address: ") + address + "\n", - QSystemTrayIcon::Information); + QIcon icon = qvariant_cast(ttm->index(start, + TransactionTableModel::ToAddress, parent) + .data(Qt::DecorationRole)); + + notificator->notify(Notificator::Information, + (amount)<0 ? tr("Sent transaction") : + tr("Incoming transaction"), + tr("Date: %1\n" + "Amount: %2\n" + "Type: %3\n" + "Address: %4\n") + .arg(date) + .arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true)) + .arg(type) + .arg(address), icon); } } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 484987ca68..59661350c3 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -11,6 +11,7 @@ class TransactionView; class OverviewPage; class AddressBookPage; class SendCoinsDialog; +class Notificator; QT_BEGIN_NAMESPACE class QLabel; @@ -77,6 +78,7 @@ private: QAction *changePassphraseAction; QSystemTrayIcon *trayIcon; + Notificator *notificator; TransactionView *transactionView; QMovie *syncIconMovie; diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp new file mode 100644 index 0000000000..86ccfc8541 --- /dev/null +++ b/src/qt/notificator.cpp @@ -0,0 +1,224 @@ +#include "notificator.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef QT_DBUS +#include +#include +#endif + +// https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128 +const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128; + +Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, QWidget *parent): + QObject(parent), + parent(parent), + programName(programName), + mode(None), + trayIcon(trayicon) +#ifdef QT_DBUS + ,interface(0) +#endif +{ + if(trayicon && trayicon->supportsMessages()) + { + mode = QSystemTray; + } +#ifdef QT_DBUS + interface = new QDBusInterface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", "org.freedesktop.Notifications"); + if(interface->isValid()) + { + mode = Freedesktop; + } +#endif +} + +Notificator::~Notificator() +{ +#ifdef QT_DBUS + delete interface; +#endif +} + +#ifdef QT_DBUS + +// Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html +class FreedesktopImage +{ +public: + FreedesktopImage() {} + FreedesktopImage(const QImage &img); + + static int metaType(); + + // Image to variant that can be marshaled over DBus + static QVariant toVariant(const QImage &img); + +private: + int width, height, stride; + bool hasAlpha; + int channels; + int bitsPerSample; + QByteArray image; + + friend QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i); + friend const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i); +}; + +Q_DECLARE_METATYPE(FreedesktopImage); + +// Image configuration settings +const int CHANNELS = 4; +const int BYTES_PER_PIXEL = 4; +const int BITS_PER_SAMPLE = 8; + +FreedesktopImage::FreedesktopImage(const QImage &img): + width(img.width()), + height(img.height()), + stride(img.width() * BYTES_PER_PIXEL), + hasAlpha(true), + channels(CHANNELS), + bitsPerSample(BITS_PER_SAMPLE) +{ + // Convert 00xAARRGGBB to RGBA bytewise (endian-independent) format + QImage tmp = img.convertToFormat(QImage::Format_ARGB32); + const uint32_t *data = reinterpret_cast(tmp.constBits()); + + unsigned int num_pixels = width * height; + image.resize(num_pixels * BYTES_PER_PIXEL); + + for(unsigned int ptr = 0; ptr < num_pixels; ++ptr) + { + image[ptr*BYTES_PER_PIXEL+0] = data[ptr] >> 16; // R + image[ptr*BYTES_PER_PIXEL+1] = data[ptr] >> 8; // G + image[ptr*BYTES_PER_PIXEL+2] = data[ptr]; // B + image[ptr*BYTES_PER_PIXEL+3] = data[ptr] >> 24; // A + } +} + +QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i) +{ + a.beginStructure(); + a << i.width << i.height << i.stride << i.hasAlpha << i.bitsPerSample << i.channels << i.image; + a.endStructure(); + return a; +} + +const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i) +{ + a.beginStructure(); + a >> i.width >> i.height >> i.stride >> i.hasAlpha >> i.bitsPerSample >> i.channels >> i.image; + a.endStructure(); + return a; +} + +int FreedesktopImage::metaType() +{ + return qDBusRegisterMetaType(); +} + +QVariant FreedesktopImage::toVariant(const QImage &img) +{ + FreedesktopImage fimg(img); + return QVariant(FreedesktopImage::metaType(), &fimg); +} + +void Notificator::notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) +{ + Q_UNUSED(cls); + // Arguments for DBus call: + QList args; + + // Program Name: + args.append(programName); + + // Unique ID of this notification type: + args.append(0U); + + // Application Icon, empty string + args.append(QString()); + + // Summary + args.append(title); + + // Body + args.append(text); + + // Actions (none, actions are deprecated) + QStringList actions; + args.append(actions); + + // Hints + QVariantMap hints; + + // If no icon specified, set icon based on class + QIcon tmpicon; + if(icon.isNull()) + { + QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion; + switch(cls) + { + case Information: sicon = QStyle::SP_MessageBoxInformation; break; + case Warning: sicon = QStyle::SP_MessageBoxWarning; break; + case Critical: sicon = QStyle::SP_MessageBoxCritical; break; + default: break; + } + tmpicon = QApplication::style()->standardIcon(sicon); + } + else + { + tmpicon = icon; + } + hints["icon_data"] = FreedesktopImage::toVariant(tmpicon.pixmap(FREEDESKTOP_NOTIFICATION_ICON_SIZE).toImage()); + args.append(hints); + + // Timeout (in msec) + args.append(millisTimeout); + + // "Fire and forget" + interface->callWithArgumentList(QDBus::NoBlock, "Notify", args); +} +#endif + +void Notificator::notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) +{ + Q_UNUSED(icon); + QSystemTrayIcon::MessageIcon sicon = QSystemTrayIcon::NoIcon; + switch(cls) // Set icon based on class + { + case Information: sicon = QSystemTrayIcon::Information; break; + case Warning: sicon = QSystemTrayIcon::Warning; break; + case Critical: sicon = QSystemTrayIcon::Critical; break; + } + trayIcon->showMessage(title, text, sicon, millisTimeout); +} + +void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) +{ + switch(mode) + { +#ifdef QT_DBUS + case Freedesktop: + notifyDBus(cls, title, text, icon, millisTimeout); + break; +#endif + case QSystemTray: + notifySystray(cls, title, text, icon, millisTimeout); + break; + default: + if(cls == Critical) + { + // Fall back to old fashioned popup dialog if critical and no other notification available + QMessageBox::critical(parent, title, text, QMessageBox::Ok, QMessageBox::Ok); + } + break; + } +} diff --git a/src/qt/notificator.h b/src/qt/notificator.h new file mode 100644 index 0000000000..13f6a908da --- /dev/null +++ b/src/qt/notificator.h @@ -0,0 +1,63 @@ +#ifndef NOTIFICATOR_H +#define NOTIFICATOR_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QSystemTrayIcon; +#ifdef QT_DBUS +class QDBusInterface; +#endif +QT_END_NAMESPACE + +// Cross-platform desktop notification client +class Notificator: public QObject +{ + Q_OBJECT +public: + // Create a new notificator + // Ownership of trayIcon is not transferred to this object + Notificator(const QString &programName=QString(), QSystemTrayIcon *trayIcon=0, QWidget *parent=0); + ~Notificator(); + + // Message class + enum Class + { + Information, + Warning, + Critical, + }; + +public slots: + + /* Show notification message. + * + * cls: general message class + * title: title shown with message + * text: message content + * icon: optional icon to show with message + * millisTimeout: notification timeout in milliseconds (default 10 seconds) + */ + void notify(Class cls, const QString &title, const QString &text, + const QIcon &icon = QIcon(), int millisTimeout = 10000); + +private: + QWidget *parent; + enum Mode { + None, + Freedesktop, // Use DBus org.freedesktop.Notifications + QSystemTray, // Use QSystemTray::showMessage + }; + QString programName; + Mode mode; + QSystemTrayIcon *trayIcon; +#ifdef QT_DBUS + QDBusInterface *interface; + + void notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout); +#endif + void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout); +}; + +#endif // NOTIFICATOR_H -- cgit v1.2.3 From 94723e27adf04eb62b3cdb40ead503ee5f40cab4 Mon Sep 17 00:00:00 2001 From: Janne Pulkkinen Date: Sat, 3 Sep 2011 20:05:54 +0300 Subject: Pull request #21: windows fixes/cleanup by Matoking --- src/qt/bitcoin.cpp | 19 ------------------ src/qt/bitcoingui.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/qt/bitcoingui.h | 2 ++ 3 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 6f4815758f..daba512adc 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -5,7 +5,6 @@ #include "clientmodel.h" #include "walletmodel.h" #include "optionsmodel.h" -#include "qtwin.h" #include "headers.h" #include "init.h" @@ -150,24 +149,6 @@ int main(int argc, char *argv[]) window.setClientModel(&clientModel); window.setWalletModel(&walletModel); - if (QtWin::isCompositionEnabled()) - { -#ifdef Q_OS_WIN - // Windows-specific customization - window.setAttribute(Qt::WA_TranslucentBackground); - window.setAttribute(Qt::WA_NoSystemBackground, false); - QPalette pal = window.palette(); - QColor bg = pal.window().color(); - bg.setAlpha(0); - pal.setColor(QPalette::Window, bg); - window.setPalette(pal); - window.ensurePolished(); - window.setAttribute(Qt::WA_StyledBackground, false); -#endif - QtWin::extendFrameIntoClientArea(&window); - window.setContentsMargins(0, 0, 0, 0); - } - window.show(); app.exec(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 95646b1df1..6d8060d179 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -21,6 +21,7 @@ #include "guiconstants.h" #include "askpassphrasedialog.h" #include "notificator.h" +#include "qtwin.h" #include #include @@ -159,6 +160,16 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Doubleclicking on a transaction on the transaction history page shows details connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); +#ifdef Q_OS_WIN + // Windows-specific customization + if (QtWin::isCompositionEnabled()) + { + QtWin::extendFrameIntoClientArea(&window); + window.setContentsMargins(0, 0, 0, 0); + } +#endif + setWindowComposition(); + gotoOverviewPage(); } @@ -216,7 +227,7 @@ void BitcoinGUI::createActions() connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked())); connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); - connect(openBitcoinAction, SIGNAL(triggered()), this, SLOT(show())); + connect(openBitcoinAction, SIGNAL(triggered()), this, SLOT(showNormal())); connect(encryptWalletAction, SIGNAL(triggered(bool)), this, SLOT(encryptWallet(bool))); connect(changePassphraseAction, SIGNAL(triggered()), this, SLOT(changePassphrase())); } @@ -297,9 +308,10 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { if(reason == QSystemTrayIcon::Trigger) { - // Doubleclick on system tray icon triggers "open bitcoin" + // Click on system tray icon triggers "open bitcoin" openBitcoinAction->trigger(); } + } void BitcoinGUI::optionsClicked() @@ -414,10 +426,12 @@ void BitcoinGUI::changeEvent(QEvent *e) } else { + show(); e->accept(); } } } + setWindowComposition(); QMainWindow::changeEvent(e); } @@ -431,6 +445,41 @@ void BitcoinGUI::closeEvent(QCloseEvent *event) QMainWindow::closeEvent(event); } +void BitcoinGUI::setWindowComposition() +{ +#ifdef Q_OS_WIN + // Make the background transparent on Windows Vista or 7, except when maximized + // Otherwise text becomes hard to read + if (QtWin::isCompositionEnabled()) + { + QPalette pal = palette(); + QColor bg = pal.window().color(); + if(isMaximized()) + { + setAttribute(Qt::WA_TranslucentBackground, false); + setAttribute(Qt::WA_StyledBackground, true); + QBrush wb = pal.window(); + bg = wb.color(); + bg.setAlpha(255); + pal.setColor(QPalette::Window, bg); + setPalette(pal); + + } + else + { + setAttribute(Qt::WA_TranslucentBackground); + setAttribute(Qt::WA_StyledBackground, false); + bg.setAlpha(0); + pal.setColor(QPalette::Window, bg); + setPalette(pal); + setAttribute(Qt::WA_NoSystemBackground, false); + ensurePolished(); + setAttribute(Qt::WA_StyledBackground, false); + } + } +#endif +} + void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) { QString strMessage = diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 59661350c3..97af431f44 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -99,6 +99,8 @@ public slots: */ void askFee(qint64 nFeeRequired, bool *payFee); + void setWindowComposition(); + private slots: // UI pages void gotoOverviewPage(); -- cgit v1.2.3 From f077d1ad62cba26b2620c87abba87be7fec15f0d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 7 Sep 2011 18:16:38 +0200 Subject: fix the build (moved code use 'this' instead of 'window') --- src/qt/bitcoingui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6d8060d179..0c9f778ff6 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -164,8 +164,8 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Windows-specific customization if (QtWin::isCompositionEnabled()) { - QtWin::extendFrameIntoClientArea(&window); - window.setContentsMargins(0, 0, 0, 0); + QtWin::extendFrameIntoClientArea(this); + setContentsMargins(0, 0, 0, 0); } #endif setWindowComposition(); -- cgit v1.2.3 From 9b9e2f1748c8d6df4ecf7a8710bf8dadb3e200aa Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 8 Sep 2011 18:07:07 +0200 Subject: http -> https --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index b9485f4d1f..e5d1f82855 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ Bitcoin-qt: Qt4 based GUI replacement for Bitcoin Features ======== -- All functionality of the original client, including wallet encryption +- All functionality of the Wx GUI, including wallet encryption - Compatibility with Linux (both GNOME and KDE), MacOSX and Windows @@ -75,8 +75,8 @@ Windows build instructions: - Open the .pro file in QT creator and build as normal (ctrl-B) .. _`QT Windows SDK`: http://qt.nokia.com/downloads/sdk-windows-cpp -.. _`dependencies archive`: http://download.visucore.com/bitcoin/qtgui_deps_1.zip -.. [#] PGP signature: http://download.visucore.com/bitcoin/qtgui_deps_1.zip.sig (signed with RSA key ID `610945D0`_) +.. _`dependencies archive`: https://download.visucore.com/bitcoin/qtgui_deps_1.zip +.. [#] PGP signature: https://download.visucore.com/bitcoin/qtgui_deps_1.zip.sig (signed with RSA key ID `610945D0`_) .. _`610945D0`: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x610945D0 Build configuration options -- cgit v1.2.3 From 78b3bf56f7804f3eb1b7cf8b189c5d567be7ca60 Mon Sep 17 00:00:00 2001 From: Janne Pulkkinen Date: Sat, 10 Sep 2011 12:43:45 +0300 Subject: The synchronization progress bar now compares the amount of total blocks to amount of blocks downloaded at application start-up. Could be probably implemented better. --- src/qt/bitcoingui.cpp | 5 +++-- src/qt/clientmodel.cpp | 8 ++++++++ src/qt/clientmodel.h | 3 +++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 0c9f778ff6..7943684eef 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -345,6 +345,7 @@ void BitcoinGUI::setNumConnections(int count) void BitcoinGUI::setNumBlocks(int count) { + int initTotal = clientModel->getNumBlocksAtStartup(); int total = clientModel->getTotalBlocksEstimate(); QString tooltip; @@ -352,8 +353,8 @@ void BitcoinGUI::setNumBlocks(int count) { progressBarLabel->setVisible(true); progressBar->setVisible(true); - progressBar->setMaximum(total); - progressBar->setValue(count); + progressBar->setMaximum(total - initTotal); + progressBar->setValue(count - initTotal); tooltip = tr("Downloaded %1 of %2 blocks of transaction history.").arg(count).arg(total); } else diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index d307473102..08abaa6b49 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -18,6 +18,8 @@ ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(MODEL_UPDATE_DELAY); + + numBlocksAtStartup = -1; } int ClientModel::getNumConnections() const @@ -30,6 +32,12 @@ int ClientModel::getNumBlocks() const return nBestHeight; } +int ClientModel::getNumBlocksAtStartup() +{ + if (numBlocksAtStartup == -1) numBlocksAtStartup = getNumBlocks(); + return numBlocksAtStartup; +} + QDateTime ClientModel::getLastBlockDate() const { return QDateTime::fromTime_t(pindexBest->GetBlockTime()); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 15387056c8..8605fb93ab 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -23,6 +23,7 @@ public: int getNumConnections() const; int getNumBlocks() const; + int getNumBlocksAtStartup(); QDateTime getLastBlockDate() const; @@ -41,6 +42,8 @@ private: int cachedNumConnections; int cachedNumBlocks; + int numBlocksAtStartup; + signals: void numConnectionsChanged(int count); void numBlocksChanged(int count); -- cgit v1.2.3 From d33cc2b5e3779033502fedc3f17aeffb4fa8e01c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 11 Sep 2011 10:49:30 +0200 Subject: clarify function signature (GetNumBlocksOfPeers) and use number of 'frozen' blocks as initial value for number of peer blocks --- src/main.cpp | 10 +++++----- src/main.h | 2 +- src/qt/bitcoingui.cpp | 2 +- src/qt/clientmodel.cpp | 4 ++-- src/qt/clientmodel.h | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 20bd94884e..4d5cb87b07 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,8 +31,8 @@ map mapBlockIndex; uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); const int nTotalBlocksEstimate = 140700; // Conservative estimate of total nr of blocks on main chain -int nMaxBlocksOfOtherNodes = 0; // Maximum amount of blocks that other nodes claim to have const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download" +int nMaxBlocksOfPeers = 0; // Amount of blocks that other nodes claim to have CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; CBigNum bnBestChainWork = 0; @@ -728,9 +728,9 @@ int GetTotalBlocksEstimate() } // Return maximum amount of blocks that other nodes claim to have -int GetMaxBlocksOfOtherNodes() +int GetNumBlocksOfPeers() { - return nMaxBlocksOfOtherNodes; + return std::max(nMaxBlocksOfPeers, GetTotalBlocksEstimate()); } bool IsInitialBlockDownload() @@ -1846,9 +1846,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight); - if(pfrom->nStartingHeight > nMaxBlocksOfOtherNodes) + if(pfrom->nStartingHeight > nMaxBlocksOfPeers) { - nMaxBlocksOfOtherNodes = pfrom->nStartingHeight; + nMaxBlocksOfPeers = pfrom->nStartingHeight; } } diff --git a/src/main.h b/src/main.h index 238cb5d8c2..d1dc72d5d9 100644 --- a/src/main.h +++ b/src/main.h @@ -99,7 +99,7 @@ void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); bool CheckProofOfWork(uint256 hash, unsigned int nBits); int GetTotalBlocksEstimate(); -int GetMaxBlocksOfOtherNodes(); +int GetNumBlocksOfPeers(); bool IsInitialBlockDownload(); std::string GetWarnings(std::string strFor); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 7943684eef..b51fd729cb 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -346,7 +346,7 @@ void BitcoinGUI::setNumConnections(int count) void BitcoinGUI::setNumBlocks(int count) { int initTotal = clientModel->getNumBlocksAtStartup(); - int total = clientModel->getTotalBlocksEstimate(); + int total = clientModel->getNumBlocksOfPeers(); QString tooltip; if(count < total) diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 08abaa6b49..2ed3ce51df 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -67,9 +67,9 @@ bool ClientModel::inInitialBlockDownload() const return IsInitialBlockDownload(); } -int ClientModel::getTotalBlocksEstimate() const +int ClientModel::getNumBlocksOfPeers() const { - return GetMaxBlocksOfOtherNodes(); + return GetNumBlocksOfPeers(); } OptionsModel *ClientModel::getOptionsModel() diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 8605fb93ab..c68fb0f035 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -32,7 +32,7 @@ public: // Return true if core is doing initial block download bool inInitialBlockDownload() const; // Return conservative estimate of total number of blocks, or 0 if unknown - int getTotalBlocksEstimate() const; + int getNumBlocksOfPeers() const; QString formatFullVersion() const; -- cgit v1.2.3 From 2c1fd3c395da9804f38d40da97d73f14e69c350f Mon Sep 17 00:00:00 2001 From: p2k Date: Sun, 11 Sep 2011 17:42:20 +0200 Subject: Some Mac OS X specific things * Added application icon for Mac OS X * Added instructions for compiling under Mac OS X * Added Portfile for compiling miniupnpc with MacPorts --- .gitignore | 4 ++++ README.rst | 33 ++++++++++++++++++++++++++++++-- bitcoin-qt.pro | 11 +++++++++++ contrib/miniupnpc/Portfile | 43 ++++++++++++++++++++++++++++++++++++++++++ src/qt/res/icons/bitcoin.icns | Bin 0 -> 99044 bytes 5 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 contrib/miniupnpc/Portfile create mode 100644 src/qt/res/icons/bitcoin.icns diff --git a/.gitignore b/.gitignore index 29c781c016..a5f96ba44c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,7 @@ Makefile bitcoin-qt #resources cpp qrc_*.cpp +#qt creator +*.pro.user +#mac specific +.DS_Store diff --git a/README.rst b/README.rst index e5d1f82855..c13134a39d 100644 --- a/README.rst +++ b/README.rst @@ -79,6 +79,27 @@ Windows build instructions: .. [#] PGP signature: https://download.visucore.com/bitcoin/qtgui_deps_1.zip.sig (signed with RSA key ID `610945D0`_) .. _`610945D0`: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x610945D0 + +Mac OS X +-------- + +- Download and install the `Qt Mac OS X SDK`_. It is recommended to also install Apple's Xcode with UNIX tools. + +- Download and install `MacPorts`_. + +- Execute the following commands in a terminal to get the dependencies: + +:: + + sudo port selfupdate + sudo port install boost db48 + +- Open the .pro file in Qt Creator and build as normal (cmd-B) + +.. _`Qt Mac OS X SDK`: http://qt.nokia.com/downloads/sdk-mac-os-cpp +.. _`MacPorts`: http://www.macports.org/install.php + + Build configuration options ============================ @@ -94,18 +115,26 @@ To use UPnP for port forwarding behind a NAT router (recommended, as more connec (in **Qt Creator**, you can find the setting for additional qmake arguments under "Projects" -> "Build Settings" -> "Build Steps", then click "Details" next to **qmake**) This requires miniupnpc for UPnP port mapping. It can be downloaded from -http://miniupnp.tuxfamily.org/files/. UPnP support is not compiled in by default. +http://miniupnp.tuxfamily.org/files/. UPnP support is not compiled in by default. Set USE_UPNP to a different value to control this: +------------+--------------------------------------------------------------+ -| USE_UPNP= | (the default) no UPnP support, miniupnp not required; | +| USE_UPNP= | (the default) no UPnP support, miniupnpc not required; | +------------+--------------------------------------------------------------+ | USE_UPNP=0 | UPnP support turned off by default at runtime; | +------------+--------------------------------------------------------------+ | USE_UPNP=1 | UPnP support turned on by default at runtime. | +------------+--------------------------------------------------------------+ +Mac OS X users: miniupnpc is currently outdated on MacPorts. An updated Portfile is provided in contrib/miniupnpc within this project. +You can execute the following commands in a terminal to install it: + +:: + + cd /contrib/miniupnpc + sudo port install + Notification support for recent (k)ubuntu versions --------------------------------------------------- diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 053dc70850..8c58cd4d39 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -174,3 +174,14 @@ CODECFORTR = UTF-8 # for lrelease/lupdate TRANSLATIONS = src/qt/locale/bitcoin_nl.ts src/qt/locale/bitcoin_de.ts \ src/qt/locale/bitcoin_ru.ts + +OTHER_FILES += \ + README.rst + +# For use with MacPorts +macx:INCLUDEPATH += /opt/local/include /opt/local/include/db48 +macx:LIBS += -L/opt/local/lib -L/opt/local/lib/db48 + +# Additional Mac options +macx:ICON = src/qt/res/icons/bitcoin.icns +macx:TARGET = "Bitcoin Qt" diff --git a/contrib/miniupnpc/Portfile b/contrib/miniupnpc/Portfile new file mode 100644 index 0000000000..133aee532c --- /dev/null +++ b/contrib/miniupnpc/Portfile @@ -0,0 +1,43 @@ +# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=tcl:et:sw=4:ts=4:sts=4 +# $Id$ + +PortSystem 1.0 + +name miniupnpc +epoch 2 +version 1.6 +revision 2 +categories net +platforms darwin +license BSD +maintainers singingwolfboy openmaintainer +description Lightweight client for UPnP protocol +long_description \ + ${description} + +homepage http://miniupnp.free.fr/ +master_sites http://miniupnp.free.fr/files/download.php?file=${distname}${extract.suffix}&dummy= +checksums md5 88055f2d4a061cfd4cfe25a9eae22f67 \ + sha1 ef8f2edb17f2e7c5b8dc67ee80a65c199d823e0a \ + rmd160 d86b75b331a3fb5525c71708548f311977c0598f + +use_configure no + +variant universal {} +if {[variant_isset universal]} { + set archflags ${configure.universal_cflags} +} else { + set archflags ${configure.cc_archflags} +} + +build.args-append CC="${configure.cc} ${archflags}" + +post-patch { + reinplace "s|-Wl,-install_name,|-Wl,-install_name,${prefix}/lib/|" ${worksrcpath}/Makefile +} + +destroot.destdir PREFIX=${prefix} INSTALLPREFIX=${destroot}${prefix} + +livecheck.type regex +livecheck.url http://miniupnp.free.fr/files/ +livecheck.regex ${name}-(\\d+(\\.\\d{1,4})+)${extract.suffix} diff --git a/src/qt/res/icons/bitcoin.icns b/src/qt/res/icons/bitcoin.icns new file mode 100644 index 0000000000..3c757080aa Binary files /dev/null and b/src/qt/res/icons/bitcoin.icns differ -- cgit v1.2.3 From 83312d7c6c9536bc1500c8b738635e62d98aa39b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 11 Sep 2011 21:06:23 +0200 Subject: remove transparency effect and windows-specific code for now, it's not working as supposed --- bitcoin-qt.pro | 2 - src/qt/bitcoingui.cpp | 47 ----------- src/qt/bitcoingui.h | 2 - src/qt/qtwin.cpp | 222 -------------------------------------------------- src/qt/qtwin.h | 37 --------- 5 files changed, 310 deletions(-) delete mode 100644 src/qt/qtwin.cpp delete mode 100644 src/qt/qtwin.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 8c58cd4d39..19d7814d39 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -99,7 +99,6 @@ HEADERS += src/qt/bitcoingui.h \ src/bitcoinrpc.h \ src/qt/overviewpage.h \ src/qt/csvmodelwriter.h \ - src/qt/qtwin.h \ src/crypter.h \ src/qt/sendcoinsentry.h \ src/qt/qvalidatedlineedit.h \ @@ -147,7 +146,6 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/bitcoinrpc.cpp \ src/qt/overviewpage.cpp \ src/qt/csvmodelwriter.cpp \ - src/qt/qtwin.cpp \ src/crypter.cpp \ src/qt/sendcoinsentry.cpp \ src/qt/qvalidatedlineedit.cpp \ diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index b51fd729cb..3e6b547006 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -21,7 +21,6 @@ #include "guiconstants.h" #include "askpassphrasedialog.h" #include "notificator.h" -#include "qtwin.h" #include #include @@ -160,16 +159,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Doubleclicking on a transaction on the transaction history page shows details connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); -#ifdef Q_OS_WIN - // Windows-specific customization - if (QtWin::isCompositionEnabled()) - { - QtWin::extendFrameIntoClientArea(this); - setContentsMargins(0, 0, 0, 0); - } -#endif - setWindowComposition(); - gotoOverviewPage(); } @@ -432,7 +421,6 @@ void BitcoinGUI::changeEvent(QEvent *e) } } } - setWindowComposition(); QMainWindow::changeEvent(e); } @@ -446,41 +434,6 @@ void BitcoinGUI::closeEvent(QCloseEvent *event) QMainWindow::closeEvent(event); } -void BitcoinGUI::setWindowComposition() -{ -#ifdef Q_OS_WIN - // Make the background transparent on Windows Vista or 7, except when maximized - // Otherwise text becomes hard to read - if (QtWin::isCompositionEnabled()) - { - QPalette pal = palette(); - QColor bg = pal.window().color(); - if(isMaximized()) - { - setAttribute(Qt::WA_TranslucentBackground, false); - setAttribute(Qt::WA_StyledBackground, true); - QBrush wb = pal.window(); - bg = wb.color(); - bg.setAlpha(255); - pal.setColor(QPalette::Window, bg); - setPalette(pal); - - } - else - { - setAttribute(Qt::WA_TranslucentBackground); - setAttribute(Qt::WA_StyledBackground, false); - bg.setAlpha(0); - pal.setColor(QPalette::Window, bg); - setPalette(pal); - setAttribute(Qt::WA_NoSystemBackground, false); - ensurePolished(); - setAttribute(Qt::WA_StyledBackground, false); - } - } -#endif -} - void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) { QString strMessage = diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 97af431f44..59661350c3 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -99,8 +99,6 @@ public slots: */ void askFee(qint64 nFeeRequired, bool *payFee); - void setWindowComposition(); - private slots: // UI pages void gotoOverviewPage(); diff --git a/src/qt/qtwin.cpp b/src/qt/qtwin.cpp deleted file mode 100644 index bb029bba24..0000000000 --- a/src/qt/qtwin.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** -** Use, modification and distribution is allowed without limitation, -** warranty, liability or support of any kind. -** -****************************************************************************/ - -#include "qtwin.h" -#include -#include -#include -#include -#include - -#ifdef Q_WS_WIN - -#include - -// Blur behind data structures -#define DWM_BB_ENABLE 0x00000001 // fEnable has been specified -#define DWM_BB_BLURREGION 0x00000002 // hRgnBlur has been specified -#define DWM_BB_TRANSITIONONMAXIMIZED 0x00000004 // fTransitionOnMaximized has been specified -#define WM_DWMCOMPOSITIONCHANGED 0x031E // Composition changed window message - -typedef struct _DWM_BLURBEHIND -{ - DWORD dwFlags; - BOOL fEnable; - HRGN hRgnBlur; - BOOL fTransitionOnMaximized; -} DWM_BLURBEHIND, *PDWM_BLURBEHIND; - -typedef struct _MARGINS -{ - int cxLeftWidth; - int cxRightWidth; - int cyTopHeight; - int cyBottomHeight; -} MARGINS, *PMARGINS; - -typedef HRESULT (WINAPI *PtrDwmIsCompositionEnabled)(BOOL* pfEnabled); -typedef HRESULT (WINAPI *PtrDwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS* pMarInset); -typedef HRESULT (WINAPI *PtrDwmEnableBlurBehindWindow)(HWND hWnd, const DWM_BLURBEHIND* pBlurBehind); -typedef HRESULT (WINAPI *PtrDwmGetColorizationColor)(DWORD *pcrColorization, BOOL *pfOpaqueBlend); - -static PtrDwmIsCompositionEnabled pDwmIsCompositionEnabled= 0; -static PtrDwmEnableBlurBehindWindow pDwmEnableBlurBehindWindow = 0; -static PtrDwmExtendFrameIntoClientArea pDwmExtendFrameIntoClientArea = 0; -static PtrDwmGetColorizationColor pDwmGetColorizationColor = 0; - - -/* - * Internal helper class that notifies windows if the - * DWM compositing state changes and updates the widget - * flags correspondingly. - */ -class WindowNotifier : public QWidget -{ -public: - WindowNotifier() { winId(); } - void addWidget(QWidget *widget) { widgets.append(widget); } - void removeWidget(QWidget *widget) { widgets.removeAll(widget); } - bool winEvent(MSG *message, long *result); - -private: - QWidgetList widgets; -}; - -static bool resolveLibs() -{ - if (!pDwmIsCompositionEnabled) { - QLibrary dwmLib(QString::fromAscii("dwmapi")); - pDwmIsCompositionEnabled =(PtrDwmIsCompositionEnabled)dwmLib.resolve("DwmIsCompositionEnabled"); - pDwmExtendFrameIntoClientArea = (PtrDwmExtendFrameIntoClientArea)dwmLib.resolve("DwmExtendFrameIntoClientArea"); - pDwmEnableBlurBehindWindow = (PtrDwmEnableBlurBehindWindow)dwmLib.resolve("DwmEnableBlurBehindWindow"); - pDwmGetColorizationColor = (PtrDwmGetColorizationColor)dwmLib.resolve("DwmGetColorizationColor"); - } - return pDwmIsCompositionEnabled != 0; -} - -#endif - -/*! - * Chekcs and returns true if Windows DWM composition - * is currently enabled on the system. - * - * To get live notification on the availability of - * this feature, you will currently have to - * reimplement winEvent() on your widget and listen - * for the WM_DWMCOMPOSITIONCHANGED event to occur. - * - */ -bool QtWin::isCompositionEnabled() -{ -#ifdef Q_WS_WIN - if (resolveLibs()) { - HRESULT hr = S_OK; - BOOL isEnabled = false; - hr = pDwmIsCompositionEnabled(&isEnabled); - if (SUCCEEDED(hr)) - return isEnabled; - } -#endif - return false; -} - -/*! - * Enables Blur behind on a Widget. - * - * \a enable tells if the blur should be enabled or not - */ -bool QtWin::enableBlurBehindWindow(QWidget *widget, bool enable) -{ - Q_ASSERT(widget); - bool result = false; -#ifdef Q_WS_WIN - if (resolveLibs()) { - DWM_BLURBEHIND bb = {0}; - HRESULT hr = S_OK; - bb.fEnable = enable; - bb.dwFlags = DWM_BB_ENABLE; - bb.hRgnBlur = NULL; - widget->setAttribute(Qt::WA_TranslucentBackground, enable); - widget->setAttribute(Qt::WA_NoSystemBackground, enable); - hr = pDwmEnableBlurBehindWindow(widget->winId(), &bb); - if (SUCCEEDED(hr)) { - result = true; - windowNotifier()->addWidget(widget); - } - } -#endif - return result; -} - -/*! - * ExtendFrameIntoClientArea. - * - * This controls the rendering of the frame inside the window. - * Note that passing margins of -1 (the default value) will completely - * remove the frame from the window. - * - * \note you should not call enableBlurBehindWindow before calling - * this functions - * - * \a enable tells if the blur should be enabled or not - */ -bool QtWin::extendFrameIntoClientArea(QWidget *widget, int left, int top, int right, int bottom) -{ - - Q_ASSERT(widget); - Q_UNUSED(left); - Q_UNUSED(top); - Q_UNUSED(right); - Q_UNUSED(bottom); - - bool result = false; -#ifdef Q_WS_WIN - if (resolveLibs()) { - QLibrary dwmLib(QString::fromAscii("dwmapi")); - HRESULT hr = S_OK; - MARGINS m = {left, top, right, bottom}; - hr = pDwmExtendFrameIntoClientArea(widget->winId(), &m); - if (SUCCEEDED(hr)) { - result = true; - windowNotifier()->addWidget(widget); - } - widget->setAttribute(Qt::WA_TranslucentBackground, result); - } -#endif - return result; -} - -/*! - * Returns the current colorizationColor for the window. - * - * \a enable tells if the blur should be enabled or not - */ -QColor QtWin::colorizatinColor() -{ - QColor resultColor = QApplication::palette().window().color(); - -#ifdef Q_WS_WIN - if (resolveLibs()) { - DWORD color = 0; - BOOL opaque = FALSE; - QLibrary dwmLib(QString::fromAscii("dwmapi")); - HRESULT hr = S_OK; - hr = pDwmGetColorizationColor(&color, &opaque); - if (SUCCEEDED(hr)) - resultColor = QColor(color); - } -#endif - return resultColor; -} - -#ifdef Q_WS_WIN -WindowNotifier *QtWin::windowNotifier() -{ - static WindowNotifier *windowNotifierInstance = 0; - if (!windowNotifierInstance) - windowNotifierInstance = new WindowNotifier; - return windowNotifierInstance; -} - - -/* Notify all enabled windows that the DWM state changed */ -bool WindowNotifier::winEvent(MSG *message, long *result) -{ - if (message && message->message == WM_DWMCOMPOSITIONCHANGED) { - bool compositionEnabled = QtWin::isCompositionEnabled(); - foreach(QWidget * widget, widgets) { - if (widget) { - widget->setAttribute(Qt::WA_NoSystemBackground, compositionEnabled); - } - widget->update(); - } - } - return QWidget::winEvent(message, result); -} -#endif diff --git a/src/qt/qtwin.h b/src/qt/qtwin.h deleted file mode 100644 index 4008c7fa28..0000000000 --- a/src/qt/qtwin.h +++ /dev/null @@ -1,37 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** -** Use, modification and distribution is allowed without limitation, -** warranty, liability or support of any kind. -** -****************************************************************************/ - -#ifndef QTWIN_H -#define QTWIN_H - -#include -#include -/** - * This is a helper class for using the Desktop Window Manager - * functionality on Windows 7 and Windows Vista. On other platforms - * these functions will simply not do anything. - */ - -class WindowNotifier; - -class QtWin -{ -public: - static bool enableBlurBehindWindow(QWidget *widget, bool enable = true); - static bool extendFrameIntoClientArea(QWidget *widget, - int left = -1, int top = -1, - int right = -1, int bottom = -1); - static bool isCompositionEnabled(); - static QColor colorizatinColor(); - -private: - static WindowNotifier *windowNotifier(); -}; - -#endif // QTWIN_H -- cgit v1.2.3 From c1e667222ad7eac6403cd60aab4d5d09ed0ff02d Mon Sep 17 00:00:00 2001 From: flower Date: Wed, 31 Aug 2011 16:08:03 +0300 Subject: make German translation up-to-date --- src/qt/locale/bitcoin_de.ts | 170 ++++++++++++++++++++++---------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/src/qt/locale/bitcoin_de.ts b/src/qt/locale/bitcoin_de.ts index 87185155ac..1978f06d41 100644 --- a/src/qt/locale/bitcoin_de.ts +++ b/src/qt/locale/bitcoin_de.ts @@ -42,7 +42,7 @@ Dieses Produkt enthält Software, welche vom OpenSSL Projekt zur Verwendung im O These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. - Dies sind ihre Bitcoin-Adressen zum empfangen von Zahlungen. Sie können jedem Sender eine andere Adresse mitteilen, sodass sie ihre empfangenen Zahlungen zurückverfolgen können. + Dies sind ihre Bitcoin-Adressen zum empfangen von Zahlungen. Um Ihre Zahlungen zurückverfolgen zu können ist es möglich jedem Sender eine andere Empfangsaddresse mitzuteilen. @@ -67,12 +67,12 @@ Dieses Produkt enthält Software, welche vom OpenSSL Projekt zur Verwendung im O &Copy to Clipboard - &Kopieren + &In die Zwischenablage kopieren Delete the currently selected address from the list. Only sending addresses can be deleted. - Ausgewählte Adresse aus der Liste entfernen. Sie können nur die Adressen von Empfängern löschen. + Die ausgewählte Adresse aus der Liste entfernen. Sie können nur ausgehende Adressen entfernen. @@ -85,17 +85,17 @@ Dieses Produkt enthält Software, welche vom OpenSSL Projekt zur Verwendung im O Label - + Bezeichnung Address - + Adresse (no label) - + (keine Bezeichnung) @@ -103,7 +103,7 @@ Dieses Produkt enthält Software, welche vom OpenSSL Projekt zur Verwendung im O Bitcoin Wallet - + Bitcoin Wallet @@ -113,7 +113,7 @@ Dieses Produkt enthält Software, welche vom OpenSSL Projekt zur Verwendung im O Number of blocks in the block chain - Anzahl der Blocks in der Block-Chain + Anzahl der Blöcke in der Block-Chain @@ -143,7 +143,7 @@ Dieses Produkt enthält Software, welche vom OpenSSL Projekt zur Verwendung im O Edit the list of stored addresses and labels - Adressliste bearbeiten + Adressen bearbeiten @@ -153,7 +153,7 @@ Dieses Produkt enthält Software, welche vom OpenSSL Projekt zur Verwendung im O Show the list of addresses for receiving payments - Liste der Adressen für Überweisungen anzeigen + Empfangsadressen anzeigen @@ -203,7 +203,7 @@ Dieses Produkt enthält Software, welche vom OpenSSL Projekt zur Verwendung im O Show the Bitcoin window - Bitcoin-Fenster öffnen + Bitcoin-Fenster anzeigen @@ -240,7 +240,7 @@ Dieses Produkt enthält Software, welche vom OpenSSL Projekt zur Verwendung im O This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? Die Größe dieser Transaktion übersteigt das Limit. - Sie können die Coins jedoch senden, wenn sie einen zusätzlichen Betrag von %1 zahlen, + Sie können die Coins jedoch senden, wenn sie einen zusätzlichen Betrag von 1% zahlen, welcher an die Teilnehmer des Bitcoin-Netzwerkes ausgeschüttet wird und dieses unterstützt. Möchten Sie die zusätzliche Gebühr zahlen? @@ -280,7 +280,7 @@ Möchten Sie die zusätzliche Gebühr zahlen? Edit Address - Adresse Bearbeiten + Adresse bearbeiten @@ -290,7 +290,7 @@ Möchten Sie die zusätzliche Gebühr zahlen? The label associated with this address book entry - Name/Beschreibung für den Adressbucheintrag + Name/Beschreibung des Adressbucheintrags @@ -300,7 +300,7 @@ Möchten Sie die zusätzliche Gebühr zahlen? The address associated with this address book entry. This can only be modified for sending addresses. - Adresse für den Adressbucheintrag + Die Adresse des Adressbucheintrag. Diese kann nur für Zahlungsadressen bearbeitet werden. @@ -353,7 +353,7 @@ Möchten Sie die zusätzliche Gebühr zahlen? Show only a tray icon after minimizing the window - Nur ein Icon im Infobereich anzeigen nach dem minimieren des Fensters + Minimiert nur im Infobereich anzeigen. @@ -482,7 +482,7 @@ Möchten Sie die zusätzliche Gebühr zahlen? The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - Adresse für die Überweisung (z.B. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + Empfangsadresse für die Überweisung (z.B. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) @@ -548,7 +548,7 @@ Möchten Sie die zusätzliche Gebühr zahlen? Must fill in an amount to pay. - Sie müssen einen Betrag angeben. + Bitte geben Sie einen Betrag ein. @@ -558,17 +558,17 @@ Möchten Sie die zusätzliche Gebühr zahlen? Are you sure you want to send %1 BTC to %2 (%3)? - Sind Sie sich sicher, %1 BTC an %2 (%3) zahlen zu wollen? + Möchten Sie %1 BTC an %2 (%3) überweisen? The recepient address is not valid, please recheck. - Die Adresse des Empfängers ist nicht gültig, bitte überprüfen Sie diese. + Die Empfangsadresse ist ungültig. The amount to pay must be larger than 0. - Der Transaktionsbetrag muss mehr als 0 betragen. + Der Betrag muss mehr als 0 betragen. @@ -637,7 +637,7 @@ Möchten Sie die zusätzliche Gebühr zahlen? Offline (%1 confirmations) - Offline (%1 Bestätigungen) + Nicht verbunden (%1 Bestätigungen) @@ -653,7 +653,7 @@ Möchten Sie die zusätzliche Gebühr zahlen? Mined balance will be available in %n more blocks - Der geminte Betrag wird nach %n Blöcken verfügbar sein + Der Betrag wird in %n Blöcken verfügbar sein @@ -695,7 +695,7 @@ Möchten Sie die zusätzliche Gebühr zahlen? Mined - Gemined + Erarbeitet @@ -720,7 +720,7 @@ Möchten Sie die zusätzliche Gebühr zahlen? Amount removed from or added to balance. - Betrag vom Kontostand entfernt oder hinzugefügt + Betrag vom Kontostand entfernt oder hinzugefügt. @@ -779,7 +779,7 @@ Möchten Sie die zusätzliche Gebühr zahlen? Mined - Gemined + Erarbeitet @@ -804,7 +804,7 @@ Möchten Sie die zusätzliche Gebühr zahlen? Could not write to file %1. - Konnte Datei %1 nicht öffnen + Konnte Datei %1 nicht zum beschreiben öffnen @@ -820,311 +820,311 @@ Möchten Sie die zusätzliche Gebühr zahlen? Bitcoin version - + Bitcoin Version Usage: - + Verwendung: Send command to -server or bitcoind - + RPC-Befehl an bitcoind schicken List commands - + RPC-Befehle auflisten Get help for a command - + Hilfe Options: - + Einstellungen Specify configuration file (default: bitcoin.conf) - + Bitte wählen Sie eine Konfigurationsdatei (Standard: bitcoin.conf) Specify pid file (default: bitcoind.pid) - + Bitte wählen Sie den Namen der PID Datei (Standard bitcoind.pid) Generate coins - + Erarbeite Bitcoins Don't generate coins - + Keine BitCoins erstellen Start minimized - + minimiert starten Specify data directory - + Bitte wählen Sie das Datenverzeichnis Specify connection timeout (in milliseconds) - + Netzwerkverbindungsabbruch nach (in Millisekunden) Connect through socks4 proxy - + Durch SOCKS4-Proxy verbinden Allow DNS lookups for addnode and connect - + Erlaube DNS Namensauflösung für addnode und connect Add a node to connect to - + Bitcoin Server registrieren Connect only to the specified node - + Nur zu den angegebenen Servern verbinden Don't accept connections from outside - + Keine externen Transatkionen akzeptieren Don't attempt to use UPnP to map the listening port - + UPnP nicht verwenden Attempt to use UPnP to map the listening port - + Versuche eine Verbindung mittels UPnP herzustellen Fee per KB to add to transactions you send - + Gebühr pro KB (je von Ihnen verschickte Transatkion) Accept command line and JSON-RPC commands - + Erlaube Kommandozeilen und RPC Befehle Run in the background as a daemon and accept commands - + Als Hintergrunddienst starten Use the test network - + Im Testnetz starten Username for JSON-RPC connections - + Benutzername für RPC Befehle Password for JSON-RPC connections - + Passwort für RPC Befehle Listen for JSON-RPC connections on <port> (default: 8332) - + Port für RPC Befehle Allow JSON-RPC connections from specified IP address - + RPC Befehle nur von dieser IP-Adresse erlauben Send commands to node running on <ip> (default: 127.0.0.1) - + Befehl an einen anderen BitCoin Server senden Set key pool size to <n> (default: 100) - + Menge der vorgenerierten Adressen Rescan the block chain for missing wallet transactions - + Block-Chain nach verlorenen Transaktionen durchsuchen (rescan) SSL options: (see the Bitcoin Wiki for SSL setup instructions) - + SSL Einstellungen (Bitte sehen Sie für eine detallierte Beschreibung im BitCoin-Wiki nach Use OpenSSL (https) for JSON-RPC connections - + RPC Befehle über HTTPS Server certificate file (default: server.cert) - + SSL Server Zertifikat Server private key (default: server.pem) - + Privater SSL Schlüssel (Standard: server.pem) Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) - + Erlaubte Kryptographiealgorithmen (Standard: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) This help message - + Dieser Hilfetext Cannot obtain a lock on data directory %s. Bitcoin is probably already running. - + Konnte das Datenverzeichnis %s nicht sperren. Evtl. wurde das Programm mehrfach gestartet. Error loading addr.dat - + Fehler beim laden der addr.dat Error loading blkindex.dat - + Fehler beim laden der blkindex.dat Error loading wallet.dat - + Fehler beim laden von wallet.dat Invalid -proxy address - + Fehlerhafte Proxy Adresse Invalid amount for -paytxfee=<amount> - + Ungültige Angabe für -paytxfee=<Betrag> Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction. - + Warnung: -paytxfee ist auf einen sehr hohen Wert gesetzt. Dies ist die Gebühr die beim senden einer Transaktion fällig wird. Warning: Disk space is low - + Warnung: Festplattenplatz wird knapp. Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds - + Fehler: Diese Transaktion erfordert eine Gebühr von mindestens %s. Die Gebühr wird fällig aufgrund der Größe, der Komplexität oder der Verwendung erst kürzlich erhaltener BitCoins Error: Transaction creation failed - + Fehler: Die Transaktion konnte nicht erstellt werden. Sending... - + Senden... Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here. - + Fehler: Die Transaktion wurde zurückgewiesen. Dies kann passieren wenn einige Ihrer BitCoins bereits ausgegeben wurde (zB aus einer Sicherungskopie Ihrer wallet.dat) Invalid amount - + Fehlerhafter Betrag Insufficient funds - + Unzureichender Kontostand Invalid bitcoin address - + Fehlerhafte Bitcoin Adresse Unable to bind to port %d on this computer. Bitcoin is probably already running. - + Fehler beim registrieren des Ports %d auf diesem Computer. Evtl. läuft BitCoin bereits To use the %s option - + Um die Option %s zuu verwenden @@ -1132,24 +1132,24 @@ SSL options: (see the Bitcoin Wiki for SSL setup instructions) in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions. - + Warnung: Sie müssen rpcpassword=password in der Konfigurationsdatei angeben. You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions. - + Sie müssen rpcpassword=password in der Konfigurationsdatei angeben. Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly. - + Bitte prüfen Sie Ihre Datums- und Uhrzeiteinstellungen, ansonsten kann es sein das BitCoin nicht ordnungsgemäss funktioniert. -beta - + -beta -- cgit v1.2.3 From 3c66913cd2d7a37f57c656c282faf7d160e88ea8 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 18 Sep 2011 12:04:03 +0200 Subject: move qt-specific scripts to qt-specific directory in scripts/ --- scripts/extract_strings_qt.py | 63 --------------------------------------- scripts/img/reload.xcf | Bin 25292 -> 0 bytes scripts/make_spinner.py | 43 -------------------------- scripts/make_windows_icon.py | 9 ------ scripts/qt/extract_strings_qt.py | 63 +++++++++++++++++++++++++++++++++++++++ scripts/qt/img/reload.xcf | Bin 0 -> 25292 bytes scripts/qt/make_spinner.py | 43 ++++++++++++++++++++++++++ scripts/qt/make_windows_icon.py | 9 ++++++ 8 files changed, 115 insertions(+), 115 deletions(-) delete mode 100755 scripts/extract_strings_qt.py delete mode 100644 scripts/img/reload.xcf delete mode 100755 scripts/make_spinner.py delete mode 100755 scripts/make_windows_icon.py create mode 100755 scripts/qt/extract_strings_qt.py create mode 100644 scripts/qt/img/reload.xcf create mode 100755 scripts/qt/make_spinner.py create mode 100755 scripts/qt/make_windows_icon.py diff --git a/scripts/extract_strings_qt.py b/scripts/extract_strings_qt.py deleted file mode 100755 index 6627de4abf..0000000000 --- a/scripts/extract_strings_qt.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/python -''' -Extract _("...") strings for translation and convert to Qt4 stringdefs so that -they can be picked up by Qt linguist. -''' -from subprocess import Popen, PIPE - -OUT_CPP="src/qt/bitcoinstrings.cpp" -EMPTY=['""'] - -def parse_po(text): - """ - Parse 'po' format produced by xgettext. - Return a list of (msgid,msgstr) tuples. - """ - messages = [] - msgid = [] - msgstr = [] - in_msgid = False - in_msgstr = False - - for line in text.split('\n'): - line = line.rstrip('\r') - if line.startswith('msgid '): - if in_msgstr: - messages.append((msgid, msgstr)) - in_msgstr = False - # message start - in_msgid = True - - msgid = [line[6:]] - elif line.startswith('msgstr '): - in_msgid = False - in_msgstr = True - msgstr = [line[7:]] - elif line.startswith('"'): - if in_msgid: - msgid.append(line) - if in_msgstr: - msgstr.append(line) - - if in_msgstr: - messages.append((msgid, msgstr)) - - return messages - -files = ['src/base58.h', 'src/bignum.h', 'src/db.cpp', 'src/db.h', 'src/headers.h', 'src/init.cpp', 'src/init.h', 'src/irc.cpp', 'src/irc.h', 'src/key.h', 'src/main.cpp', 'src/main.h', 'src/net.cpp', 'src/net.h', 'src/noui.h', 'src/script.cpp', 'src/script.h', 'src/serialize.h', 'src/strlcpy.h', 'src/uint256.h', 'src/util.cpp', 'src/util.h'] - -# xgettext -n --keyword=_ $FILES -child = Popen(['xgettext','--output=-','-n','--keyword=_'] + files, stdout=PIPE) -(out, err) = child.communicate() - -messages = parse_po(out) - -f = open(OUT_CPP, 'w') -f.write('#include \n') -f.write('// Automatically generated by extract_strings.py\n') -f.write('static const char *bitcoin_strings[] = {') -for (msgid, msgstr) in messages: - if msgid != EMPTY: - f.write('QT_TRANSLATE_NOOP("bitcoin-core", %s),\n' % ('\n'.join(msgid))) -f.write('};') -f.close() diff --git a/scripts/img/reload.xcf b/scripts/img/reload.xcf deleted file mode 100644 index dc8be62831..0000000000 Binary files a/scripts/img/reload.xcf and /dev/null differ diff --git a/scripts/make_spinner.py b/scripts/make_spinner.py deleted file mode 100755 index 1d4ee0202d..0000000000 --- a/scripts/make_spinner.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -# W.J. van der Laan, 2011 -# Make spinning .mng animation from a .png -# Requires imagemagick 6.7+ -from __future__ import division -from os import path -from PIL import Image -from subprocess import Popen - -SRC='img/reload_scaled.png' -DST='../src/qt/res/movies/update_spinner.mng' -TMPDIR='/tmp' -TMPNAME='tmp-%03i.png' -NUMFRAMES=35 -FRAMERATE=10.0 -CONVERT='convert' -CLOCKWISE=True -DSIZE=(16,16) - -im_src = Image.open(SRC) - -if CLOCKWISE: - im_src = im_src.transpose(Image.FLIP_LEFT_RIGHT) - -def frame_to_filename(frame): - return path.join(TMPDIR, TMPNAME % frame) - -frame_files = [] -for frame in xrange(NUMFRAMES): - rotation = (frame + 0.5) / NUMFRAMES * 360.0 - if CLOCKWISE: - rotation = -rotation - im_new = im_src.rotate(rotation, Image.BICUBIC) - im_new.thumbnail(DSIZE, Image.ANTIALIAS) - outfile = frame_to_filename(frame) - im_new.save(outfile, 'png') - frame_files.append(outfile) - -p = Popen([CONVERT, "-delay", str(FRAMERATE), "-dispose", "2"] + frame_files + [DST]) -p.communicate() - - - diff --git a/scripts/make_windows_icon.py b/scripts/make_windows_icon.py deleted file mode 100755 index d722ebe9b6..0000000000 --- a/scripts/make_windows_icon.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -# create multiresolution windows icon -ICON_SRC=../src/qt/res/icons/bitcoin.png -ICON_DST=../src/qt/res/icons/bitcoin.ico -convert ${ICON_SRC} -resize 16x16 bitcoin-16.png -convert ${ICON_SRC} -resize 32x32 bitcoin-32.png -convert ${ICON_SRC} -resize 48x48 bitcoin-48.png -convert bitcoin-16.png bitcoin-32.png bitcoin-48.png ${ICON_DST} - diff --git a/scripts/qt/extract_strings_qt.py b/scripts/qt/extract_strings_qt.py new file mode 100755 index 0000000000..6627de4abf --- /dev/null +++ b/scripts/qt/extract_strings_qt.py @@ -0,0 +1,63 @@ +#!/usr/bin/python +''' +Extract _("...") strings for translation and convert to Qt4 stringdefs so that +they can be picked up by Qt linguist. +''' +from subprocess import Popen, PIPE + +OUT_CPP="src/qt/bitcoinstrings.cpp" +EMPTY=['""'] + +def parse_po(text): + """ + Parse 'po' format produced by xgettext. + Return a list of (msgid,msgstr) tuples. + """ + messages = [] + msgid = [] + msgstr = [] + in_msgid = False + in_msgstr = False + + for line in text.split('\n'): + line = line.rstrip('\r') + if line.startswith('msgid '): + if in_msgstr: + messages.append((msgid, msgstr)) + in_msgstr = False + # message start + in_msgid = True + + msgid = [line[6:]] + elif line.startswith('msgstr '): + in_msgid = False + in_msgstr = True + msgstr = [line[7:]] + elif line.startswith('"'): + if in_msgid: + msgid.append(line) + if in_msgstr: + msgstr.append(line) + + if in_msgstr: + messages.append((msgid, msgstr)) + + return messages + +files = ['src/base58.h', 'src/bignum.h', 'src/db.cpp', 'src/db.h', 'src/headers.h', 'src/init.cpp', 'src/init.h', 'src/irc.cpp', 'src/irc.h', 'src/key.h', 'src/main.cpp', 'src/main.h', 'src/net.cpp', 'src/net.h', 'src/noui.h', 'src/script.cpp', 'src/script.h', 'src/serialize.h', 'src/strlcpy.h', 'src/uint256.h', 'src/util.cpp', 'src/util.h'] + +# xgettext -n --keyword=_ $FILES +child = Popen(['xgettext','--output=-','-n','--keyword=_'] + files, stdout=PIPE) +(out, err) = child.communicate() + +messages = parse_po(out) + +f = open(OUT_CPP, 'w') +f.write('#include \n') +f.write('// Automatically generated by extract_strings.py\n') +f.write('static const char *bitcoin_strings[] = {') +for (msgid, msgstr) in messages: + if msgid != EMPTY: + f.write('QT_TRANSLATE_NOOP("bitcoin-core", %s),\n' % ('\n'.join(msgid))) +f.write('};') +f.close() diff --git a/scripts/qt/img/reload.xcf b/scripts/qt/img/reload.xcf new file mode 100644 index 0000000000..dc8be62831 Binary files /dev/null and b/scripts/qt/img/reload.xcf differ diff --git a/scripts/qt/make_spinner.py b/scripts/qt/make_spinner.py new file mode 100755 index 0000000000..136aff3cb7 --- /dev/null +++ b/scripts/qt/make_spinner.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# W.J. van der Laan, 2011 +# Make spinning .mng animation from a .png +# Requires imagemagick 6.7+ +from __future__ import division +from os import path +from PIL import Image +from subprocess import Popen + +SRC='img/reload_scaled.png' +DST='../../src/qt/res/movies/update_spinner.mng' +TMPDIR='/tmp' +TMPNAME='tmp-%03i.png' +NUMFRAMES=35 +FRAMERATE=10.0 +CONVERT='convert' +CLOCKWISE=True +DSIZE=(16,16) + +im_src = Image.open(SRC) + +if CLOCKWISE: + im_src = im_src.transpose(Image.FLIP_LEFT_RIGHT) + +def frame_to_filename(frame): + return path.join(TMPDIR, TMPNAME % frame) + +frame_files = [] +for frame in xrange(NUMFRAMES): + rotation = (frame + 0.5) / NUMFRAMES * 360.0 + if CLOCKWISE: + rotation = -rotation + im_new = im_src.rotate(rotation, Image.BICUBIC) + im_new.thumbnail(DSIZE, Image.ANTIALIAS) + outfile = frame_to_filename(frame) + im_new.save(outfile, 'png') + frame_files.append(outfile) + +p = Popen([CONVERT, "-delay", str(FRAMERATE), "-dispose", "2"] + frame_files + [DST]) +p.communicate() + + + diff --git a/scripts/qt/make_windows_icon.py b/scripts/qt/make_windows_icon.py new file mode 100755 index 0000000000..bf607b1c62 --- /dev/null +++ b/scripts/qt/make_windows_icon.py @@ -0,0 +1,9 @@ +#!/bin/bash +# create multiresolution windows icon +ICON_SRC=../../src/qt/res/icons/bitcoin.png +ICON_DST=../../src/qt/res/icons/bitcoin.ico +convert ${ICON_SRC} -resize 16x16 bitcoin-16.png +convert ${ICON_SRC} -resize 32x32 bitcoin-32.png +convert ${ICON_SRC} -resize 48x48 bitcoin-48.png +convert bitcoin-16.png bitcoin-32.png bitcoin-48.png ${ICON_DST} + -- cgit v1.2.3 From e122e42354149386159fbff82fbd66e7a8ba1fdb Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 18 Sep 2011 12:41:48 +0200 Subject: assure that base bitcoind and bitcoin still build --- src/makefile.linux-mingw | 4 ++-- src/makefile.mingw | 4 ++-- src/makefile.osx | 4 ++-- src/makefile.unix | 4 ++-- src/makefile.vc | 2 +- src/noui.h | 4 ++++ src/ui.h | 4 +++- 7 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index 24cc127c2d..cae82f8cff 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -49,7 +49,7 @@ HEADERS = \ net.h \ noui.h \ protocol.h \ - rpc.h \ + bitcoinrpc.h \ script.h \ serialize.h \ strlcpy.h \ @@ -76,7 +76,7 @@ OBJS= \ obj/main.o \ obj/net.o \ obj/protocol.o \ - obj/rpc.o \ + obj/bitcoinrpc.o \ obj/script.o \ obj/util.o \ obj/wallet.o \ diff --git a/src/makefile.mingw b/src/makefile.mingw index 1ca1a7bbe2..5aa5ebab40 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -46,7 +46,7 @@ HEADERS = \ net.h \ noui.h \ protocol.h \ - rpc.h \ + bitcoinrpc.h \ script.h \ serialize.h \ strlcpy.h \ @@ -74,7 +74,7 @@ OBJS= \ obj/main.o \ obj/net.o \ obj/protocol.o \ - obj/rpc.o \ + obj/bitcoinrpc.o \ obj/script.o \ obj/util.o \ obj/wallet.o \ diff --git a/src/makefile.osx b/src/makefile.osx index 97264c7eb4..9dce3f3af2 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -46,7 +46,7 @@ HEADERS = \ net.h \ noui.h \ protocol.h \ - rpc.h \ + bitcoinrpc.h \ script.h \ serialize.h \ strlcpy.h \ @@ -65,7 +65,7 @@ OBJS= \ obj/main.o \ obj/net.o \ obj/protocol.o \ - obj/rpc.o \ + obj/bitcoinrpc.o \ obj/script.o \ obj/util.o \ obj/wallet.o \ diff --git a/src/makefile.unix b/src/makefile.unix index 298d856ecb..2b7f3f679d 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -52,7 +52,7 @@ HEADERS = \ net.h \ noui.h \ protocol.h \ - rpc.h \ + bitcoinrpc.h \ script.h \ serialize.h \ strlcpy.h \ @@ -71,7 +71,7 @@ OBJS= \ obj/main.o \ obj/net.o \ obj/protocol.o \ - obj/rpc.o \ + obj/bitcoinrpc.o \ obj/script.o \ obj/util.o \ obj/wallet.o \ diff --git a/src/makefile.vc b/src/makefile.vc index a5437bcf5c..5d283793cb 100644 --- a/src/makefile.vc +++ b/src/makefile.vc @@ -59,7 +59,7 @@ HEADERS = \ net.h \ noui.h \ protocol.h \ - rpc.h \ + bitcoinrpc.h \ script.h \ serialize.h \ strlcpy.h \ diff --git a/src/noui.h b/src/noui.h index cbe6fa4c7b..754c2225fc 100644 --- a/src/noui.h +++ b/src/noui.h @@ -67,4 +67,8 @@ inline void MainFrameRepaint() { } +inline void InitMessage(const std::string &message) +{ +} + #endif diff --git a/src/ui.h b/src/ui.h index 2a128a7bac..1784af77b3 100644 --- a/src/ui.h +++ b/src/ui.h @@ -24,7 +24,9 @@ void CalledSetStatusBar(const std::string& strText, int nField); void MainFrameRepaint(); void CreateMainWindow(); void SetStartOnSystemStartup(bool fAutoStart); - +inline void InitMessage(const std::string &message) +{ +} -- cgit v1.2.3 From 0465c41c847ddee7eeb5caefb164149400ff8395 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 18 Sep 2011 11:55:41 +0200 Subject: move current qt specific readme to doc/, restore original README.md --- README-original.md | 30 ---------- README.md | 29 ++++++++++ README.rst | 162 ----------------------------------------------------- doc/readme-qt.rst | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 191 insertions(+), 192 deletions(-) delete mode 100644 README-original.md create mode 100644 README.md delete mode 100644 README.rst create mode 100644 doc/readme-qt.rst diff --git a/README-original.md b/README-original.md deleted file mode 100644 index a41eb2d770..0000000000 --- a/README-original.md +++ /dev/null @@ -1,30 +0,0 @@ - -Bitcoin integration/staging tree - -Development process -=================== - -Developers work in their own trees, then submit pull requests when -they think their feature or bug fix is ready. - -If it is a simple/trivial/non-controversial change, then one of the -bitcoin development team members simply pulls it. - -If it is a more complicated or potentially controversial -change, then the patch submitter will be asked to start a -discussion (if they haven't already) on the mailing list: -http://sourceforge.net/mailarchive/forum.php?forum_name=bitcoin-development - -The patch will be accepted if there is broad consensus that it is a -good thing. Developers should expect to rework and resubmit patches -if they don't match the project's coding conventions (see coding.txt) -or are controversial. - -The master branch is regularly built and tested, but is not guaranteed -to be completely stable. Tags are regularly created to indicate new -official, stable release versions of Bitcoin. If you would like to -help test the Bitcoin core, please contact QA@Bitcoin.org. - -Feature branches are created when there are major new features being -worked on by several people. - diff --git a/README.md b/README.md new file mode 100644 index 0000000000..67fa93d371 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ + +Bitcoin integration/staging tree + +Development process +=================== + +Developers work in their own trees, then submit pull requests when +they think their feature or bug fix is ready. + +If it is a simple/trivial/non-controversial change, then one of the +bitcoin development team members simply pulls it. + +If it is a more complicated or potentially controversial +change, then the patch submitter will be asked to start a +discussion (if they haven't already) on the mailing list: +http://sourceforge.net/mailarchive/forum.php?forum_name=bitcoin-development + +The patch will be accepted if there is broad consensus that it is a +good thing. Developers should expect to rework and resubmit patches +if they don't match the project's coding conventions (see coding.txt) +or are controversial. + +The master branch is regularly built and tested, but is not guaranteed +to be completely stable. Tags are regularly created to indicate new +official, stable release versions of Bitcoin. If you would like to +help test the Bitcoin core, please contact QA@Bitcoin.org. + +Feature branches are created when there are major new features being +worked on by several people. diff --git a/README.rst b/README.rst deleted file mode 100644 index c13134a39d..0000000000 --- a/README.rst +++ /dev/null @@ -1,162 +0,0 @@ -Bitcoin-qt: Qt4 based GUI replacement for Bitcoin -================================================= - -Features -======== - -- All functionality of the Wx GUI, including wallet encryption - -- Compatibility with Linux (both GNOME and KDE), MacOSX and Windows - -- Notification on incoming / outgoing transactions (compatible with FreeDesktop and other desktop notification schemes) - -- General interface improvements: Splash screen, tabbed interface - -- Overview page with current balance, unconfirmed balance, and such - -- Better transaction list with status icons, real-time filtering and a context menu - -- Asks for confirmation before sending coins, for your own safety - -- CSV export of transactions and address book (for Excel bookkeeping) - -- Shows alternative icon when connected to testnet, so you never accidentally send real coins during testing - -- Shows a progress bar on initial block download, so that you don't have to wonder how many blocks it needs to download to be up to date - -- Sendmany support, send to multiple recipients at the same time - -- Multiple unit support, can show subdivided bitcoins (uBTC, mBTC) for users that like large numbers - -- Support for English, German, Russian and Dutch languages - -- Address books and transaction table can be sorted by any column - -- Accepts "bitcoin:" URLs from browsers and other sources through drag and drop - -Build instructions -=================== - -Debian -------- - -First, make sure that the required packages for Qt4 development of your -distribution are installed, for Debian and Ubuntu these are: - -:: - - apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \ - libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ - libssl-dev libdb4.8++-dev - -then execute the following: - -:: - - qmake - make - -Alternatively, install Qt Creator and open the `bitcoin-qt.pro` file. - -An executable named `bitcoin-qt` will be built. - - -Windows --------- - -Windows build instructions: - -- Download the `QT Windows SDK`_ and install it. You don't need the Symbian stuff, just the desktop Qt. - -- Download and extract the `dependencies archive`_ [#]_, or compile openssl, boost and dbcxx yourself. - -- Copy the contents of the folder "deps" to "X:\\QtSDK\\mingw", replace X:\\ with the location where you installed the Qt SDK. Make sure that the contents of "deps\\include" end up in the current "include" directory. - -- Open the .pro file in QT creator and build as normal (ctrl-B) - -.. _`QT Windows SDK`: http://qt.nokia.com/downloads/sdk-windows-cpp -.. _`dependencies archive`: https://download.visucore.com/bitcoin/qtgui_deps_1.zip -.. [#] PGP signature: https://download.visucore.com/bitcoin/qtgui_deps_1.zip.sig (signed with RSA key ID `610945D0`_) -.. _`610945D0`: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x610945D0 - - -Mac OS X --------- - -- Download and install the `Qt Mac OS X SDK`_. It is recommended to also install Apple's Xcode with UNIX tools. - -- Download and install `MacPorts`_. - -- Execute the following commands in a terminal to get the dependencies: - -:: - - sudo port selfupdate - sudo port install boost db48 - -- Open the .pro file in Qt Creator and build as normal (cmd-B) - -.. _`Qt Mac OS X SDK`: http://qt.nokia.com/downloads/sdk-mac-os-cpp -.. _`MacPorts`: http://www.macports.org/install.php - - -Build configuration options -============================ - -UPNnP port forwarding ---------------------- - -To use UPnP for port forwarding behind a NAT router (recommended, as more connections overall allow for a faster and more stable bitcoin experience), pass the following argument to qmake: - -:: - - qmake "USE_UPNP=1" - -(in **Qt Creator**, you can find the setting for additional qmake arguments under "Projects" -> "Build Settings" -> "Build Steps", then click "Details" next to **qmake**) - -This requires miniupnpc for UPnP port mapping. It can be downloaded from -http://miniupnp.tuxfamily.org/files/. UPnP support is not compiled in by default. - -Set USE_UPNP to a different value to control this: - -+------------+--------------------------------------------------------------+ -| USE_UPNP= | (the default) no UPnP support, miniupnpc not required; | -+------------+--------------------------------------------------------------+ -| USE_UPNP=0 | UPnP support turned off by default at runtime; | -+------------+--------------------------------------------------------------+ -| USE_UPNP=1 | UPnP support turned on by default at runtime. | -+------------+--------------------------------------------------------------+ - -Mac OS X users: miniupnpc is currently outdated on MacPorts. An updated Portfile is provided in contrib/miniupnpc within this project. -You can execute the following commands in a terminal to install it: - -:: - - cd /contrib/miniupnpc - sudo port install - -Notification support for recent (k)ubuntu versions ---------------------------------------------------- - -To see desktop notifications on (k)ubuntu versions starting from 10.04, enable usage of the -FreeDesktop notification interface through DBUS using the following qmake option: - -:: - - qmake "USE_DBUS=1" - -Berkely DB version warning -========================== - -A warning for people using the *static binary* version of Bitcoin on a Linux/UNIX-ish system (tl;dr: **Berkely DB databases are not forward compatible**). - -The static binary version of Bitcoin is linked against libdb4.7 or libdb4.8 (see also `this Debian issue`_). - -Now the nasty thing is that databases from 5.X are not compatible with 4.X. - -If the globally installed development package of Berkely DB installed on your system is 5.X, any source you -build yourself will be linked against that. The first time you run with a 5.X version the database will be upgraded, -and 4.X cannot open the new format. This means that you cannot go back to the old statically linked version without -significant hassle! - -.. _`this Debian issue`: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=621425 diff --git a/doc/readme-qt.rst b/doc/readme-qt.rst new file mode 100644 index 0000000000..b12aa30904 --- /dev/null +++ b/doc/readme-qt.rst @@ -0,0 +1,162 @@ +Bitcoin-qt: Qt4 based GUI replacement for Bitcoin +================================================= + +Features +======== + +- All functionality of the Wx GUI, including wallet encryption + +- Compatibility with Linux (both GNOME and KDE), MacOSX and Windows + +- Notification on incoming / outgoing transactions (compatible with FreeDesktop and other desktop notification schemes) + +- General interface improvements: Splash screen, tabbed interface + +- Overview page with current balance, unconfirmed balance, and such + +- Better transaction list with status icons, real-time filtering and a context menu + +- Asks for confirmation before sending coins, for your own safety + +- CSV export of transactions and address book (for Excel bookkeeping) + +- Shows alternative icon when connected to testnet, so you never accidentally send real coins during testing + +- Shows a progress bar on initial block download, so that you don't have to wonder how many blocks it needs to download to be up to date + +- Sendmany support, send to multiple recipients at the same time + +- Multiple unit support, can show subdivided bitcoins (uBTC, mBTC) for users that like large numbers + +- Support for English, German, Russian and Dutch languages + +- Address books and transaction table can be sorted by any column + +- Accepts "bitcoin:" URLs from browsers and other sources through drag and drop + +Build instructions +=================== + +Debian +------- + +First, make sure that the required packages for Qt4 development of your +distribution are installed, for Debian and Ubuntu these are: + +:: + + apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \ + libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ + libssl-dev libdb4.8++-dev + +then execute the following: + +:: + + qmake + make + +Alternatively, install Qt Creator and open the `bitcoin-qt.pro` file. + +An executable named `bitcoin-qt` will be built. + + +Windows +-------- + +Windows build instructions: + +- Download the `QT Windows SDK`_ and install it. You don't need the Symbian stuff, just the desktop Qt. + +- Download and extract the `dependencies archive`_ [#]_, or compile openssl, boost and dbcxx yourself. + +- Copy the contents of the folder "deps" to "X:\\QtSDK\\mingw", replace X:\\ with the location where you installed the Qt SDK. Make sure that the contents of "deps\\include" end up in the current "include" directory. + +- Open the .pro file in QT creator and build as normal (ctrl-B) + +.. _`QT Windows SDK`: http://qt.nokia.com/downloads/sdk-windows-cpp +.. _`dependencies archive`: https://download.visucore.com/bitcoin/qtgui_deps_1.zip +.. [#] PGP signature: https://download.visucore.com/bitcoin/qtgui_deps_1.zip.sig (signed with RSA key ID `610945D0`_) +.. _`610945D0`: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x610945D0 + + +Mac OS X +-------- + +- Download and install the `Qt Mac OS X SDK`_. It is recommended to also install Apple's Xcode with UNIX tools. + +- Download and install `MacPorts`_. + +- Execute the following commands in a terminal to get the dependencies: + +:: + + sudo port selfupdate + sudo port install boost db48 + +- Open the .pro file in Qt Creator and build as normal (cmd-B) + +.. _`Qt Mac OS X SDK`: http://qt.nokia.com/downloads/sdk-mac-os-cpp +.. _`MacPorts`: http://www.macports.org/install.php + + +Build configuration options +============================ + +UPNnP port forwarding +--------------------- + +To use UPnP for port forwarding behind a NAT router (recommended, as more connections overall allow for a faster and more stable bitcoin experience), pass the following argument to qmake: + +:: + + qmake "USE_UPNP=1" + +(in **Qt Creator**, you can find the setting for additional qmake arguments under "Projects" -> "Build Settings" -> "Build Steps", then click "Details" next to **qmake**) + +This requires miniupnpc for UPnP port mapping. It can be downloaded from +http://miniupnp.tuxfamily.org/files/. UPnP support is not compiled in by default. + +Set USE_UPNP to a different value to control this: + ++------------+--------------------------------------------------------------+ +| USE_UPNP= | (the default) no UPnP support, miniupnpc not required; | ++------------+--------------------------------------------------------------+ +| USE_UPNP=0 | UPnP support turned off by default at runtime; | ++------------+--------------------------------------------------------------+ +| USE_UPNP=1 | UPnP support turned on by default at runtime. | ++------------+--------------------------------------------------------------+ + +Mac OS X users: miniupnpc is currently outdated on MacPorts. An updated Portfile is provided in contrib/miniupnpc within this project. +You can execute the following commands in a terminal to install it: + +:: + + cd /contrib/miniupnpc + sudo port install + +Notification support for recent (k)ubuntu versions +--------------------------------------------------- + +To see desktop notifications on (k)ubuntu versions starting from 10.04, enable usage of the +FreeDesktop notification interface through DBUS using the following qmake option: + +:: + + qmake "USE_DBUS=1" + +Berkely DB version warning +========================== + +A warning for people using the *static binary* version of Bitcoin on a Linux/UNIX-ish system (tl;dr: **Berkely DB databases are not forward compatible**). + +The static binary version of Bitcoin is linked against libdb4.7 or libdb4.8 (see also `this Debian issue`_). + +Now the nasty thing is that databases from 5.X are not compatible with 4.X. + +If the globally installed development package of Berkely DB installed on your system is 5.X, any source you +build yourself will be linked against that. The first time you run with a 5.X version the database will be upgraded, +and 4.X cannot open the new format. This means that you cannot go back to the old statically linked version without +significant hassle! + +.. _`this Debian issue`: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=621425 -- cgit v1.2.3