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: [PATCH 001/312] 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 00000000..d6ff91a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*~ +*.o diff --git a/BitcoinGUI.cpp b/BitcoinGUI.cpp new file mode 100644 index 00000000..b18a18b8 --- /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 00000000..e4ff2fe6 --- /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 00000000..934b231b --- /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 0000000000000000000000000000000000000000..621ca40245be8863d582ae83d25bffb95b476fc6 GIT binary patch literal 656 zcmV;B0&o3^P)1RCwBylTT<9K@`T{Y&Y9vP11%& zstq<(Db$ckda58rp+vlhhqhNQA|CWm^lB;Cn}RtBr6=(sf*w2xrjbGqtx69|C4wlL zLsE+kyKHTm?9A@?X1Xi>iO@dy_RY?I{N}ygw`6K|_7VUbMNw1(WuvN`^RNCoP6!#m zoDyrXcdfDTl5>Cy_iLcKuG{x~Zf<^YZ4J(3GF#tmd#hZjd|oz;<&Tw0aj?HXKbFgl zWzy-B$#^{Zwp>o~rwauH-9kx#$bct8r=}%)&@`IK3=JJWaqf2EfpvXy>Q1*FiG~yf z!m28LuE%rWhvG6u0zeRAtq_RboS2vosU6Y3pSdoFRWIuUTz6w;I{)x@QxJ@?fA)lL zm1oOuVED2Gj5x-Nk(QD5Ja1nB;k#QX-oR%m2{_#}fHF?nRX{0iTY-dADqB|>^L~-{ z0|YyaTl`IX*7nqk9$5gI6HojI&*HYCYRSY7GYVwgWrc6uvYW qw?#Vy{vX{a-T%d{b_N0XDZl{2Ogn_%%oR8Q0000 + +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 0000000000000000000000000000000000000000..09520aca23dd88af90ea8516d0e2daa09562544f GIT binary patch literal 1086 zcmV-E1i|}>P)5QBzZlJ&>jrn;NG=2-?OJjLHDDAgDlx&c^Kfyyu}2tN;1?zssLr;D7o2 z&7OTWi};kcp>emO2u)SpNUg^b(`%u~sgkZ7d-2Fj;(A`#D8Ak2v?;|;w(shHxT&=R zK@>5Jf1yM?$`51T#V;*{-*Rs1`QVw?P8l15PhY=9YxLU2_TKwIuc9biS(qkQ5QweJ z;PQ20Q$@V3n^DXbCeM%a^~kv)U+2K!^M}6CMF5v3f4KL)1HIKsiSr{v{CZ&wTSG75 zl!)fo3)L*mY({Xm}xsy8vwm%2A7-nS z#f@FK-bih+ImacQPk4!>|yyJ)irPdB%93bExSRFeg#r-`<$yKuSP zSZg$L=_t3|^*G_YM!AwFwLFj6Vx{NSZKgzY`8mXZR;Y2= zHF1V|IftN2lnZ(6j#>na4$-JqtJn6@kre@lX2oK;Cb&E^MeyP%p_L^D?tYYL(x6x< zA;>biuA>`Fx! zXk|H&&t-bm+6LlFVPd+*jlDep?7Zh7W{U+`R++vyj?-Mi)6{_^$y^Es(G77z6wQqv z&;9aKS=Btqwpy~0c>=>95n2uLec&|fu@s{xjv&RRaM#tN`PvAtT_HIAn_+j=rWg_B<%Cpfz_tGSyV8l8;i zH_r6c(Su@QZTjVwR{yIvZ|kmcxjl%Yh=D*hlV*M{$k?|5BJtu`uYdF4OYi*{`Oh2w z$%*$5c%rK_Pnyl@gQB2s&1N&Ud_ljO$(1fBX6@J;M}I$aJ^wemeO({_YrJCs001R) zMObuXVRU6WV{&C-bY%cCFflPLFf}bOH&ih-IxsalGB_(RGCD9Y6X;fO0000bbVXQn zWMOn=I&E)cX=Zr." +#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 0000000000000000000000000000000000000000..fb510fbea6c96133e9effa439775cc0cbcbb008b GIT binary patch literal 876 zcmV-y1C#uTP)A=D;nOFk$cj$e%(t{zyx0xos(>#|OgVh~LXO58wGtzLWEr zM1;+FN^5z#)@OMk;Mo!c6XAJds()sscPiICvYBi8uRxi4yz;}HsZU>hvFeCt@AG#2 ze!v6|Bh2>=q`Iz;{P4vC>nF3hZ^t)gl#OPGrFmcXvtzeE*rlHIY%8{7Er2376qSNz z$L%h*zrR~Q@m=*_zwIy|-T#DuM?D$*+%s^kLXCTq3;)36B+Si1xfeWMcocz&Nm6q$ zyeyTsyZf(|sV9Pu1x`7}&iQ4bEV2+Mnn)lGO`zK#kpRPhK!~-PUCc#hnEpGCpG8k= z!N_L-ieEjxz0N$@yqL_8h{e#~dIwW;3oG#@$fO~YW>o~W?F?G$X_AXEV#X@_w27Df zN=p@udQ*Oc zf72mD)!U`V=aRclRo?dO=@bz(fR2LQXY2FvgEf}r3dO2pzWLL-;p*qs>+m4vpRZ%r zbySLc zz(L>4CQU+6n#Zmc_(t-Lh7yqaJ&1LS};$Yz*HW)toCs>`o!J$zu|&oBG_ zH;o%8IouwsaVmQQE_(oka5NphAIsm3{`m3aX70Z;KUy4Ma9t4q0000Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipY) z5-%DN#z%|*00S~fL_t(I%XO1qY*S?b#ees^-|p`EZ(CPOi<^`=rfde7k%dTNbSmNl zpgswS@okC55OgYHA}H1n&6I#|K41)ixCavgW=3EdL*|S#g3-in?l0A?9sRSlUDw^- zdp{o_WQllQ&&e<6;T%mV#cCaRRr9V1dY)PzTlZ*PeJG}(R3>v{VtVHKcgf81#giwL zwK}h@2E^YtpWXSyw)m!w&Njmc1M-$*vAmSxw`)VcUcB_n!NW(b%l`qK==JyP+xJLs zL$ndswQ*e=$1MRMv>Jr2qYKG=V#YpmcCfqWt)z_M&K?IzUc zeG;V{7W4fGVE}@vDxG3FYoGn{TGzXA>x%Z;eteI2t+^Um7iuY&U8EE!}X*!Oh6YvNU2o4Rwblcq3NGl#gc*-_MT=#%MSie_clMr!w-)0>8XR1QZDVS zA|^}AsL>G(twzz`e~QknIb@ZF6pFH4!B zUDH_1D!#hV$JdwosPTXh5UTU>O804mkfd*1Cw}Z1hK8?V8gO@1M>o`@bX|9#?SA?4 z?_(ChAebiT2AHPFp*OD3*tmsMYLw$AcQc+CqbUOUJhZh)N-OsFa&gJ|wmD{uEfy=S zHi|&tCcrRU^7%<7CUTtVdy)Bkf>;Ey3$QUHh(zU2e@-p`s41oR;JxZy;h;P}GF3v; z!Rv!+&#g8$+sKNiwN9=913v z$aMM8;UndNJH8R%^&a5~2F)F7!@kGr>-|lx>#A(FIF(En2XeWU%e^1FmDTxw0YS=U U5^I#n*#H0l07*qoM6N<$f;9HV`2YX_ literal 0 HcmV?d00001 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: [PATCH 002/312] 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 52b939e8e60f98721ced68181b0b0aab38770f85..51067a192bf9f49852d71d62e513bc495a82668c 100644 GIT binary patch delta 854 zcmV-c1F8J72dW2|{sqNrE!WSAR{Avn=wN0!Bc@M1NkK*Fq!48|7P#p|wnt4N5yh z;T`n2?&Tg=AGb``VKI^{U&dIe2j)A@D8ZGI#r}Iqkvm-`s^oIk65UNilRws%_bpF8~X;HLsxaO zg$!rDo_|ES22M~x_9JyP7IjTQvn1n_6O8{b0UkhwaLCW+hsM!Won&s27fOn0i8;v z^`qV>I_JWDu359#?6)0>764p;Td+`?BR^;H+JD5W+$bjTMWI!Iw)<7|PW{*V_1e$3 zk95NQg1fltstwU^Uo1MK`-<>|*xOC^rv6J4z{!QwK zR=P^dR0wVg;`93u!l9I3qr6hatEoh~1GaU;KDctUdit)IqH%LH*zbx5x;+m?H^yiW zMo(~rL(WQ(TTM~_$4OW-^>@<8>(l>PBS7eg>h-jndxKs6r-R|ZW+{a%TIGdesytn{ g8dHUnqPc$mFN0oldve>!cK`qY07*qoM6N<$f1R5;6ZlV5C8WdOy0_q*Tj?)qZZ_j5C7K z#BJ^`)vX=x%Z;eteI2t+^Um z7iuY&U8EE!}X*!Oh6YvNUS!9&|JELt_2C! zZ^JOwV^lYjDWq`iGU0G1b#)z>=0=8wzhPo}iE_cgV^$@kTcPQnS;dlq7xtcJL(2~S zPxm%I$HNbf^XaLBlu|D3ts*8%%c#*24XsAe-+zkEtvO_sh7^jjUBOpVOMCm%2q9Tm znCIMu^M8!}0a>rch8Rjpof%!zSj;NEy3ohhm-?vjfDjO>^YKdeX@roZZ(JvS>=}lJ zuVNZ-cT`6=)TDG>ccATl`SR~$7QrBxCg=v3rpciw{{~tu{b952_4>5}3f2`vTVZ-{r1%j;;*ep7JLj zdJe1&i{r_3iRyqxG+K+_@5ON(a=8MtvqhvMXu8X9jU}w^H{ULOai=rwX}!mYH#OEg z5Lb#cQd`@A>$+sKNiwN9=913v$aMM8;UndNJH8R%^&a5~2F)F7!@kGr>-|lx>#A(F oIF(En2XeWU%e^1FmDTxw0YS=U5^I#n*#H0l07*qoM6N<$f|*mdDgXcg 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: [PATCH 003/312] 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 b18a18b8..0b81f72f 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 00000000..0f352960 --- /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 00000000..9071ed66 --- /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 ab440fab..26a3216f 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 00000000..48022383 --- /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 + + 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: [PATCH 004/312] 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 d6ff91a9..a261fd9c 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 00000000..dd2ec9f9 --- /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 00000000..13721210 --- /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 00000000..7e853ede --- /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 00000000..3a27aa62 --- /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 0b81f72f..f07b8280 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 e4ff2fe6..590bb3ef 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 00000000..a89a58dc --- /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 00000000..f2720c37 --- /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 00000000..d2ce01ee --- /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 00000000..7bbfb1f8 --- /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 934b231b..4729921c 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 0f352960..e5cf2581 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 9071ed66..8913f1d9 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 26a3216f..4f794ac6 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 48022383..00000000 --- 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 3c024d31..00000000 --- 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 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: [PATCH 005/312] 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 00000000..b25ee3e9 --- /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 00000000..490452e1 --- /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 f07b8280..3ee74a1e 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 4f794ac6..b28b45f4 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 00000000..ebce276c --- /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 + + 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: [PATCH 006/312] 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 621ca40245be8863d582ae83d25bffb95b476fc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 656 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 M07*qoM6N<$f}QWU5dZ)H 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: [PATCH 007/312] 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 0000000000000000000000000000000000000000..621ca40245be8863d582ae83d25bffb95b476fc6 GIT binary patch literal 656 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 M07*qoM6N<$f}QWU5dZ)H literal 0 HcmV?d00001 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: [PATCH 008/312] update model --- BitcoinGUI.cpp | 19 ++++++++++++------- TransactionTableModel.cpp | 7 ++++++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/BitcoinGUI.cpp b/BitcoinGUI.cpp index 3ee74a1e..f0a66334 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 e5cf2581..aa3128c5 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(); } 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: [PATCH 009/312] ignore generated resource file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a261fd9c..819c55e3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.o moc_*.cpp *.pro.user +qrc_*.cpp 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: [PATCH 010/312] 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 f0a66334..0d7fbf4b 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 590bb3ef..63d28c29 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 aa3128c5..d36bea21 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(); } 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: [PATCH 011/312] 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 0d7fbf4b..0f122377 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 63d28c29..0c9edad8 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 00000000..891b43f4 --- /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 00000000..529eb214 --- /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 b28b45f4..99a2601e 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 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: [PATCH 012/312] rename to qt standard --- AddressBookDialog.cpp | 7 - OptionsDialog.cpp | 7 - SendCoinsDialog.cpp | 6 - SettingsDialog.cpp | 7 - SettingsDialog.h | 18 --- TODO | 3 + AboutDialog.cpp => aboutdialog.cpp | 0 AboutDialog.h => aboutdialog.h | 0 addressbookdialog.cpp | 19 +++ AddressBookDialog.h => addressbookdialog.h | 16 +- addressbookdialog.ui | 77 +++++++++ ...essTableModel.cpp => addresstablemodel.cpp | 0 AddressTableModel.h => addresstablemodel.h | 0 bitcoin.pro | 20 ++- BitcoinGUI.cpp => bitcoingui.cpp | 17 +- BitcoinGUI.h => bitcoingui.h | 0 mainoptionspage.cpp | 67 ++++++++ mainoptionspage.h | 18 +++ optionsdialog.cpp | 55 +++++++ OptionsDialog.h => optionsdialog.h | 7 + sendcoinsdialog.cpp | 14 ++ SendCoinsDialog.h => sendcoinsdialog.h | 12 +- sendcoinsdialog.ui | 146 ++++++++++++++++++ ...ableModel.cpp => transactiontablemodel.cpp | 0 ...ionTableModel.h => transactiontablemodel.h | 0 25 files changed, 447 insertions(+), 69 deletions(-) delete mode 100644 AddressBookDialog.cpp delete mode 100644 OptionsDialog.cpp delete mode 100644 SendCoinsDialog.cpp delete mode 100644 SettingsDialog.cpp delete mode 100644 SettingsDialog.h rename AboutDialog.cpp => aboutdialog.cpp (100%) rename AboutDialog.h => aboutdialog.h (100%) create mode 100644 addressbookdialog.cpp rename AddressBookDialog.h => addressbookdialog.h (52%) create mode 100644 addressbookdialog.ui rename AddressTableModel.cpp => addresstablemodel.cpp (100%) rename AddressTableModel.h => addresstablemodel.h (100%) rename BitcoinGUI.cpp => bitcoingui.cpp (93%) rename BitcoinGUI.h => bitcoingui.h (100%) create mode 100644 mainoptionspage.cpp create mode 100644 mainoptionspage.h create mode 100644 optionsdialog.cpp rename OptionsDialog.h => optionsdialog.h (50%) create mode 100644 sendcoinsdialog.cpp rename SendCoinsDialog.h => sendcoinsdialog.h (67%) create mode 100644 sendcoinsdialog.ui rename TransactionTableModel.cpp => transactiontablemodel.cpp (100%) rename TransactionTableModel.h => transactiontablemodel.h (100%) diff --git a/AddressBookDialog.cpp b/AddressBookDialog.cpp deleted file mode 100644 index 7e853ede..00000000 --- a/AddressBookDialog.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "AddressBookDialog.h" - -AddressBookDialog::AddressBookDialog(QWidget *parent) : - QDialog(parent) -{ -} - diff --git a/OptionsDialog.cpp b/OptionsDialog.cpp deleted file mode 100644 index 891b43f4..00000000 --- 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/SendCoinsDialog.cpp b/SendCoinsDialog.cpp deleted file mode 100644 index a89a58dc..00000000 --- a/SendCoinsDialog.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "SendCoinsDialog.h" - -SendCoinsDialog::SendCoinsDialog(QWidget *parent) : - QDialog(parent) -{ -} diff --git a/SettingsDialog.cpp b/SettingsDialog.cpp deleted file mode 100644 index d2ce01ee..00000000 --- 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 7bbfb1f8..00000000 --- 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 4729921c..3d1785cd 100644 --- a/TODO +++ b/TODO @@ -51,3 +51,6 @@ AboutDialog - Translation +- Toolbar icon + +- 'notify' on incoming transaction diff --git a/AboutDialog.cpp b/aboutdialog.cpp similarity index 100% rename from AboutDialog.cpp rename to aboutdialog.cpp diff --git a/AboutDialog.h b/aboutdialog.h similarity index 100% rename from AboutDialog.h rename to aboutdialog.h diff --git a/addressbookdialog.cpp b/addressbookdialog.cpp new file mode 100644 index 00000000..ca74159d --- /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 similarity index 52% rename from AddressBookDialog.h rename to addressbookdialog.h index 3a27aa62..a51c02a7 100644 --- a/AddressBookDialog.h +++ b/addressbookdialog.h @@ -3,16 +3,26 @@ #include +namespace Ui { + class AddressBookDialog; +} + class AddressBookDialog : public QDialog { Q_OBJECT + public: explicit AddressBookDialog(QWidget *parent = 0); + ~AddressBookDialog(); -signals: - -public slots: + 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 00000000..e646b08e --- /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 similarity index 100% rename from AddressTableModel.cpp rename to addresstablemodel.cpp diff --git a/AddressTableModel.h b/addresstablemodel.h similarity index 100% rename from AddressTableModel.h rename to addresstablemodel.h diff --git a/bitcoin.pro b/bitcoin.pro index 99a2601e..01d93ddb 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 similarity index 93% rename from BitcoinGUI.cpp rename to bitcoingui.cpp index 0f122377..a3b346ed 100644 --- a/BitcoinGUI.cpp +++ b/bitcoingui.cpp @@ -5,8 +5,7 @@ */ #include "BitcoinGUI.h" #include "TransactionTableModel.h" -#include "AddressBookDialog.h" -#include "SettingsDialog.h" +#include "addressbookdialog.h" #include "SendCoinsDialog.h" #include "OptionsDialog.h" #include "AboutDialog.h" @@ -144,15 +143,15 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): /* Status bar */ statusBar(); - QLabel *label_connections = new QLabel("6 connections", this); + 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", this); + 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", this); + QLabel *label_transactions = new QLabel("6 transactions"); label_transactions->setFrameStyle(QFrame::Panel | QFrame::Sunken); label_transactions->setMinimumWidth(100); @@ -166,8 +165,8 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): 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(button_new, SIGNAL(clicked()), this, SLOT(newAddressClicked())); + connect(button_clipboard, SIGNAL(clicked()), this, SLOT(copyClipboardClicked())); connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked())); } @@ -182,7 +181,7 @@ void BitcoinGUI::addressbookClicked() { qDebug() << "Address book clicked"; AddressBookDialog dlg; - /* TODO: Set tab to "Sending" */ + dlg.setTab(AddressBookDialog::SendingTab); dlg.exec(); } @@ -190,7 +189,7 @@ void BitcoinGUI::receivingAddressesClicked() { qDebug() << "Receiving addresses clicked"; AddressBookDialog dlg; - /* TODO: Set tab to "Receiving" */ + dlg.setTab(AddressBookDialog::ReceivingTab); dlg.exec(); } diff --git a/BitcoinGUI.h b/bitcoingui.h similarity index 100% rename from BitcoinGUI.h rename to bitcoingui.h diff --git a/mainoptionspage.cpp b/mainoptionspage.cpp new file mode 100644 index 00000000..d833a793 --- /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 00000000..de2ef9fc --- /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 00000000..a70eadd5 --- /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 similarity index 50% rename from OptionsDialog.h rename to optionsdialog.h index 529eb214..2a4beacc 100644 --- a/OptionsDialog.h +++ b/optionsdialog.h @@ -2,6 +2,8 @@ #define OPTIONSDIALOG_H #include +#include +#include class OptionsDialog : public QDialog { @@ -12,7 +14,12 @@ public: 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 00000000..ef3ade68 --- /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 similarity index 67% rename from SendCoinsDialog.h rename to sendcoinsdialog.h index f2720c37..82fae9cf 100644 --- a/SendCoinsDialog.h +++ b/sendcoinsdialog.h @@ -3,16 +3,20 @@ #include +namespace Ui { + class SendCoinsDialog; +} + class SendCoinsDialog : public QDialog { Q_OBJECT + public: explicit SendCoinsDialog(QWidget *parent = 0); + ~SendCoinsDialog(); -signals: - -public slots: - +private: + Ui::SendCoinsDialog *ui; }; #endif // SENDCOINSDIALOG_H diff --git a/sendcoinsdialog.ui b/sendcoinsdialog.ui new file mode 100644 index 00000000..56ec6d3d --- /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 similarity index 100% rename from TransactionTableModel.cpp rename to transactiontablemodel.cpp diff --git a/TransactionTableModel.h b/transactiontablemodel.h similarity index 100% rename from TransactionTableModel.h rename to transactiontablemodel.h 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: [PATCH 013/312] 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 819c55e3..94f5bcbf 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 dd2ec9f9..90d74e69 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 b25ee3e9..95ea7326 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 d43befd4..ebc0570c 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 01d93ddb..4e9bfda3 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 a3b346ed..4083afbf 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 d833a793..021e1e47 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 a70eadd5..e609e544 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 ef3ade68..283039f2 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 d36bea21..5a84d2ea 100644 --- a/transactiontablemodel.cpp +++ b/transactiontablemodel.cpp @@ -1,4 +1,4 @@ -#include "TransactionTableModel.h" +#include "transactiontablemodel.h" #include 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: [PATCH 014/312] 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 3d1785cd..0cc78f11 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 90d74e69..3d7a3f98 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 13721210..827cc741 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 00000000..2cc178fb --- /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 ca74159d..e1ce1fee 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 a51c02a7..e287019d 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 e646b08e..530322ee 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 4e9bfda3..7bcf3afd 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 ebce276c..0d91bbcd 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 4083afbf..c78bb3c5 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 621ca40245be8863d582ae83d25bffb95b476fc6..abfb3c3a5107343044db140ad70c3eccfa87fe9e 100644 GIT binary patch literal 1211 zcmV;s1VsCZP)5a821AU-MO{GT zp$WC>LJ&}6L`ZB1-~(YH3dWLKA{!7TF$lfAm)p5B^BotRLK{#QrYAX>Oujki|DE&u za=uX_!epAB)a-!$uMeU)Mw~-LaNHjYv0)itBLxM0QsDwP*^&-xRe*c3V&i8yg z&th!~V+=~Ee-uz6LVJ6A*Tst$H|KJ>HsAM2r_*>|N@X&c>wo-y{m&~`uat_#;wOM) zbGc_~YicqzkJJzZ0YNb0%0Ykvf*>G@B9>3TDHl>L+fJW8eafz0+2Ms@xZ~QjYt`d{ z$FX3PG>lX#4Ta$XC%-<`Zbk9rQ%lHZAHef1;=q`R81>o~n7f!IXA3U_r>qmFfRcz* zYpsD%5v@ndwl2llGvCv(q62F*UmQKc$`u`0W5C?g5Kw?pXss*vFc1-)!kvybW1tALm8cZxJkO-&$zIEa;CwPOAH z4J=){oIr?4*G3*+w20!ZVFm_%#sN<(Tu3YmlRzDd?_K;Z51qf8aw230gM)*(Fs7nnDy0!x z+aBf6{*O8H?Kfy`cxTT}Hgs?1hjZtclFMQ}i)Rg>X-O)md3`wtUp>hD`SW*95O4|_ z8X9oo5T`%{Z8cq+x>&P%9fjaF{a+qMfCUR4LjdKHQu0_HTfaI$uQs;OyKz~GiG zTW(AcFa~DV&&D}N>>MHval)#O4z_K3g|HIS^VXY0QABG?8^$P%(rBfSB>oOgttT=N z5e$${`xEM{gb~O4``NzzHBu?di4(_Jwrn{m`|pEzYyesp#iF%NDWxcv%gO54LxVVG z&YH#Ey?cn`n00Gc6NND@j&VrR_`5S~>+X4kVVN)t4S@1}-{x|;eK&60n3v6F-Lz@b zm^yVT)z#HxtFp>Z`;xzyS66?#`j^s_5cP0VQ)8;WpYc*Dvk1$vA*x6#M)F;x?Qwk z7=~dOjl?c0Rf4b_1VIU)TrQW3#Zq~2FdrU1bSQ%4J^&s}czB6t0Ds(jDruZ%^garK z2$70FMZpLVD5WYulz0pj#z@6utdqqc5j06sod~D|kpl#!oEnXd$0Z%_cPf}PoAkem ZzX3Nh0V!cC;+p^f002ovPDHLkV1mI9FyH_H literal 656 zcmV;B0&o3^P)1RCwBylTT<9K@`T{Y&Y9vP11%& zstq<(Db$ckda58rp+vlhhqhNQA|CWm^lB;Cn}RtBr6=(sf*w2xrjbGqtx69|C4wlL zLsE+kyKHTm?9A@?X1Xi>iO@dy_RY?I{N}ygw`6K|_7VUbMNw1(WuvN`^RNCoP6!#m zoDyrXcdfDTl5>Cy_iLcKuG{x~Zf<^YZ4J(3GF#tmd#hZjd|oz;<&Tw0aj?HXKbFgl zWzy-B$#^{Zwp>o~rwauH-9kx#$bct8r=}%)&@`IK3=JJWaqf2EfpvXy>Q1*FiG~yf z!m28LuE%rWhvG6u0zeRAtq_RboS2vosU6Y3pSdoFRWIuUTz6w;I{)x@QxJ@?fA)lL zm1oOuVED2Gj5x-Nk(QD5Ja1nB;k#QX-oR%m2{_#}fHF?nRX{0iTY-dADqB|>^L~-{ z0|YyaTl`IX*7nqk9$5gI6HojI&*HYCYRSY7GYVwgWrc6uvYW qw?#Vy{vX{a-T%d{b_N0XDZl{2Ogn_%%oR8Q0000mz$e+&*A z9&fy@cK`qdO-V#SR7l5-mRqP@M;*q0|Cw29?RDAv?6Y@rYI2N8)21rcCdKy2;ssxn z6rTj4l_;o4DB=wv5JjyLp;UxE_@ECWieM`!f)7$Et)^xA9>g$cc$HGYrEF^Z!2Pe?K$dH?nwip0!g0y!U8KXpvADJyrsM0)%U&nsss? zgi!-qS^i%!*NrStZX4G(53(6MDyQmU1rZRDTO_E#PA%Z z)ZW_{e{t$>%bzojMzqX!drJg0)CsgduNnNR0(Top1t@HAcgS=){cJoxe=WGc7o`D8 zgWQDV#wiQrmU?1oJtQy&GN1r*AS2Tq%Y$D%%%Kn5`32d<6E*=`fGxpR*SDJ!*cqTr zp?O5E5!675ty7_-J!sHaeG+li?wi!CixS!0!*1n<#4X_$Sz~pfOBX;FOdk+rxQgIs= zm+er3X!;=0$nYjb}1zR^2k;`s?#I6+04F!FIDmomy3Mw^_U zJICw%lcYZ;9w#J8e?l@zNRyP!K}wYs?5q|(7B9qAwZdOa(Zk|UJGiB1{KBgnjtVuC zC}Sm9=1nf*;!si$GiF?mNk?8IvqQCosM`CGYik|ABqq|JO%Xr>2$r zv{jd#dN+I2K6cA)=53c|9^n(99Rp&isK~3FET1qcMr_Ike?8UXin@dqp`Z7uhBbB! z(@9CQ(ZXmE(-;IILnF0kb)8y+2TChE-5^j+8sedip_YoNO6eoSMU2i}bfJ+-qe?}X zg^V}Hm;^fSY;orD+3yTa){mKYH{a{*Ti81nM$uxou|vADK&xsKW=*`(L$pFHC=7z3 zD_wj)q(6L>f4YDwuQ92MI|zuzQ~YiN9hmQ}{dxGP_P{LP{(An#`Wr7T%r$=$mC;E( z*1OBFbmKH;G#=8wIwD;IyGmWIviYg|4<9`K$PXS^GN-X6IGKw(^l2p>a<4!|>coZT zdf(N)APS7B4WKW&co#is{NCVDs~V*>%27yQn6{@#e=5B7Ow4aj|8d8eYU%3HhrU8@ z(!(7#n2YCG9h{@l@*Y3??AmeH6GYFX+=x{utC&@d7L5|YCk~@3ag7y`T(VRK52AwV0R=8SR z^w6VKe|GrY(vt-3;3vx`&wt%S4qB`0T+rME8)XFJC|t+zkNe+*F5qpctU^fz0Zo4W z%Fk)eM?YOUdF~NE3{YC#;BMwdaD@s52^^lj^W_=%mac3Wj=!VT<%wrc&}{j?TzdZB zUkXDPtreo=Z5&NNsaaaQoeodk^;g{e4kplucbNs+%95F>)yk<*9Yc5njKKScQ$p;00000NkvXXu0mjf<)yAK delta 890 zcmV-=1BLv}3#tbpiBL{Q4GJ0x0000DNk~Le0000G0000G2nGNE03Y-JVUZy}e+Lv1 z8dQ!TLjV8+DoI2^R5;6ZlS^z=WdKIcea+l?b!N)YX_;bcgN<5KA=HWw41xh;v<|{sq zNrE!WSAR{Avn=wN0!Bc@e?(rK*Fq!48|7P#p|wnt4N5yh;T`n2?&Tg=AGb``VKI^{U&dIe2j)A z@D8ZGI#r}Iqkvm-`s^oIk65UNilRws%_bpF8~X;HLsxaOg$!rDf1X6S22M~x_9JyP z7IjTQvn1n_6O8{b0UkhwaLCW+hsM!Won&s27fOn0i8;v^`qV>I_JWDu359#?6)0> z764p;Td+`?BR^;Hf7-;W+$bjTMWI!Iw)<7|PW{*V_1e$3k95NQg1fltstwU^Uo1MK`-<>|*xOC^rv6J4z{!QwKR=P^dR0wVg;`93u!l9I3 zqr6hatEoh~1GaU;KDctUdit)IqH%LH*zbx5x;+m?H^yiWP)2ZsL(WQ(TTM~_$4OW- z^>@<8>(l>PBS7eg>h-jndxKs6r-R|ZW+{a%TIGdesytn{8dHUnqPc$mFN0oldve>! QcK`qY07*qoM6N<$f&@9O{{R30 diff --git a/res/images/about.png b/res/images/about.png new file mode 100644 index 0000000000000000000000000000000000000000..c9ab9511efb6da23110dea9a1630c8d59336242a GIT binary patch literal 3488 zcmai13se(l7EYx{j|IghdybX*Xcq`oWJAJB3CQaD2mwMDFd~bfktFhz6&xOhr0%Yd zu|{M8NyLHUZUYSjFe1xCL#&(S8A2%_2_djmn;6JLoLxpV$A_s;*{ z@4Mf<1Ab09wC)3k4^Sx7xlWTlZ!Ap;8Gmk9;Bd=}3*Ks~CiY z!b@AphO1mUJI7&5h{(<%^qnItixVLjYStLBWi724Cb?gqeSj17@qkCHY!t{akPN}W zPE^*RzgZ72^Ldkt*NZ}Oy!Qy3i0>cEd9hb}dn?1Lp>K?DzCR~%Ux!+Wq*^Ut z3J$KSHmUNJ`l*%{t7`GH#pL8Fb%?>B#QV&IF1m4aM6?&`9Uy)r`J018HD%)QUf$EX zQV=?4+MVMouDv(;G=0_L;BEttmr!?YYluNqMITOX z;4D6-YO@G4ePQ*FvL$i~5<#ZT9KApmQRx*>s*Lx>9U~id;y6=>Y302~BT0r?iMyV& ztU8y;S6JbhxtAAgK_Q>ty(h@vS$yc46L9b#gNY%|3=_Qym8zj&(X%*q@dQFXWU{#l zPll&fPsW8T=&$Dj-XH=kZ06wWxX(IWE9D;5ew^kAGdmZTs~5mbyH6K*#G>0 zwfhaB-qJO!@NTmeX16de595L+s?{SoMX+obr_e;>^ovw&7TQ%1XRY`Z0<5Ac<6t4r zH~sn3TnsTNh+n_fG&nWc3qoA($3AFcVj|y?0#UT}jY#|m4vuS{=r>DeX*A}H$z~0q zJ53)(EAQczh8n&y6$#=@LZrTOcB0sF>(&;(=upHl0!_CCo8|}Kro-s`3HEp~xm#je8i)Gq>BA8YY6MHn! za4+gewG+_N>S2!Byd@+jW9P&lBCjBU(#U?*iNl1${N$7>cc=clEl)-%PkWUOAIC4- zQ|y7yi)@!T_8@8TP-B_}pp8B}pYMVpIyzdW>DGqIBN$g#SDzFo0PCXD!U<>4oRM-{ zS;_3imU(+G$#`EvXD0|5jpd~jL%MHbA_#F@5X!~Q-mEF+&5zXpM6ri|&TJ};sZ-o; z3&ppjxuXrX`q%kHm(+)v8m_fhBkfzSGA6tAn%<7h!5q)#{)ObhvT)&^Bja z+StjGCeeRTpT-a`t*67v)4Y*d!vqLO+4#qexvsB!p3h*2U&0p%>GdaD(ZtS9zO+(9 z5j|tus^J!ckPdNs;X2f8LXn^VQ{B}K}V8+lpSYr z29rjwmnpved~|hxT_RWrf{l$b%4q!^BqhFjEvBh}7Zv4u&Rbl2E<>xi=<3S1L~{>n zhliy3E6$Nt8^%MW6olH=!~Z>4(@Jvf*XMlf%o)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 - - - - + 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: [PATCH 015/312] 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 2cc178fb..17a15dd0 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 530322ee..8620a1cc 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 b1adbc51..ef7eaf37 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 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: [PATCH 016/312] 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 ebc0570c..ad983562 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 c78bb3c5..01b18f1b 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 0c9edad8..6e1003cd 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 1eaa3588..d8ef7812 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() 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: [PATCH 017/312] 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 ad983562..456ce1e9 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 01b18f1b..771b9026 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 6e1003cd..89e3897a 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(); 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: [PATCH 018/312] 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 456ce1e9..403e4c51 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 771b9026..072dc929 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 89e3897a..12bbf58b 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); 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: [PATCH 019/312] 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 0cc78f11..4f81ec95 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 e1ce1fee..72b0da35 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 e287019d..d075c9ba 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 8620a1cc..0aa093e0 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 95ea7326..e8746c2b 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 490452e1..50ed80dd 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 7bcf3afd..d0b9512b 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 0d91bbcd..80904b34 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 072dc929..4af703cf 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 00000000..bd555979 --- /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 00000000..650ed534 --- /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 00000000..2d8aa880 --- /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 0000000000000000000000000000000000000000..f2dcb20636509921784894cc075e93f495cf3902 GIT binary patch literal 968 zcmV;(12_DMP)Px#24YJ`L;y_yO#n?P4+J>?000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipY= z4LB*&Ql=#U00T%#L_t(I%UzQ_Y#U_&hM)UB-<@-I96NDhJI+s&Ka@%EOvncQ}i^JmX}U3!>T z9+Y=Zp9)Cfo=J?19E)|wP!wgCxL7E1BlTxtV!CM^MHqr=m5H|6YbuEloEsoS8*wA<*8aWhYlaaFuap*oO-!esG58+-W^f$_p@lK zMkv(5&?Ao$3bx`k49b?p)8pgVO-ZBKz|3aQb)A8seQL?fz9fRd@L?$>g+d8c({UY} zd@c*X(&9Wz3-grA7XH=%l}d$ry+)&M6N|?|aKz(LJ&~O`TzrNO2&AS0ka+A#f^7l( zeuGphg{s(qhkCt<&*uZ^6OQXRuIqy9ph$^fc+u4z!RpEq>2#V*CdNXLpQve?QxfLh@pgjk|Z3{lW0`t}}gd?u^Lg)?exBicSs= z?eT>|Z3rQ7QK(u~R##U^&COCNSAOg0>YY0K;TPsE!GnD8-dmwucI||!s?XuFw{BZ4 qjplZ~TD4P}*Es*tXWw0ZnEwy#SX00_=&3CL0000 +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 8913f1d9..77ad7306 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; 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: [PATCH 020/312] 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 72b0da35..71543f11 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 d075c9ba..bf7c2a65 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 0aa093e0..d66962a2 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 e8746c2b..d985bcee 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 d0b9512b..b34f562f 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 00000000..408027b4 --- /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 00000000..f6a2ac02 --- /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 12bbf58b..9142b6b8 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 e609e544..70dd8632 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 2a4beacc..501c82e9 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 d8ef7812..6f459fb6 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 e3ffd1d3..a2fcdd07 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 ef7eaf37..f14ec2af 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 + + + + 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: [PATCH 021/312] begin integration with bitcoin upstream --- bitcoin.pro | 63 +- bitcoin.qrc => gui/bitcoin.qrc | 0 aboutdialog.ui => gui/forms/aboutdialog.ui | 4 +- .../forms/addressbookdialog.ui | 0 .../forms/editaddressdialog.ui | 0 .../forms/sendcoinsdialog.ui | 4 +- aboutdialog.h => gui/include/aboutdialog.h | 0 .../include/addressbookdialog.h | 0 .../include/addresstablemodel.h | 0 .../include/bitcoinaddressvalidator.h | 2 + bitcoingui.h => gui/include/bitcoingui.h | 0 .../include/editaddressdialog.h | 0 .../include/mainoptionspage.h | 0 .../include/optionsdialog.h | 0 .../include/sendcoinsdialog.h | 0 .../include/transactiontablemodel.h | 0 {res => gui/res}/icons/address-book.png | Bin {res => gui/res}/icons/bitcoin.png | Bin {res => gui/res}/icons/quit.png | Bin {res => gui/res}/icons/send.png | Bin {res => gui/res}/icons/toolbar.png | Bin {res => gui/res}/images/about.png | Bin aboutdialog.cpp => gui/src/aboutdialog.cpp | 0 .../src/addressbookdialog.cpp | 0 .../src/addresstablemodel.cpp | 0 bitcoin.cpp => gui/src/bitcoin.cpp | 0 .../src/bitcoinaddressvalidator.cpp | 0 bitcoingui.cpp => gui/src/bitcoingui.cpp | 0 .../src/editaddressdialog.cpp | 0 .../src/mainoptionspage.cpp | 0 .../src/optionsdialog.cpp | 0 .../src/sendcoinsdialog.cpp | 0 .../src/transactiontablemodel.cpp | 0 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 +++++++++ 38 files changed, 3489 insertions(+), 35 deletions(-) rename bitcoin.qrc => gui/bitcoin.qrc (100%) rename aboutdialog.ui => gui/forms/aboutdialog.ui (97%) rename addressbookdialog.ui => gui/forms/addressbookdialog.ui (100%) rename editaddressdialog.ui => gui/forms/editaddressdialog.ui (100%) rename sendcoinsdialog.ui => gui/forms/sendcoinsdialog.ui (98%) rename aboutdialog.h => gui/include/aboutdialog.h (100%) rename addressbookdialog.h => gui/include/addressbookdialog.h (100%) rename addresstablemodel.h => gui/include/addresstablemodel.h (100%) rename bitcoinaddressvalidator.h => gui/include/bitcoinaddressvalidator.h (94%) rename bitcoingui.h => gui/include/bitcoingui.h (100%) rename editaddressdialog.h => gui/include/editaddressdialog.h (100%) rename mainoptionspage.h => gui/include/mainoptionspage.h (100%) rename optionsdialog.h => gui/include/optionsdialog.h (100%) rename sendcoinsdialog.h => gui/include/sendcoinsdialog.h (100%) rename transactiontablemodel.h => gui/include/transactiontablemodel.h (100%) rename {res => gui/res}/icons/address-book.png (100%) rename {res => gui/res}/icons/bitcoin.png (100%) rename {res => gui/res}/icons/quit.png (100%) rename {res => gui/res}/icons/send.png (100%) rename {res => gui/res}/icons/toolbar.png (100%) rename {res => gui/res}/images/about.png (100%) rename aboutdialog.cpp => gui/src/aboutdialog.cpp (100%) rename addressbookdialog.cpp => gui/src/addressbookdialog.cpp (100%) rename addresstablemodel.cpp => gui/src/addresstablemodel.cpp (100%) rename bitcoin.cpp => gui/src/bitcoin.cpp (100%) rename bitcoinaddressvalidator.cpp => gui/src/bitcoinaddressvalidator.cpp (100%) rename bitcoingui.cpp => gui/src/bitcoingui.cpp (100%) rename editaddressdialog.cpp => gui/src/editaddressdialog.cpp (100%) rename mainoptionspage.cpp => gui/src/mainoptionspage.cpp (100%) rename optionsdialog.cpp => gui/src/optionsdialog.cpp (100%) rename sendcoinsdialog.cpp => gui/src/sendcoinsdialog.cpp (100%) rename transactiontablemodel.cpp => gui/src/transactiontablemodel.cpp (100%) 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 diff --git a/bitcoin.pro b/bitcoin.pro index b34f562f..88b92bc6 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/gui/bitcoin.qrc similarity index 100% rename from bitcoin.qrc rename to gui/bitcoin.qrc diff --git a/aboutdialog.ui b/gui/forms/aboutdialog.ui similarity index 97% rename from aboutdialog.ui rename to gui/forms/aboutdialog.ui index 17a15dd0..88668d4d 100644 --- a/aboutdialog.ui +++ b/gui/forms/aboutdialog.ui @@ -26,7 +26,7 @@ - :/images/about + :/images/about @@ -123,7 +123,7 @@ This product includes software developed by the OpenSSL Project for use in the O - + diff --git a/addressbookdialog.ui b/gui/forms/addressbookdialog.ui similarity index 100% rename from addressbookdialog.ui rename to gui/forms/addressbookdialog.ui diff --git a/editaddressdialog.ui b/gui/forms/editaddressdialog.ui similarity index 100% rename from editaddressdialog.ui rename to gui/forms/editaddressdialog.ui diff --git a/sendcoinsdialog.ui b/gui/forms/sendcoinsdialog.ui similarity index 98% rename from sendcoinsdialog.ui rename to gui/forms/sendcoinsdialog.ui index f14ec2af..31a0b99e 100644 --- a/sendcoinsdialog.ui +++ b/gui/forms/sendcoinsdialog.ui @@ -121,7 +121,7 @@ &Send - + :/icons/send:/icons/send @@ -144,7 +144,7 @@ - + diff --git a/aboutdialog.h b/gui/include/aboutdialog.h similarity index 100% rename from aboutdialog.h rename to gui/include/aboutdialog.h diff --git a/addressbookdialog.h b/gui/include/addressbookdialog.h similarity index 100% rename from addressbookdialog.h rename to gui/include/addressbookdialog.h diff --git a/addresstablemodel.h b/gui/include/addresstablemodel.h similarity index 100% rename from addresstablemodel.h rename to gui/include/addresstablemodel.h diff --git a/bitcoinaddressvalidator.h b/gui/include/bitcoinaddressvalidator.h similarity index 94% rename from bitcoinaddressvalidator.h rename to gui/include/bitcoinaddressvalidator.h index f6a2ac02..8b57a247 100644 --- a/bitcoinaddressvalidator.h +++ b/gui/include/bitcoinaddressvalidator.h @@ -3,6 +3,8 @@ #include +#include + class BitcoinAddressValidator : public QRegExpValidator { Q_OBJECT diff --git a/bitcoingui.h b/gui/include/bitcoingui.h similarity index 100% rename from bitcoingui.h rename to gui/include/bitcoingui.h diff --git a/editaddressdialog.h b/gui/include/editaddressdialog.h similarity index 100% rename from editaddressdialog.h rename to gui/include/editaddressdialog.h diff --git a/mainoptionspage.h b/gui/include/mainoptionspage.h similarity index 100% rename from mainoptionspage.h rename to gui/include/mainoptionspage.h diff --git a/optionsdialog.h b/gui/include/optionsdialog.h similarity index 100% rename from optionsdialog.h rename to gui/include/optionsdialog.h diff --git a/sendcoinsdialog.h b/gui/include/sendcoinsdialog.h similarity index 100% rename from sendcoinsdialog.h rename to gui/include/sendcoinsdialog.h diff --git a/transactiontablemodel.h b/gui/include/transactiontablemodel.h similarity index 100% rename from transactiontablemodel.h rename to gui/include/transactiontablemodel.h diff --git a/res/icons/address-book.png b/gui/res/icons/address-book.png similarity index 100% rename from res/icons/address-book.png rename to gui/res/icons/address-book.png diff --git a/res/icons/bitcoin.png b/gui/res/icons/bitcoin.png similarity index 100% rename from res/icons/bitcoin.png rename to gui/res/icons/bitcoin.png diff --git a/res/icons/quit.png b/gui/res/icons/quit.png similarity index 100% rename from res/icons/quit.png rename to gui/res/icons/quit.png diff --git a/res/icons/send.png b/gui/res/icons/send.png similarity index 100% rename from res/icons/send.png rename to gui/res/icons/send.png diff --git a/res/icons/toolbar.png b/gui/res/icons/toolbar.png similarity index 100% rename from res/icons/toolbar.png rename to gui/res/icons/toolbar.png diff --git a/res/images/about.png b/gui/res/images/about.png similarity index 100% rename from res/images/about.png rename to gui/res/images/about.png diff --git a/aboutdialog.cpp b/gui/src/aboutdialog.cpp similarity index 100% rename from aboutdialog.cpp rename to gui/src/aboutdialog.cpp diff --git a/addressbookdialog.cpp b/gui/src/addressbookdialog.cpp similarity index 100% rename from addressbookdialog.cpp rename to gui/src/addressbookdialog.cpp diff --git a/addresstablemodel.cpp b/gui/src/addresstablemodel.cpp similarity index 100% rename from addresstablemodel.cpp rename to gui/src/addresstablemodel.cpp diff --git a/bitcoin.cpp b/gui/src/bitcoin.cpp similarity index 100% rename from bitcoin.cpp rename to gui/src/bitcoin.cpp diff --git a/bitcoinaddressvalidator.cpp b/gui/src/bitcoinaddressvalidator.cpp similarity index 100% rename from bitcoinaddressvalidator.cpp rename to gui/src/bitcoinaddressvalidator.cpp diff --git a/bitcoingui.cpp b/gui/src/bitcoingui.cpp similarity index 100% rename from bitcoingui.cpp rename to gui/src/bitcoingui.cpp diff --git a/editaddressdialog.cpp b/gui/src/editaddressdialog.cpp similarity index 100% rename from editaddressdialog.cpp rename to gui/src/editaddressdialog.cpp diff --git a/mainoptionspage.cpp b/gui/src/mainoptionspage.cpp similarity index 100% rename from mainoptionspage.cpp rename to gui/src/mainoptionspage.cpp diff --git a/optionsdialog.cpp b/gui/src/optionsdialog.cpp similarity index 100% rename from optionsdialog.cpp rename to gui/src/optionsdialog.cpp diff --git a/sendcoinsdialog.cpp b/gui/src/sendcoinsdialog.cpp similarity index 100% rename from sendcoinsdialog.cpp rename to gui/src/sendcoinsdialog.cpp diff --git a/transactiontablemodel.cpp b/gui/src/transactiontablemodel.cpp similarity index 100% rename from transactiontablemodel.cpp rename to gui/src/transactiontablemodel.cpp diff --git a/lib/include/base58.h b/lib/include/base58.h new file mode 100644 index 00000000..7acdf63a --- /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 00000000..e3535329 --- /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 00000000..f810b339 --- /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 00000000..356b2dc8 --- /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 00000000..af8cfcf6 --- /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 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: [PATCH 022/312] 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 88b92bc6..dd31f3ee 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 00000000..0737027f --- /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 00000000..113b8a1f --- /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 00000000..1461af7b --- /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 00000000..0e2a1477 --- /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 00000000..5258e2ab --- /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 00000000..418c3907 --- /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 00000000..6433f973 --- /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 00000000..8d0dbfca --- /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 00000000..8b137891 --- /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 00000000..fa0daec3 --- /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 00000000..9a468ab6 --- /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 00000000..d36d3253 --- /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 00000000..1ff6c0b4 --- /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 8b57a247..d1a06f7e 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 408027b4..8e719163 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 6f459fb6..6c216d39 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 e3535329..c207af10 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 356b2dc8..14feb168 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 af8cfcf6..e25004fa 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); 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: [PATCH 023/312] core is a more appropriate name for the bitcoin library --- bitcoin.pro | 12 ++++++------ {lib => core}/include/base58.h | 0 {lib => core}/include/bignum.h | 0 {lib => core}/include/serialize.h | 0 {lib => core}/include/uint256.h | 0 {lib => core}/include/util.h | 0 6 files changed, 6 insertions(+), 6 deletions(-) rename {lib => core}/include/base58.h (100%) rename {lib => core}/include/bignum.h (100%) rename {lib => core}/include/serialize.h (100%) rename {lib => core}/include/uint256.h (100%) rename {lib => core}/include/util.h (100%) diff --git a/bitcoin.pro b/bitcoin.pro index dd31f3ee..84ecc9a0 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/lib/include/base58.h b/core/include/base58.h similarity index 100% rename from lib/include/base58.h rename to core/include/base58.h diff --git a/lib/include/bignum.h b/core/include/bignum.h similarity index 100% rename from lib/include/bignum.h rename to core/include/bignum.h diff --git a/lib/include/serialize.h b/core/include/serialize.h similarity index 100% rename from lib/include/serialize.h rename to core/include/serialize.h diff --git a/lib/include/uint256.h b/core/include/uint256.h similarity index 100% rename from lib/include/uint256.h rename to core/include/uint256.h diff --git a/lib/include/util.h b/core/include/util.h similarity index 100% rename from lib/include/util.h rename to core/include/util.h 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: [PATCH 024/312] 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 84ecc9a0..69340137 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 c207af10..5b4c78e7 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 00000000..390602e1 --- /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 00000000..7c9ace06 --- /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 00000000..746dd02f --- /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 f810b339..8e7677a2 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 00000000..d4d1908e --- /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 e25004fa..2d1d4297 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 00000000..0e88e2ea --- /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 d1a06f7e..8322eef7 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 6c216d39..ce95244d 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() 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: [PATCH 025/312] 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 + .../include/json/json_spirit_error_position.h | 54 + json/include/json/json_spirit_reader.h | 62 + .../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/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 4f81ec95..33cf4063 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 69340137..b323ba8a 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 00000000..9826194e --- /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 00000000..d40c5ed0 --- /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 00000000..61b27285 --- /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 00000000..18e53597 --- /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 390602e1..c973d6eb 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 7c9ace06..92b73fe5 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 00000000..afb19526 --- /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 00000000..48a7b8a8 --- /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 00000000..22a6020d --- /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 00000000..52c0f5b4 --- /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 00000000..3126d348 --- /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 00000000..099d9e07 --- /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 00000000..68b6b4ee --- /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 00000000..4f489fdb --- /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 00000000..9efcbbb1 --- /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 00000000..97334ca0 --- /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 0e88e2ea..4e93f625 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 00000000..ac1879d5 --- /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 00000000..17208507 --- /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 00000000..96494a97 --- /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 00000000..4dec00e6 --- /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 00000000..7e59c9ad --- /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 00000000..553e3b96 --- /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 00000000..7e83a2a7 --- /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 00000000..52e14068 --- /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 00000000..28c49ddc --- /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 00000000..3469bf17 --- /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 00000000..2d723685 --- /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 00000000..32dfeb2c --- /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 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: [PATCH 026/312] todo update --- TODO | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index 33cf4063..d1713eba 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? 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: [PATCH 027/312] 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 d1713eba..dceee9b2 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 7acdf63a..580bd3fc 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 746dd02f..6bbcd64e 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 2d1d4297..b1eabd52 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 4f489fdb..1320781c 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) { 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: [PATCH 028/312] 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 dceee9b2..3136696f 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 31a0b99e..ce4edde4 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 a2fcdd07..95dd34b1 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 71543f11..9ad18f1c 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 4af703cf..760789a0 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 ce95244d..3907b4bc 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() 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: [PATCH 029/312] 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 760789a0..e7cd34aa 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()); } 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: [PATCH 030/312] 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 00000000..ef33af07 --- /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 3136696f..00000000 --- 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 - - 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: [PATCH 031/312] update build system for macosx --- bitcoin.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/bitcoin.pro b/bitcoin.pro index b323ba8a..e87169e8 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 \ From ad88e7626ba5ac2274d7ec9c218537ec7df0ad50 Mon Sep 17 00:00:00 2001 From: Wladimir van der Laan Date: Sun, 22 May 2011 14:54:13 +0200 Subject: [PATCH 032/312] 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 403e4c51..a5f4a05a 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); 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: [PATCH 033/312] 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 e87169e8..f3231823 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 3126d348..5528c430 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 9142b6b8..3b722fcc 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 00000000..fb1a5ede --- /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 a5f4a05a..c843cc40 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 e7cd34aa..5546a0ec 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 00000000..0f191023 --- /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()); +} 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: [PATCH 034/312] 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 5528c430..d40d29cf 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"); 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: [PATCH 035/312] 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 50ed80dd..97780c54 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 77ad7306..e86ab988 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 9ad18f1c..4ca863bd 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 d985bcee..9a72cd97 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 5546a0ec..168dffd7 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 ee58ecba..0f16a3fa 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) 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: [PATCH 036/312] 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 d66962a2..761ea0fb 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 e86ab988..684a9470 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 9a72cd97..aec29fa2 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 168dffd7..d8c72ae0 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 0f16a3fa..454a10d9 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(); + } +} + 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: [PATCH 037/312] 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 f3231823..f4f6a1a1 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 00000000..a73eadc0 --- /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 684a9470..2d562995 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 00000000..3a5b5ac3 --- /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 454a10d9..af907474 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 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: [PATCH 038/312] 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 f4f6a1a1..5f406e82 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 00000000..47eed05d --- /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 2d562995..2ff3b0cb 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 d8c72ae0..d7a71fc5 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 3a5b5ac3..d27a8e10 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 00000000..8126cc76 --- /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 af907474..4f84f4ae 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(); 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: [PATCH 039/312] 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 4f84f4ae..ffc2472f 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; } 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: [PATCH 040/312] 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 3b722fcc..80c24a37 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 fb1a5ede..a5613e34 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 2ff3b0cb..1ea749d8 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 d7a71fc5..831bef3f 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 0f191023..cc52df41 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 ffc2472f..900cb6da 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 { 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: [PATCH 041/312] 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 3907b4bc..123b930a 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(); 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: [PATCH 042/312] 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 47eed05d..646f87c9 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 8126cc76..4024e25c 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 900cb6da..a5d8ffda 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); 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: [PATCH 043/312] 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 a5d8ffda..2f1b77ee 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: 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: [PATCH 044/312] 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 2f1b77ee..af8234bd 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(); } 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: [PATCH 045/312] 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 5f406e82..e4d694eb 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 00000000..cdd1a74d --- /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 646f87c9..9769d806 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 1ea749d8..b02019d4 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 831bef3f..b409744b 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 cc52df41..e355676c 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 af8234bd..3a501979 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()]; 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: [PATCH 046/312] "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 9769d806..d8aca761 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 4024e25c..9feca443 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 3a501979..57d618e9 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: 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: [PATCH 047/312] 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 e4d694eb..49574d60 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 \ 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: [PATCH 048/312] 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 80c24a37..955d7b47 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 a5613e34..828c80f8 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 95dd34b1..f73c38d6 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 b409744b..66a3edd3 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 e355676c..be8b0b41 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 123b930a..398e236e 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() 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: [PATCH 049/312] 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 49574d60..53835632 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 828c80f8..44f1c0ab 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 de2ef9fc..4ef5e60a 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 501c82e9..c064b0a9 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 00000000..4dd21c7f --- /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 66a3edd3..66891222 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 be8b0b41..35824720 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 021e1e47..3d69baed 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 70dd8632..e1f3d67d 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 00000000..25c7366c --- /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 398e236e..9040d21f 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() 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: [PATCH 050/312] 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 44f1c0ab..49b34609 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 4dd21c7f..3e0bcc1d 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 35824720..641c515e 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 25c7366c..e3287f39 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 9040d21f..a6ab6015 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; 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: [PATCH 051/312] 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 c064b0a9..ff8542d4 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 e1f3d67d..4d0493a2 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); +} 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: [PATCH 052/312] 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 53835632..d34488dd 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 4ef5e60a..00000000 --- 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 00000000..4dd2d1a8 --- /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 ff8542d4..07e85297 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 3e0bcc1d..4fb6d251 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 3d69baed..00000000 --- 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 00000000..e70aa7eb --- /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 4d0493a2..8e7f403a 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 e3287f39..f653f67e 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; +} 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: [PATCH 053/312] 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 b02019d4..b484a597 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 57d618e9..c7fb43ed 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(); } 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: [PATCH 054/312] 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 641c515e..97b5f44f 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; } 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: [PATCH 055/312] 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 97780c54..81fad6db 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 aec29fa2..d1f1a9c8 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(); + } } + 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: [PATCH 056/312] 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 b1eabd52..eccdbd37 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 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: [PATCH 057/312] update readme --- README.rst | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index ef33af07..047af0d1 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) + 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: [PATCH 058/312] readme fix --- README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.rst b/README.rst index 047af0d1..71529643 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 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: [PATCH 059/312] 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 81fad6db..6617bb3e 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 a73eadc0..eaa81999 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 4ca863bd..ba6fdb51 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 d1f1a9c8..21a90442 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 66891222..0dc895dc 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 d27a8e10..59b4de30 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; +} 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: [PATCH 060/312] 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 2d8aa880..763a0bb8 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 8322eef7..c7b2eefc 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 650ed534..8e4a0388 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 eaa81999..748e29bf 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 ba6fdb51..853da585 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 21a90442..c375ca75 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 8e719163..bccf4457 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 bd555979..0699b563 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 59b4de30..d01f23d8 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 a6ab6015..b50ed990 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 c7fb43ed..e23f45dd 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(); } 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: [PATCH 061/312] resolve dependency issues --- bitcoin.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/bitcoin.pro b/bitcoin.pro index d34488dd..e2f282a3 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 \ 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: [PATCH 062/312] 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 6617bb3e..f9ccab4e 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 8e4a0388..3d8a5dcf 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 853da585..3f8e3815 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 c375ca75..1cacd08f 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 0699b563..6c0148d9 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); +} 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: [PATCH 063/312] 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 c7b2eefc..73f6ea1f 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 bccf4457..761a2669 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; } 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: [PATCH 064/312] 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 f9ccab4e..18902609 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 3d8a5dcf..dd776695 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 3f8e3815..9b9e9bbc 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 1cacd08f..fbb2eb52 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 a7f23680..ba5b1d99 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 6c0148d9..ddc7292c 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; + } +} 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: [PATCH 065/312] 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 49b34609..01c0d70f 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 b484a597..70377ea0 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 97b5f44f..7dcbc576 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 e23f45dd..5bea3b59 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(); } } 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: [PATCH 066/312] 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 18902609..87994143 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 01c0d70f..d68b34fe 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 dd776695..6f396d04 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 9b9e9bbc..35078d3a 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 fbb2eb52..1cbc1b5d 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 ba5b1d99..59964960 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 7dcbc576..497f8dc3 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 ddc7292c..dd054176 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; } 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: [PATCH 067/312] 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 d8aca761..c082fffe 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 9feca443..83c7d635 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 5bea3b59..0d50e6e2 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)); } } 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: [PATCH 068/312] 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 35078d3a..0228eff4 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 b50ed990..721ab141 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()); } 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: [PATCH 069/312] 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 71529643..e3d6bd75 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) 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: [PATCH 070/312] 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 497f8dc3..5f3517ab 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()); 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: [PATCH 071/312] 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 e3d6bd75..eb209ce0 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 497f8dc3..5f3517ab 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()); 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: [PATCH 072/312] 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 3d7a3f98..13347961 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() 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: [PATCH 073/312] 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 eb209ce0..70d3c418 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 f653f67e..98fd3b11 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 */ } 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: [PATCH 074/312] 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 8e7f403a..1ec777c4 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) 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: [PATCH 075/312] 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 70d3c418..bb14be60 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 955d7b47..8b45dace 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 4fb6d251..0124e2ab 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 c843cc40..663590d0 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 59964960..b2aee6b3 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 98fd3b11..37d5cb15 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; } 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: [PATCH 076/312] 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 e2f282a3..9a3570aa 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 00000000..e58ccc22 --- /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 d40c5ed0..33aeef33 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 8b45dace..c2c786b2 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 d68b34fe..09d1fc92 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 663590d0..dc3e8070 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 b2aee6b3..b6872046 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 5f3517ab..8fd3599e 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; +} 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: [PATCH 077/312] 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 dc3e8070..a1b74d87 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; 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: [PATCH 078/312] 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 bb14be60..9856370b 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 c2c786b2..ab7b4bbd 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 a1b74d87..6dbcb6c2 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 b6872046..70da0b23 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); +} 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: [PATCH 079/312] 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 92b73fe5..8d9b39f7 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 6bbcd64e..d1ded872 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 8e7677a2..0d66d6a9 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 eccdbd37..32a98e62 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 d40d29cf..f7a1ce9e 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 099d9e07..a76374d1 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 68b6b4ee..0456041e 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 1320781c..39360a33 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 9efcbbb1..530bef4a 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 4e93f625..61991092 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; } 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: [PATCH 080/312] 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 ce4edde4..b53e14a5 100644 --- a/gui/forms/sendcoinsdialog.ui +++ b/gui/forms/sendcoinsdialog.ui @@ -57,6 +57,9 @@ 16777215 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + 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: [PATCH 081/312] 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 0228eff4..eaab66a2 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 1cbc1b5d..91b87fb7 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 6dbcb6c2..e0032674 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 70da0b23..ed7d133a 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 8fd3599e..97391e09 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 37d5cb15..1528fdf6 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 83c7d635..6c1f3a5e 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 0d50e6e2..f9978479 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(); } } 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: [PATCH 082/312] 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 9856370b..a1932d84 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 9a3570aa..7758376f 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 00000000..8a44734d --- /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 ab7b4bbd..e18e2ff2 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 00000000..5a859493 --- /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 00000000..4f8f754b --- /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 70377ea0..47e4e4cf 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 ed7d133a..c92a546e 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 00000000..4d8a55e9 --- /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 00000000..3bd4808c --- /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 f9978479..8fe18399 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(); } 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: [PATCH 083/312] 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 88668d4d..45915c85 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. 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: [PATCH 084/312] 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 eaab66a2..90950a64 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(); 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: [PATCH 085/312] 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 47e4e4cf..5974c0e7 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 c92a546e..51dcf87d 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); } 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: [PATCH 086/312] 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 e18e2ff2..96452ef1 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 51dcf87d..96125ef0 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); + } +} 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: [PATCH 087/312] 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 761ea0fb..2b3c69fb 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 763a0bb8..f0ba28a8 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 b53e14a5..595b7f40 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 8a44734d..2f70a382 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 96125ef0..c08476ce 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 d01f23d8..a81cc14f 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 1ec777c4..d46b48fb 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); 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: [PATCH 088/312] 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 a1932d84..f24335b5 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) 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: [PATCH 089/312] move back to original directory structure --- bitcoin.pro | 182 +++++++++--------- {core/include => src}/base58.h | 0 {core/include => src}/bignum.h | 0 {cryptopp/include => src}/cryptopp/config.h | 0 {cryptopp/src => src/cryptopp}/cpu.cpp | 0 {cryptopp/include => src}/cryptopp/cpu.h | 0 {cryptopp/include => src}/cryptopp/cryptlib.h | 0 {cryptopp/include => src}/cryptopp/iterhash.h | 0 {cryptopp/include => src}/cryptopp/misc.h | 0 {cryptopp/include => src}/cryptopp/pch.h | 0 {cryptopp/include => src}/cryptopp/secblock.h | 0 {cryptopp/src => src/cryptopp}/sha.cpp | 0 {cryptopp/include => src}/cryptopp/sha.h | 0 {cryptopp/include => src}/cryptopp/simple.h | 0 {cryptopp/include => src}/cryptopp/smartptr.h | 0 {cryptopp/include => src}/cryptopp/stdcpp.h | 0 {core/src => src}/db.cpp | 0 {core/include => src}/db.h | 0 {core/include => src}/externui.h | 0 {core/include => src}/headers.h | 0 {core/src => src}/init.cpp | 0 {core/include => src}/init.h | 0 {core/src => src}/irc.cpp | 0 {core/include => src}/irc.h | 0 {json/include => src}/json/json_spirit.h | 0 .../json/json_spirit_error_position.h | 0 {json/src => src/json}/json_spirit_reader.cpp | 0 .../include => src}/json/json_spirit_reader.h | 0 .../json/json_spirit_reader_template.h | 0 .../json/json_spirit_stream_reader.h | 0 .../include => src}/json/json_spirit_utils.h | 0 {json/src => src/json}/json_spirit_value.cpp | 0 .../include => src}/json/json_spirit_value.h | 0 {json/src => src/json}/json_spirit_writer.cpp | 0 .../include => src}/json/json_spirit_writer.h | 0 .../json/json_spirit_writer_template.h | 0 {core/include => src}/key.h | 0 {core/src => src}/main.cpp | 0 {core/include => src}/main.h | 0 {core/src => src}/net.cpp | 0 {core/include => src}/net.h | 0 {core/include => src}/noui.h | 0 {gui/src => src/qt}/aboutdialog.cpp | 0 {gui/include => src/qt}/aboutdialog.h | 0 {gui/src => src/qt}/addressbookdialog.cpp | 0 {gui/include => src/qt}/addressbookdialog.h | 0 {gui/src => src/qt}/addresstablemodel.cpp | 0 {gui/include => src/qt}/addresstablemodel.h | 0 {gui/src => src/qt}/bitcoin.cpp | 0 {gui => src/qt}/bitcoin.qrc | 0 .../qt}/bitcoinaddressvalidator.cpp | 0 .../qt}/bitcoinaddressvalidator.h | 0 {gui/src => src/qt}/bitcoingui.cpp | 0 {gui/include => src/qt}/bitcoingui.h | 0 {gui/src => src/qt}/clientmodel.cpp | 0 {gui/include => src/qt}/clientmodel.h | 0 {gui/src => src/qt}/editaddressdialog.cpp | 0 {gui/include => src/qt}/editaddressdialog.h | 0 {gui => src/qt}/forms/aboutdialog.ui | 0 {gui => src/qt}/forms/addressbookdialog.ui | 0 {gui => src/qt}/forms/editaddressdialog.ui | 0 {gui => src/qt}/forms/sendcoinsdialog.ui | 0 .../qt}/forms/transactiondescdialog.ui | 0 {gui/include => src/qt}/guiconstants.h | 0 {gui/src => src/qt}/guiutil.cpp | 0 {gui/include => src/qt}/guiutil.h | 0 {gui/src => src/qt}/monitoreddatamapper.cpp | 0 {gui/include => src/qt}/monitoreddatamapper.h | 0 {gui/src => src/qt}/optionsdialog.cpp | 0 {gui/include => src/qt}/optionsdialog.h | 0 {gui/src => src/qt}/optionsmodel.cpp | 0 {gui/include => src/qt}/optionsmodel.h | 0 {gui => src/qt}/res/icons/address-book.png | Bin {gui => src/qt}/res/icons/bitcoin.png | Bin {gui => src/qt}/res/icons/quit.png | Bin {gui => src/qt}/res/icons/send.png | Bin {gui => src/qt}/res/icons/toolbar.png | Bin {gui => src/qt}/res/images/about.png | Bin {gui/src => src/qt}/sendcoinsdialog.cpp | 0 {gui/include => src/qt}/sendcoinsdialog.h | 0 {gui/src => src/qt}/transactiondesc.cpp | 0 {gui/include => src/qt}/transactiondesc.h | 0 {gui/src => src/qt}/transactiondescdialog.cpp | 0 .../qt}/transactiondescdialog.h | 0 {gui/src => src/qt}/transactionrecord.cpp | 0 {gui/include => src/qt}/transactionrecord.h | 0 {gui/src => src/qt}/transactiontablemodel.cpp | 0 .../qt}/transactiontablemodel.h | 0 {core/src => src}/rpc.cpp | 0 {core/include => src}/rpc.h | 0 {core/src => src}/script.cpp | 0 {core/include => src}/script.h | 0 {core/include => src}/serialize.h | 0 {core/include => src}/strlcpy.h | 0 {core/include => src}/uint256.h | 0 {core/src => src}/util.cpp | 0 {core/include => src}/util.h | 0 97 files changed, 91 insertions(+), 91 deletions(-) rename {core/include => src}/base58.h (100%) rename {core/include => src}/bignum.h (100%) rename {cryptopp/include => src}/cryptopp/config.h (100%) rename {cryptopp/src => src/cryptopp}/cpu.cpp (100%) rename {cryptopp/include => src}/cryptopp/cpu.h (100%) rename {cryptopp/include => src}/cryptopp/cryptlib.h (100%) rename {cryptopp/include => src}/cryptopp/iterhash.h (100%) rename {cryptopp/include => src}/cryptopp/misc.h (100%) rename {cryptopp/include => src}/cryptopp/pch.h (100%) rename {cryptopp/include => src}/cryptopp/secblock.h (100%) rename {cryptopp/src => src/cryptopp}/sha.cpp (100%) rename {cryptopp/include => src}/cryptopp/sha.h (100%) rename {cryptopp/include => src}/cryptopp/simple.h (100%) rename {cryptopp/include => src}/cryptopp/smartptr.h (100%) rename {cryptopp/include => src}/cryptopp/stdcpp.h (100%) rename {core/src => src}/db.cpp (100%) rename {core/include => src}/db.h (100%) rename {core/include => src}/externui.h (100%) rename {core/include => src}/headers.h (100%) rename {core/src => src}/init.cpp (100%) rename {core/include => src}/init.h (100%) rename {core/src => src}/irc.cpp (100%) rename {core/include => src}/irc.h (100%) rename {json/include => src}/json/json_spirit.h (100%) rename {json/include => src}/json/json_spirit_error_position.h (100%) rename {json/src => src/json}/json_spirit_reader.cpp (100%) rename {json/include => src}/json/json_spirit_reader.h (100%) rename {json/include => src}/json/json_spirit_reader_template.h (100%) rename {json/include => src}/json/json_spirit_stream_reader.h (100%) rename {json/include => src}/json/json_spirit_utils.h (100%) rename {json/src => src/json}/json_spirit_value.cpp (100%) rename {json/include => src}/json/json_spirit_value.h (100%) rename {json/src => src/json}/json_spirit_writer.cpp (100%) rename {json/include => src}/json/json_spirit_writer.h (100%) rename {json/include => src}/json/json_spirit_writer_template.h (100%) rename {core/include => src}/key.h (100%) rename {core/src => src}/main.cpp (100%) rename {core/include => src}/main.h (100%) rename {core/src => src}/net.cpp (100%) rename {core/include => src}/net.h (100%) rename {core/include => src}/noui.h (100%) rename {gui/src => src/qt}/aboutdialog.cpp (100%) rename {gui/include => src/qt}/aboutdialog.h (100%) rename {gui/src => src/qt}/addressbookdialog.cpp (100%) rename {gui/include => src/qt}/addressbookdialog.h (100%) rename {gui/src => src/qt}/addresstablemodel.cpp (100%) rename {gui/include => src/qt}/addresstablemodel.h (100%) rename {gui/src => src/qt}/bitcoin.cpp (100%) rename {gui => src/qt}/bitcoin.qrc (100%) rename {gui/src => src/qt}/bitcoinaddressvalidator.cpp (100%) rename {gui/include => src/qt}/bitcoinaddressvalidator.h (100%) rename {gui/src => src/qt}/bitcoingui.cpp (100%) rename {gui/include => src/qt}/bitcoingui.h (100%) rename {gui/src => src/qt}/clientmodel.cpp (100%) rename {gui/include => src/qt}/clientmodel.h (100%) rename {gui/src => src/qt}/editaddressdialog.cpp (100%) rename {gui/include => src/qt}/editaddressdialog.h (100%) rename {gui => src/qt}/forms/aboutdialog.ui (100%) rename {gui => src/qt}/forms/addressbookdialog.ui (100%) rename {gui => src/qt}/forms/editaddressdialog.ui (100%) rename {gui => src/qt}/forms/sendcoinsdialog.ui (100%) rename {gui => src/qt}/forms/transactiondescdialog.ui (100%) rename {gui/include => src/qt}/guiconstants.h (100%) rename {gui/src => src/qt}/guiutil.cpp (100%) rename {gui/include => src/qt}/guiutil.h (100%) rename {gui/src => src/qt}/monitoreddatamapper.cpp (100%) rename {gui/include => src/qt}/monitoreddatamapper.h (100%) rename {gui/src => src/qt}/optionsdialog.cpp (100%) rename {gui/include => src/qt}/optionsdialog.h (100%) rename {gui/src => src/qt}/optionsmodel.cpp (100%) rename {gui/include => src/qt}/optionsmodel.h (100%) rename {gui => src/qt}/res/icons/address-book.png (100%) rename {gui => src/qt}/res/icons/bitcoin.png (100%) rename {gui => src/qt}/res/icons/quit.png (100%) rename {gui => src/qt}/res/icons/send.png (100%) rename {gui => src/qt}/res/icons/toolbar.png (100%) rename {gui => src/qt}/res/images/about.png (100%) rename {gui/src => src/qt}/sendcoinsdialog.cpp (100%) rename {gui/include => src/qt}/sendcoinsdialog.h (100%) rename {gui/src => src/qt}/transactiondesc.cpp (100%) rename {gui/include => src/qt}/transactiondesc.h (100%) rename {gui/src => src/qt}/transactiondescdialog.cpp (100%) rename {gui/include => src/qt}/transactiondescdialog.h (100%) rename {gui/src => src/qt}/transactionrecord.cpp (100%) rename {gui/include => src/qt}/transactionrecord.h (100%) rename {gui/src => src/qt}/transactiontablemodel.cpp (100%) rename {gui/include => src/qt}/transactiontablemodel.h (100%) rename {core/src => src}/rpc.cpp (100%) rename {core/include => src}/rpc.h (100%) rename {core/src => src}/script.cpp (100%) rename {core/include => src}/script.h (100%) rename {core/include => src}/serialize.h (100%) rename {core/include => src}/strlcpy.h (100%) rename {core/include => src}/uint256.h (100%) rename {core/src => src}/util.cpp (100%) rename {core/include => src}/util.h (100%) diff --git a/bitcoin.pro b/bitcoin.pro index 7758376f..dd9ea7ff 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/src/base58.h similarity index 100% rename from core/include/base58.h rename to src/base58.h diff --git a/core/include/bignum.h b/src/bignum.h similarity index 100% rename from core/include/bignum.h rename to src/bignum.h diff --git a/cryptopp/include/cryptopp/config.h b/src/cryptopp/config.h similarity index 100% rename from cryptopp/include/cryptopp/config.h rename to src/cryptopp/config.h diff --git a/cryptopp/src/cpu.cpp b/src/cryptopp/cpu.cpp similarity index 100% rename from cryptopp/src/cpu.cpp rename to src/cryptopp/cpu.cpp diff --git a/cryptopp/include/cryptopp/cpu.h b/src/cryptopp/cpu.h similarity index 100% rename from cryptopp/include/cryptopp/cpu.h rename to src/cryptopp/cpu.h diff --git a/cryptopp/include/cryptopp/cryptlib.h b/src/cryptopp/cryptlib.h similarity index 100% rename from cryptopp/include/cryptopp/cryptlib.h rename to src/cryptopp/cryptlib.h diff --git a/cryptopp/include/cryptopp/iterhash.h b/src/cryptopp/iterhash.h similarity index 100% rename from cryptopp/include/cryptopp/iterhash.h rename to src/cryptopp/iterhash.h diff --git a/cryptopp/include/cryptopp/misc.h b/src/cryptopp/misc.h similarity index 100% rename from cryptopp/include/cryptopp/misc.h rename to src/cryptopp/misc.h diff --git a/cryptopp/include/cryptopp/pch.h b/src/cryptopp/pch.h similarity index 100% rename from cryptopp/include/cryptopp/pch.h rename to src/cryptopp/pch.h diff --git a/cryptopp/include/cryptopp/secblock.h b/src/cryptopp/secblock.h similarity index 100% rename from cryptopp/include/cryptopp/secblock.h rename to src/cryptopp/secblock.h diff --git a/cryptopp/src/sha.cpp b/src/cryptopp/sha.cpp similarity index 100% rename from cryptopp/src/sha.cpp rename to src/cryptopp/sha.cpp diff --git a/cryptopp/include/cryptopp/sha.h b/src/cryptopp/sha.h similarity index 100% rename from cryptopp/include/cryptopp/sha.h rename to src/cryptopp/sha.h diff --git a/cryptopp/include/cryptopp/simple.h b/src/cryptopp/simple.h similarity index 100% rename from cryptopp/include/cryptopp/simple.h rename to src/cryptopp/simple.h diff --git a/cryptopp/include/cryptopp/smartptr.h b/src/cryptopp/smartptr.h similarity index 100% rename from cryptopp/include/cryptopp/smartptr.h rename to src/cryptopp/smartptr.h diff --git a/cryptopp/include/cryptopp/stdcpp.h b/src/cryptopp/stdcpp.h similarity index 100% rename from cryptopp/include/cryptopp/stdcpp.h rename to src/cryptopp/stdcpp.h diff --git a/core/src/db.cpp b/src/db.cpp similarity index 100% rename from core/src/db.cpp rename to src/db.cpp diff --git a/core/include/db.h b/src/db.h similarity index 100% rename from core/include/db.h rename to src/db.h diff --git a/core/include/externui.h b/src/externui.h similarity index 100% rename from core/include/externui.h rename to src/externui.h diff --git a/core/include/headers.h b/src/headers.h similarity index 100% rename from core/include/headers.h rename to src/headers.h diff --git a/core/src/init.cpp b/src/init.cpp similarity index 100% rename from core/src/init.cpp rename to src/init.cpp diff --git a/core/include/init.h b/src/init.h similarity index 100% rename from core/include/init.h rename to src/init.h diff --git a/core/src/irc.cpp b/src/irc.cpp similarity index 100% rename from core/src/irc.cpp rename to src/irc.cpp diff --git a/core/include/irc.h b/src/irc.h similarity index 100% rename from core/include/irc.h rename to src/irc.h diff --git a/json/include/json/json_spirit.h b/src/json/json_spirit.h similarity index 100% rename from json/include/json/json_spirit.h rename to src/json/json_spirit.h diff --git a/json/include/json/json_spirit_error_position.h b/src/json/json_spirit_error_position.h similarity index 100% rename from json/include/json/json_spirit_error_position.h rename to src/json/json_spirit_error_position.h diff --git a/json/src/json_spirit_reader.cpp b/src/json/json_spirit_reader.cpp similarity index 100% rename from json/src/json_spirit_reader.cpp rename to src/json/json_spirit_reader.cpp diff --git a/json/include/json/json_spirit_reader.h b/src/json/json_spirit_reader.h similarity index 100% rename from json/include/json/json_spirit_reader.h rename to src/json/json_spirit_reader.h diff --git a/json/include/json/json_spirit_reader_template.h b/src/json/json_spirit_reader_template.h similarity index 100% rename from json/include/json/json_spirit_reader_template.h rename to src/json/json_spirit_reader_template.h diff --git a/json/include/json/json_spirit_stream_reader.h b/src/json/json_spirit_stream_reader.h similarity index 100% rename from json/include/json/json_spirit_stream_reader.h rename to src/json/json_spirit_stream_reader.h diff --git a/json/include/json/json_spirit_utils.h b/src/json/json_spirit_utils.h similarity index 100% rename from json/include/json/json_spirit_utils.h rename to src/json/json_spirit_utils.h diff --git a/json/src/json_spirit_value.cpp b/src/json/json_spirit_value.cpp similarity index 100% rename from json/src/json_spirit_value.cpp rename to src/json/json_spirit_value.cpp diff --git a/json/include/json/json_spirit_value.h b/src/json/json_spirit_value.h similarity index 100% rename from json/include/json/json_spirit_value.h rename to src/json/json_spirit_value.h diff --git a/json/src/json_spirit_writer.cpp b/src/json/json_spirit_writer.cpp similarity index 100% rename from json/src/json_spirit_writer.cpp rename to src/json/json_spirit_writer.cpp diff --git a/json/include/json/json_spirit_writer.h b/src/json/json_spirit_writer.h similarity index 100% rename from json/include/json/json_spirit_writer.h rename to src/json/json_spirit_writer.h diff --git a/json/include/json/json_spirit_writer_template.h b/src/json/json_spirit_writer_template.h similarity index 100% rename from json/include/json/json_spirit_writer_template.h rename to src/json/json_spirit_writer_template.h diff --git a/core/include/key.h b/src/key.h similarity index 100% rename from core/include/key.h rename to src/key.h diff --git a/core/src/main.cpp b/src/main.cpp similarity index 100% rename from core/src/main.cpp rename to src/main.cpp diff --git a/core/include/main.h b/src/main.h similarity index 100% rename from core/include/main.h rename to src/main.h diff --git a/core/src/net.cpp b/src/net.cpp similarity index 100% rename from core/src/net.cpp rename to src/net.cpp diff --git a/core/include/net.h b/src/net.h similarity index 100% rename from core/include/net.h rename to src/net.h diff --git a/core/include/noui.h b/src/noui.h similarity index 100% rename from core/include/noui.h rename to src/noui.h diff --git a/gui/src/aboutdialog.cpp b/src/qt/aboutdialog.cpp similarity index 100% rename from gui/src/aboutdialog.cpp rename to src/qt/aboutdialog.cpp diff --git a/gui/include/aboutdialog.h b/src/qt/aboutdialog.h similarity index 100% rename from gui/include/aboutdialog.h rename to src/qt/aboutdialog.h diff --git a/gui/src/addressbookdialog.cpp b/src/qt/addressbookdialog.cpp similarity index 100% rename from gui/src/addressbookdialog.cpp rename to src/qt/addressbookdialog.cpp diff --git a/gui/include/addressbookdialog.h b/src/qt/addressbookdialog.h similarity index 100% rename from gui/include/addressbookdialog.h rename to src/qt/addressbookdialog.h diff --git a/gui/src/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp similarity index 100% rename from gui/src/addresstablemodel.cpp rename to src/qt/addresstablemodel.cpp diff --git a/gui/include/addresstablemodel.h b/src/qt/addresstablemodel.h similarity index 100% rename from gui/include/addresstablemodel.h rename to src/qt/addresstablemodel.h diff --git a/gui/src/bitcoin.cpp b/src/qt/bitcoin.cpp similarity index 100% rename from gui/src/bitcoin.cpp rename to src/qt/bitcoin.cpp diff --git a/gui/bitcoin.qrc b/src/qt/bitcoin.qrc similarity index 100% rename from gui/bitcoin.qrc rename to src/qt/bitcoin.qrc diff --git a/gui/src/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp similarity index 100% rename from gui/src/bitcoinaddressvalidator.cpp rename to src/qt/bitcoinaddressvalidator.cpp diff --git a/gui/include/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h similarity index 100% rename from gui/include/bitcoinaddressvalidator.h rename to src/qt/bitcoinaddressvalidator.h diff --git a/gui/src/bitcoingui.cpp b/src/qt/bitcoingui.cpp similarity index 100% rename from gui/src/bitcoingui.cpp rename to src/qt/bitcoingui.cpp diff --git a/gui/include/bitcoingui.h b/src/qt/bitcoingui.h similarity index 100% rename from gui/include/bitcoingui.h rename to src/qt/bitcoingui.h diff --git a/gui/src/clientmodel.cpp b/src/qt/clientmodel.cpp similarity index 100% rename from gui/src/clientmodel.cpp rename to src/qt/clientmodel.cpp diff --git a/gui/include/clientmodel.h b/src/qt/clientmodel.h similarity index 100% rename from gui/include/clientmodel.h rename to src/qt/clientmodel.h diff --git a/gui/src/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp similarity index 100% rename from gui/src/editaddressdialog.cpp rename to src/qt/editaddressdialog.cpp diff --git a/gui/include/editaddressdialog.h b/src/qt/editaddressdialog.h similarity index 100% rename from gui/include/editaddressdialog.h rename to src/qt/editaddressdialog.h diff --git a/gui/forms/aboutdialog.ui b/src/qt/forms/aboutdialog.ui similarity index 100% rename from gui/forms/aboutdialog.ui rename to src/qt/forms/aboutdialog.ui diff --git a/gui/forms/addressbookdialog.ui b/src/qt/forms/addressbookdialog.ui similarity index 100% rename from gui/forms/addressbookdialog.ui rename to src/qt/forms/addressbookdialog.ui diff --git a/gui/forms/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui similarity index 100% rename from gui/forms/editaddressdialog.ui rename to src/qt/forms/editaddressdialog.ui diff --git a/gui/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui similarity index 100% rename from gui/forms/sendcoinsdialog.ui rename to src/qt/forms/sendcoinsdialog.ui diff --git a/gui/forms/transactiondescdialog.ui b/src/qt/forms/transactiondescdialog.ui similarity index 100% rename from gui/forms/transactiondescdialog.ui rename to src/qt/forms/transactiondescdialog.ui diff --git a/gui/include/guiconstants.h b/src/qt/guiconstants.h similarity index 100% rename from gui/include/guiconstants.h rename to src/qt/guiconstants.h diff --git a/gui/src/guiutil.cpp b/src/qt/guiutil.cpp similarity index 100% rename from gui/src/guiutil.cpp rename to src/qt/guiutil.cpp diff --git a/gui/include/guiutil.h b/src/qt/guiutil.h similarity index 100% rename from gui/include/guiutil.h rename to src/qt/guiutil.h diff --git a/gui/src/monitoreddatamapper.cpp b/src/qt/monitoreddatamapper.cpp similarity index 100% rename from gui/src/monitoreddatamapper.cpp rename to src/qt/monitoreddatamapper.cpp diff --git a/gui/include/monitoreddatamapper.h b/src/qt/monitoreddatamapper.h similarity index 100% rename from gui/include/monitoreddatamapper.h rename to src/qt/monitoreddatamapper.h diff --git a/gui/src/optionsdialog.cpp b/src/qt/optionsdialog.cpp similarity index 100% rename from gui/src/optionsdialog.cpp rename to src/qt/optionsdialog.cpp diff --git a/gui/include/optionsdialog.h b/src/qt/optionsdialog.h similarity index 100% rename from gui/include/optionsdialog.h rename to src/qt/optionsdialog.h diff --git a/gui/src/optionsmodel.cpp b/src/qt/optionsmodel.cpp similarity index 100% rename from gui/src/optionsmodel.cpp rename to src/qt/optionsmodel.cpp diff --git a/gui/include/optionsmodel.h b/src/qt/optionsmodel.h similarity index 100% rename from gui/include/optionsmodel.h rename to src/qt/optionsmodel.h diff --git a/gui/res/icons/address-book.png b/src/qt/res/icons/address-book.png similarity index 100% rename from gui/res/icons/address-book.png rename to src/qt/res/icons/address-book.png diff --git a/gui/res/icons/bitcoin.png b/src/qt/res/icons/bitcoin.png similarity index 100% rename from gui/res/icons/bitcoin.png rename to src/qt/res/icons/bitcoin.png diff --git a/gui/res/icons/quit.png b/src/qt/res/icons/quit.png similarity index 100% rename from gui/res/icons/quit.png rename to src/qt/res/icons/quit.png diff --git a/gui/res/icons/send.png b/src/qt/res/icons/send.png similarity index 100% rename from gui/res/icons/send.png rename to src/qt/res/icons/send.png diff --git a/gui/res/icons/toolbar.png b/src/qt/res/icons/toolbar.png similarity index 100% rename from gui/res/icons/toolbar.png rename to src/qt/res/icons/toolbar.png diff --git a/gui/res/images/about.png b/src/qt/res/images/about.png similarity index 100% rename from gui/res/images/about.png rename to src/qt/res/images/about.png diff --git a/gui/src/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp similarity index 100% rename from gui/src/sendcoinsdialog.cpp rename to src/qt/sendcoinsdialog.cpp diff --git a/gui/include/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h similarity index 100% rename from gui/include/sendcoinsdialog.h rename to src/qt/sendcoinsdialog.h diff --git a/gui/src/transactiondesc.cpp b/src/qt/transactiondesc.cpp similarity index 100% rename from gui/src/transactiondesc.cpp rename to src/qt/transactiondesc.cpp diff --git a/gui/include/transactiondesc.h b/src/qt/transactiondesc.h similarity index 100% rename from gui/include/transactiondesc.h rename to src/qt/transactiondesc.h diff --git a/gui/src/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp similarity index 100% rename from gui/src/transactiondescdialog.cpp rename to src/qt/transactiondescdialog.cpp diff --git a/gui/include/transactiondescdialog.h b/src/qt/transactiondescdialog.h similarity index 100% rename from gui/include/transactiondescdialog.h rename to src/qt/transactiondescdialog.h diff --git a/gui/src/transactionrecord.cpp b/src/qt/transactionrecord.cpp similarity index 100% rename from gui/src/transactionrecord.cpp rename to src/qt/transactionrecord.cpp diff --git a/gui/include/transactionrecord.h b/src/qt/transactionrecord.h similarity index 100% rename from gui/include/transactionrecord.h rename to src/qt/transactionrecord.h diff --git a/gui/src/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp similarity index 100% rename from gui/src/transactiontablemodel.cpp rename to src/qt/transactiontablemodel.cpp diff --git a/gui/include/transactiontablemodel.h b/src/qt/transactiontablemodel.h similarity index 100% rename from gui/include/transactiontablemodel.h rename to src/qt/transactiontablemodel.h diff --git a/core/src/rpc.cpp b/src/rpc.cpp similarity index 100% rename from core/src/rpc.cpp rename to src/rpc.cpp diff --git a/core/include/rpc.h b/src/rpc.h similarity index 100% rename from core/include/rpc.h rename to src/rpc.h diff --git a/core/src/script.cpp b/src/script.cpp similarity index 100% rename from core/src/script.cpp rename to src/script.cpp diff --git a/core/include/script.h b/src/script.h similarity index 100% rename from core/include/script.h rename to src/script.h diff --git a/core/include/serialize.h b/src/serialize.h similarity index 100% rename from core/include/serialize.h rename to src/serialize.h diff --git a/core/include/strlcpy.h b/src/strlcpy.h similarity index 100% rename from core/include/strlcpy.h rename to src/strlcpy.h diff --git a/core/include/uint256.h b/src/uint256.h similarity index 100% rename from core/include/uint256.h rename to src/uint256.h diff --git a/core/src/util.cpp b/src/util.cpp similarity index 100% rename from core/src/util.cpp rename to src/util.cpp diff --git a/core/include/util.h b/src/util.h similarity index 100% rename from core/include/util.h rename to src/util.h 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: [PATCH 090/312] add build instructions to doc --- README.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.rst b/README.rst index f24335b5..df73f069 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. + 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: [PATCH 091/312] 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 09520aca23dd88af90ea8516d0e2daa09562544f..cee616e35409b29b263889433025825060e8db77 100644 GIT binary patch delta 620 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 THAzN^00000NkvXXu0mjf;4b=l delta 927 zcmV;Q17Q5|1jq-FH-Ak4O#n?P4+J>?000SaNLh0L01FcU01FcV0GgZ_00007bV*G` z2ipY=4LB*&Ql=#U00T%#L_t(I%UzQ_Y#U_&hM)UB-<@-I96NDhJI+s&KbRzQP6k8agHkF{*LBO~Qt|edxtZK{l=ElL zeO-E(S00piPM-=$;hss1j2w$~$50eym$+CcawGL;VPhlnX*kmN?bP)9E`S7>esj{_ z>Nmccc;@I>pnomE`q~<~oXPFQG!L-h-49ga=n~DQ)GcT7Nc&4j;rYypwO7dbwAqntU4VJRhr zLJ3vVaUGj{E(^fY;yg2#V*CdKebw!o_G|iyhUesjCg-P+m1r=Vv>!!cbEOa@bs=TeRA%M z$mP~w>FSD34i4?{g+grzA#hQsT2)q8S4qvyQYlw{>*(s8I{V=l<}Sg5eDK~|p Date: Sun, 12 Jun 2011 11:22:44 +0200 Subject: [PATCH 092/312] 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 8fe18399..ddfebff3 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) { 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: [PATCH 093/312] 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 00000000..ab042014 --- /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. 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: [PATCH 094/312] 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 580bd3fc..c2729d47 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 5b4c78e7..5eaa4028 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 52c0f5b4..c2c239db 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 f7a1ce9e..51fdf11b 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 8d9b39f7..436ffbec 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 39360a33..ca6380fc 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 d1ded872..8a55856e 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 32a98e62..25e1f77f 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 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: [PATCH 095/312] prepare internationalization; rename project to bitcoin-qt --- README.rst | 7 ++++--- bitcoin.pro => bitcoin-qt.pro | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) rename bitcoin.pro => bitcoin-qt.pro (98%) diff --git a/README.rst b/README.rst index df73f069..2ffc4a92 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.pro b/bitcoin-qt.pro similarity index 98% rename from bitcoin.pro rename to bitcoin-qt.pro index dd9ea7ff..ab5ddadd 100644 --- a/bitcoin.pro +++ b/bitcoin-qt.pro @@ -105,3 +105,6 @@ FORMS += \ 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 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: [PATCH 096/312] 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 00000000..3e7b05fd --- /dev/null +++ b/src/qt/res/icons/bitcoin.svg @@ -0,0 +1,61 @@ + + + + + + + + image/svg+xml + + + + + + + + + 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: [PATCH 097/312] 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 abfb3c3a5107343044db140ad70c3eccfa87fe9e..1086fbeb6336b6474f48a5510a0582e3d790348a 100644 GIT binary patch literal 1211 zcmV;s1VsCZP)BoUbkKpjg@lVct@!o$9i@X!`8?U``aBEkT zti&Wd4~6~%7=Gf&yQ80dG*s*mYM)5b*ngiwe>+oGSBu#;S3Q`bks+1l(_WU3_b^`_EBPULuEq0@~c+*!L|`AQ2}vkFCd_=+HWpT+Tz@9IZ!umB-5i2+PRu?ZiGQx|#z%qu6KJsuFq z3TYqpy(&A*qE?z_?2v>m5tPuW8IOod%!}aGE1VgURgn;;$swz0BUOdSc0n;wVOmE6 z>>z(vYvTBn8Xr_SfRDOFxpqhmMyQn(* znS|RFP|ZUS?1)0_BQY*S2^^U$p<#hYVl$}09EHghBdS?K>_ZO3%YDZ}l(K-oFHhj+ z>etVQ$QpNQC!FK_gQf2Ygn3Fz}(EQFzqY`9(ocz{X^jWGY*(*Qd;X_ZOqo>#sypG;&N+&|Fy%5osq!o}n z>yDg>LfXO1U1(=CMy>Qz%Wq z;HRtqtYYHoH}EO0+ad~FtVWt=6CSf1R&o7?K2B;yv_l4%FRkIm!V0Fw$8}=ITW=U1 zlU2#v0p?IlK=SN=yOly+nRh)Ah(>O3YdE*6`=-i;U>$9~yW99w&DWSQ0XXY#{nXvj z>XLqcWEt0;PMjRiw_FYm-0!Y82o=~K41UF0Sk&^+tDnAkvnG$}U#PS{3G6CTVkuX;DdiGP~K}{4{|yipQtr3pT*Jge*Uij Z0{~;&23{aS+7bW&002ovPDHLkV1ng#K&=1( literal 1211 zcmV;s1VsCZP)5a821AU-MO{GT zp$WC>LJ&}6L`ZB1-~(YH3dWLKA{!7TF$lfAm)p5B^BotRLK{#QrYAX>Oujki|DE&u za=uX_!epAB)a-!$uMeU)Mw~-LaNHjYv0)itBLxM0QsDwP*^&-xRe*c3V&i8yg z&th!~V+=~Ee-uz6LVJ6A*Tst$H|KJ>HsAM2r_*>|N@X&c>wo-y{m&~`uat_#;wOM) zbGc_~YicqzkJJzZ0YNb0%0Ykvf*>G@B9>3TDHl>L+fJW8eafz0+2Ms@xZ~QjYt`d{ z$FX3PG>lX#4Ta$XC%-<`Zbk9rQ%lHZAHef1;=q`R81>o~n7f!IXA3U_r>qmFfRcz* zYpsD%5v@ndwl2llGvCv(q62F*UmQKc$`u`0W5C?g5Kw?pXss*vFc1-)!kvybW1tALm8cZxJkO-&$zIEa;CwPOAH z4J=){oIr?4*G3*+w20!ZVFm_%#sN<(Tu3YmlRzDd?_K;Z51qf8aw230gM)*(Fs7nnDy0!x z+aBf6{*O8H?Kfy`cxTT}Hgs?1hjZtclFMQ}i)Rg>X-O)md3`wtUp>hD`SW*95O4|_ z8X9oo5T`%{Z8cq+x>&P%9fjaF{a+qMfCUR4LjdKHQu0_HTfaI$uQs;OyKz~GiG zTW(AcFa~DV&&D}N>>MHval)#O4z_K3g|HIS^VXY0QABG?8^$P%(rBfSB>oOgttT=N z5e$${`xEM{gb~O4``NzzHBu?di4(_Jwrn{m`|pEzYyesp#iF%NDWxcv%gO54LxVVG z&YH#Ey?cn`n00Gc6NND@j&VrR_`5S~>+X4kVVN)t4S@1}-{x|;eK&60n3v6F-Lz@b zm^yVT)z#HxtFp>Z`;xzyS66?#`j^s_5cP0VQ)8;WpYc*Dvk1$vA*x6#M)F;x?Qwk z7=~dOjl?c0Rf4b_1VIU)TrQW3#Zq~2FdrU1bSQ%4J^&s}czB6t0Ds(jDruZ%^garK z2$70FMZpLVD5WYulz0pj#z@6utdqqc5j06sod~D|kpl#!oEnXd$0Z%_cPf}PoAkem ZzX3Nh0V!cC;+p^f002ovPDHLkV1mI9FyH_H diff --git a/src/qt/res/icons/send.png b/src/qt/res/icons/send.png index 0ba5359d7bf6b31cdb78a9d3eebe5a582e1800da..a1c56f52e0add34db69bdf2def576062a79c7cfc 100644 GIT binary patch literal 1704 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 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: [PATCH 098/312] 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 cee616e35409b29b263889433025825060e8db77..55620f3e03d516fa4e8a401b9f76c1eb4c5e9339 100644 GIT binary patch delta 982 zcmV;{11bE41^)+-H-G;C{{a7>y{D4^000SaNLh0L00FxI00FxJI_%@(00007bV*G` z2ipb=5feJgyj?Q@00VzXL_t(I%WacwOj~sr#n1En@4bDY6xKq)1q8$j4w+)I>Ci2a zi5laG8AGPYGT;56nNg=^F(wm-u#xzZ&27QA8Ka5LEM{3`!+&fUPMIGBH$b3aU|V2N zY)g3?z3sjC?+3G~iRaV#aDK^2P7dM!IF=r3I6QR%z>=!MN~iw` z(p4j6Er@<_jkOdX%+FG`3Be#{2xb_jg|uN|=yLMtnmyqcZ!Od!7spc;0Px>%Bi^a+ z_0JzDom%P9wtp#_9a3>1@Fyqso}4?8NZHcC1jknwyBp&BZ~7z4!FLyV75;aF%=rVQ z5mi;~ifW@ze@gV^W!vH-)2YygRV&aJTFfc%D9M`gjIEZLmJ3&wulQddxoaUQ`xF2O z?k)LPQ-uRU1B49~McLYGUxk{w1BrehNC13i-mH2~Dt~qmG29u*IQBx}yBpiW4|7$q zEjQ0C9+9kpga(ULRF~&gmgG6t0GNv$dGJ(M^ZLhY-u`Xh#ytb|7HLGRq1x}R$+Y7( zey_pz05gMFK(Yb|4NIoLmp@+aeRXqLMUFRn?S|C_yLN3Uf9&T#;&^mPj!IUbs4Cvr zG0A`8B zq=93lq9ss`UpFZ_G6Mq0H!32y%^ckhCGv2wr%n3*nXMdla ztZf;562OJp+jBi*lkowkT`e69&jtY40W3<(fV507EF3x=>m~rA|E;19|H{mJRhxsN z%OZAWaB=taoZ0!(ks%#G8h`;HHvE3|Cna979Y#`i2jandT1W3!>Z;u4v!6$HcD!us z1%C<7aoW8{&re-<=$VSI`@-`dG&TC`6h@^@k@+rFx-3JNsZ@#@KMb}qVO#{Jb=VY& zjzsi}_ub;$N&wdR-Q_+vmmdE$cInY|o~Inb^r&QLFjLY<>$GM6rPhH-{WD8q{@R%` z?WAUCw6^1wqV~!{*D4DF!~&)T$qZ7+!cX~;h4Jm}p?zb~^f#7;ng6^I09-brMO|hW ztPs`B_4##PmnbkLcxu*&wtYWyZZv9i2|~z}NM2?B0ztQLFKKiVX8-^I07*qoM6N<$ Ef=a8`I{*Lx delta 601 zcmV-f0;c`{2ZaTYH-Gm4rT|2*6O(BG000SaNLh0L007+p007+qa8sfO00007bV*G` z2ipb=2?{WxT<`q=00IR`L_t(I%T1C?h>u|u#()2BMkY&{p{6ONMolESWMg4LktAj> zva^_wBH6elVv>c8lEelnmkl9V5Gj$|5|RZn%ECX)_xvorcYiK#y}fnb^PF>@=RB?U z#g%|GC!pQv5V(${3(|#-KyIKH&{vQK6R9SFJCPIUM|3Y{J~$Ko1zCXZg*?X`#!N+l zP&Hs%$QjIba2h5Uu)Ye}g+74{p`DOD=w+-sngR;u2-celDBvt~A-Er%iXJD{SVkvn z(P0d&_JtL_n19dB6jG8N64ydKg0?V%bHVfIk|rlL+FxNdAQRx`T?A=2U_Swu(4Uy) znA=6{4$M%6ZfpYOHlL3$ZFB(A2N^_Hqkq5$#L~!tPOyKxGh-CETUPZ6v#Mm@$8>>f z#`8>fh50_wd2GMASE%d*mttOm|G*9CGN>sHdMfbSM1Ms`F+U)W&?)FP%t7=S*3(EX zV*MB!6QOS_%E}a9uK|3H{z*{5-8ugPg z#}&{TtZ$Z>XkP`s2TvCbdNG?z^&99s$QJYz<|)bbqCV&`^h;;qza8xdS2Z?v5M2!U zh~9zplv1K)Q=X&cZVVOHKt>_!8+!_R6w{3v!SqZr*>B7+)LSv4R1`XB8?yskf;kC( n08`@cAcrus(H-b0X(sv)Tn6%rK7gUC00000NkvXXu0mjf0CxoT diff --git a/src/qt/res/icons/bitcoin.svg b/src/qt/res/icons/bitcoin.svg index 3e7b05fd..96f10178 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 e33d0f4bd951644233c240ae6e0214e559cf660c..84c18e29fad89bfae9f44239e6ef9f9fee4ea0ea 100644 GIT binary patch delta 965 zcmV;$13LWi1d9leHwpg${{a7>y{D6rKp}q-6(tbv_doyu1B*#SK~y-)ZBu_tlyw|_ zKHu+q@4e?8aL0jgaS(9+c+pIzrv0!VvlO?Av60CvX4zUb6U%DTGUr;Qxz-j<+feM+ zTxI3VIY>rjgVYMlFxrdjI1Z2l?l|$_faCq~?GMsw>+{$1$MZaUw$Jkr{^wLxbu@o$ z!h@0{eU4=sl)O69UHPfwB!FZvs9C!H50|b1Ay%WLq|ep7?S=zhC$DB)0ug~Tf@ulp zhKX1#_C?wI$*pVr14FgV2@3$&jCeg7w|35L+gT9FPP0{Wn;qaX!lN|Wc(&_gLYMW( z7{z@)-@0?J6oy<1Tlx6PGtTQ~UiL)5+-$2>`x*A@7illLL$ck^;A{SU%M7$7`p$9T$O1 z1Yq`OoBdCLNgxSu%_8TXs=4pnUvE~hnlg8`*U29i+zvGP2bQMbey;{DS}`UIOlluwt@#FKFd=w^A83hf*>Zk1}6IfgkuRK z_1({x4lrh9zWGg}MVqf?x?O)tfUF5vU?|GA7c&4h5W~MrXe1JD2N2&-anDPhoP4A9 ztByASTo`yH>#)RTcmQAtNX$$|dkNz2QV2{;TWIbWJPjb}aoTDY7iQFayt}F$!0c7K zS~nBTREH7J6Pq5EKKe1*w8LKrFbi6+|MI%J=qA8+q5NhevwwK>cg24$2l^t>E&vLE zabcMdLV&}<(X;V0q^c@!+LGCrlkL4vk`=l##YaM+(C+A%*|hE0cnW|HzyL7Pu)Vb2 z?_nE-sf%z=sHePcyiA(s7%<;EG4$fA)ho_}81pEy_u$1r-;))0e*W}O+q>)6tSILs z7TLMTb=ZVUn7Yt)opyg6(e|ns@8eyADX6(}Y&0JI?WW?KDuO^D-=}(AJn&0r@1J)s z_CKL8JyT*rhN%m~(CO_Xt$X_iM?W~OA#r_74=s~z3bEy{Z(LI6&&^t3fq@u@B_J4q zI0*!MBYkgu*|{(3O&tCDIEMeTMgXWRwNvL*k^5F}@9odtn_DrS;oyEtfW^j**oUWw nTUz4zKP(v2!pAOS>YC;+EEsJv8}=njjL!5cmk=B={KP01+8oFGEbDq|V~KL+E-@FV6l(2T5*aCjn2OP4G$& z;ob_J>NaK9;UKtG90gnG26z=>5yF4cho=>Kv7mP^;&a*1DB{;KMqvkZDIZhNOMMWh zx&+kmw93m$Mvi^p8v?)^g({(q3Zt)%1nPKbVL-%7jC1HhF&T##F2#U5snfc|KL4riZOt}s3p_XIK8Q@kJi3bBo}yG?ex2m!H_I`8fbfPa4q(k40& zK0$x>#9DuON6@trA>iQ(W0Q!v9A6AT+{X9|wn=de9EW&@F@v!NmI#@Hw4OQ(V~_;> z1fDPW6KE47f&b9{-Kg_e21WoXF!x9=pwp$>&60>1{TZ417_Y&RGEoGeZqR*j5aR{L q3PvXr#CM2SkQ$K2^8Wl2{Rb4PWT$U6Nk)kP0000 Date: Mon, 13 Jun 2011 09:05:48 +0200 Subject: [PATCH 099/312] 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 c08476ce..3a24c76a 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 ddfebff3..64d99a5b 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 5974c0e7..804f004e 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(); 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: [PATCH 100/312] 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 90950a64..c716cd3e 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 bf7c2a65..25b8839c 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 3a24c76a..05ac66fe 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 2b3c69fb..2fc6ca78 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 721ab141..1c11944e 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(); 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: [PATCH 101/312] 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 e58ccc22..3243164c 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 e0032674..a71c3482 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 4d8a55e9..a9e55d5a 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 530bef4a..ca88bec9 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 61991092..fd4a9e45 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 25e1f77f..4759e8c1 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); 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: [PATCH 102/312] 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 ab5ddadd..c7b7ff95 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 00000000..56f47654 --- /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 00000000..2fa8de05 --- /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 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: [PATCH 103/312] 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 c7b7ff95..f1ce9b12 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 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: [PATCH 104/312] 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 2ffc4a92..9d7ad10f 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 f1ce9b12..f3299554 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 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: [PATCH 105/312] 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 05ac66fe..23640fe6 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 64d99a5b..47cab1b2 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(); 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: [PATCH 106/312] 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 80904b34..8cf6c325 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 23640fe6..96afa41a 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 96452ef1..e1b3ef17 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 0000000000000000000000000000000000000000..66f3ae4f86a864755c59d0622cfb1bec43b018d2 GIT binary patch literal 702 zcmV;v0zv(WP)9K!-r$RAa3cUz=h$kx*p%C;S)QeO+MyOGc6l=R-OQ@Re53~ES`+5kQCRIByFqiqf zna@0ewkVZK_lw2iTV2!L? zP6z=21VI4b_u)8B6QHW9>@6jIAmGw5EldilF1}iS65Lkm(kVL z)dUD3Mt630vX0|iI&%235CVgPgNR0>sMqUoT^B-#rUToyVOiFd;zF43`{10zbzOL# z_gA82S^qU30zA({KA%6RR4S&TC~zF-pksniTD!bZTCCAPVrdDLg@yCr^wd0+`hO{{Pp!2Due)I42&@-iUL`dJDMq_pa86L8DDZa3`|Ub zA06TQ+8TZbL4*_vg?ZO?M^#k?V=TP?O~%-DLdY>8@6;0cybb- zb2;&KZmviG;QjJ)^l3Jm;G8FH+wO5)_jPx7_hZ8_^2ua!(cItXrKzd;YyJIqzin-S z4GoF2TFv;W>(35w7&@TB#=yqVr2_~C7G9tW z8!WsBQ+WUeCY~Yb5~x&?CY39Kt426~pmN*DwYm2m1~MRJC_VEzAN`KbQCzb`!x zh@z-6i>L>KK~-xV02%Q0@bJ(A>fe5Ee|!6B+Gso%ty$gL`t)uxnE(bbX_|(;UT+G- zfK#v6L#31gJiwGnCC7`K%_lcDH(wUhDc_n+JzZb_I0K5&Xyld4i#^JhCtp66k;S~Wlp*f0!3W6Z*AB^v}m#k;6fOfAbW zN|7%v+M*~bMNwpqsR7n?UB_Bm1{MM1`+iBi+1R-IeSiNU!0r3@Tki*h yDewRgrBrJ?9t&^}Kq>WQd3o6Z9s!P0>hmAqc@oq2xll4QHqyuv1=lpU`&ZD@XMC2Bb z0Su6EE($m^zXMK0J`BTVv)QzPKG64l-#HiHd0u=Lxm&B%EZ_GN zzyOHq^?Ee1Lht>;i%z%m2!X5Di=VzU8jZQRxg+3oe}CW6TBm?9P^(s}mnIUlyT`AJ zH)mIh#TzTFAMIy5J39&B1Q;|LjdZ8e5ugg_>FMe01mHM`L65+UI2w%cJkJJ(KvXW5 zM`0KOSb!xWlPe4Y0}_LY8H5nWaS8~5?Y+G{1E_!RoyzL!)BeWBbBSZFEH8ho{9Xsq zAckQCySuw1zyqv8q2MZ|6yN|lkw{pMS1LWZwzTwe6a*ZUN^x)xL=2cQ!D&P~opym7 zU~;)!+8C4M?>lSPbzSGz;USmT)-X!ZOC||!k0A~LA&4NG&1QiJFs|#mTI;E^N+y*` zWt?FqqjO=1R*EQ}H-oSzBbfr=bUYNxveF`w1B`9kmN6y`vaDU$3T>l)mfja<7sjo9LGZyd=u#{4pe*n6I6nJ9G$GZRk002ov JPDHLkV1jPI6o&u+ literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/connect3_16.png b/src/qt/res/icons/connect3_16.png new file mode 100644 index 0000000000000000000000000000000000000000..a211700785d4f811bd535432234fcb76acdf2f46 GIT binary patch literal 625 zcmV-%0*?KOP)2L+d?h)FCT*>tVx`haXp0VY(7~xtJ1Dq`;M_mp z;tz217jzMX;_RTfH-lITF(rf0q&34iNli&|PR{51z7BQ}GK4sxUAaqya4C{zVG|PnGWXvu3^! zMKPsx05T$oGCa?-ff10kTCHRXaJIMhve)lDLBow(x4(}2ZRWEP$b=x0NGXdz1{^h; z&ABOn-#>r7w6wg5%II``*HIbd5vZ6j(e!#f4Kx8&tJNw~8JveX$P`q{V3aY5CCC`1 z6yqQwiXs35Ftpajl!H;IKvZQHO;>h)(gH#c9UVaRd4o`(-WYXvGJoFt@< zg)j}!noJ2?*LC?HCQ1lV0;+&?9LE-K*49=!jmATO z<@NO+$Hk+{&);%Y02?dj!JYZsuK!|H15ela{_fi~A9w^9LWqOE9sC=h=ivQ^00000 LNkvXXu0mjfwnP^- literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/connect4_16.png b/src/qt/res/icons/connect4_16.png new file mode 100644 index 0000000000000000000000000000000000000000..e2fe97d49646f10ac7815fc2eb4ff020adc82644 GIT binary patch literal 611 zcmV-p0-XJcP)&26vgrXGc!B8z$ysQM2!#0?W+K%=2=u1#}dI)7|3ya&rt`{DYz>>9ZXO z1zF)JtOa*W5M}qIxCk69Prux6@6Vxd`|iCjr|nIKT^B+{Rs_6($|?KpeX^pu2<)|w z-i(fp&m)elKYRVDIPe8XNnR;g$QkOM(qDrpjxKI+lzRvk#M0?HdS!r6A|jk-1!WLo z1IB<8E&|j3mzrlqfm#7$7aOHVRi7i&gPgkJDs-c3(WY^|+50vMl}`M%LuZ~puprVQ|K xBK|Qp>}#!GOeyeWhSgn9-})9)s_=M{)&JcgLd%V)K7arK002ovPDHLkV1im!2{`}& literal 0 HcmV?d00001 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: [PATCH 107/312] 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 47cab1b2..d46704a9 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); } 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: [PATCH 108/312] 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 8cf6c325..3fa123b3 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 0000000000000000000000000000000000000000..6d698b350f84df619837b7eee8775d187d605844 GIT binary patch literal 690 zcmV;j0!{siP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipb? z76b=55lgxN0013nR9JLFZ*6U5Zgc_CX>@2HM@dakWG-a~0006B zNkl4&6;bVJf3 zNpZPcHrI8{aU7eQo3q8mMN1}=2H4oxnC%OfO(#0g+jr0c6Qq0Q%R*>Bec;EI9pp= z0Aw;5rl+Ur>+6ewcfc#4d{u&zjg1Wpg1|h_vu3jyOL{4(znkAH!6EQ8olf6hUtdqw z>-F=2fq~b+lQ0awc9Wfdq?iPr0Yev@Z@>%SF>n%wq4j#CPCPh1K8}``mz|lJ8EUnf z59EP6!1b#}0LUI49kH^qLaWsRU}|d01qOj@!0A;9J_bR+=;$ax5HK<_((a#M{KX}H Y1CMG5vJObYvj6}907*qoM6N<$g4EL=(*OVf literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/transaction1.png b/src/qt/res/icons/transaction1.png new file mode 100644 index 0000000000000000000000000000000000000000..f19450aa9fd8b77b8823793e5afd22fcbdadefb3 GIT binary patch literal 969 zcmV;)12+7LP)Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipb? z6*34S^mzvW0013nR9JLFZ*6U5Zgc_CX>@2HM@dakWG-a~0009Z zNklAEoO|y~GEIikm}({@wVFi=HBvt!VG!I&Mk?Y)g_PpnrJ(IX z#f5^RD;II)CW2IOrIVKGN>a25rgdX%ut}QCosch+G`aVlbKVaZ)ncFhAD+$6;NPig zqCo$xR$jk4zcW66dw+B}%?B-nk5`kmCr?&q$Dey>`t-@}8Dr2g#smN`_hZHIau|*OC-Z3-) z1`5L_Vo7S3F0YQ;n@e{`B~S$}0OX;8pc3Gn0szXfMYStIVk1OQD0v__fT%L_+s0^| zZn>J0lpq2jfl)#P1?hzjE4)g)cn%LgLW4l4|0U~@Y|C zJTh~3thebkYCBCNXpDdeK`mGTX27$7*949d6q1PNsD1v`q2{ws^vqae(0cpz$EHS4 z9cu=O(p<}wO9_Q0a2mr&0;eSuTt<#EIf>I3#`>C{UwmrH7_{zV621R$Kdw}yl==%sUJ%Z3M00000NkvXXu0mjf-wLWK literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/transaction2.png b/src/qt/res/icons/transaction2.png new file mode 100644 index 0000000000000000000000000000000000000000..01bb558a100e37e6985d700585a60bb38669ffc1 GIT binary patch literal 413 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgg)Ha<~v<98h?X+R;_%#er@=ltB< z)VvZPmw~~#C^fMpHASI3vm`^o-P1Q9MK6^dDE`IM#WBR<^xn(1UM_|rZ4ce&Itg&U zW$|X_3g1x7+NE8 z`3rwieebz%j_^P0Q+bB*^Z9uh{1W9)&*)aXd#FHdPebvVkTYTRYo{`vU)0>`Rdqai zTI;6ho<~fz_x&S!6-$fqH^eqqM|3I1rrRdn)Kx3&|LAqANAa!M_pMSFl-vs1zDm_J z?|sjg6%}!qO-`!Hy6wxv$q8$umS;!tv)f1Jemf{CCwstatus.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); } 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: [PATCH 109/312] 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 00000000..b33251bc --- /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 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: [PATCH 110/312] 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 96afa41a..b45a4f9f 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); 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: [PATCH 111/312] 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 b33251bc..eced2014 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 3fa123b3..63bb26d8 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 b45a4f9f..4e9a0213 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 0000000000000000000000000000000000000000..448e47f947d3da4e023f69279e7f8a830ed5ca75 GIT binary patch literal 946 zcmV;j15NyiP)Ip-$*fn9H5H_r6~) zqzOIA*`4R?9$~HJXy)hVy-X%^Hjd*nj@HvsO4>@T#BsbDhT*TJQYkvR7cUQl5TaZz zpYBrX%Im!YZ>4+tN#}B$cv_`dt+KWK)cz12{86*^Lb+UiU=KlItwjhSipAoEH+!-R zg|2=ts~r+uX@WRrKr1?f7CVg>^!@#a?}O)ZG2wo>u(0s8wbpt7<#PG-gqQecz|DDb zXOA7FDfIWV@n{`fhfyyo56wa!wll{&`My)G>&|#esVUi$!`Y9?F%OnG=95HG;|ETxpBsErJPo-M`#ox=@A=!SjF=fyOh@+@_FSh-`>1ItTp?= z0Y6^5N+1;-N= zVjR%M;72X|R=~vM8^n&~_RrU!g<-hpluD&&tKI%Y{<)@y$H(xp-Q4+om9?!+B2VyF ztx8*19{*e8r&~7&UC?*$n1{aqX{A!}9e`r7_~2%}{)M`~s+F+xo){)GG(aE>*DJTF zw|tIGoZ_Q%A7fXR&3EV!udS@CH0=@SKhXx{ U3^ThEGXMYp07*qoM6N<$g34vUH2?qr literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/clock2.png b/src/qt/res/icons/clock2.png new file mode 100644 index 0000000000000000000000000000000000000000..c1a6e99f7f28d66f4dd6901f4c09a2e4325ac3b7 GIT binary patch literal 944 zcmV;h15f;kP)Z$OUJg1324BT5lxs2GsJ|8 zhaDFZHJA{RCA%9hCR{x1qSx9(JrNEb)RP`GM$`nzB9zh05p{96Wei$2V6hhqM zk=e+S@HOq`BiczvkjOFI8s^{CRmgh07D%?AwHfjSAEa$U2hA^@pFh6{Ec!wSIg&|VJ)Fvo$}mRyKGK(1 zYuH#{C#Rc?B)W{aCPH=i<)P+mU&~vH5c^=#VpAW`bwqzVA;*FySD+hH4*&gw}dV?(FQ8mx=#*eLQy}_B?blfs#H>JFK-> zPhhN|6-Tr>A^Eo^u)^@;t!uR?YW(aKi^X`e5q=nL-7};4F=RHw&AZF2Y~Cl9l6%!E zp%AQXZSm8M>qNd!yiu_$kJ}f@<#NjdC=?3I*H*Vb()X7wo@3zX5rRV_v<3WDzQuN@ z&EXSy&b@yQHWux7ceg%WSXj8>oO3UoX?Aw@^k^Wzo_KZSxJ_r+^#Y7Fq@y~nvBk|l z*J>+Io?mz&+n3(B$IQ%3Z#tcx)>==dggs-7_Uj#0)><#^?(SZ#R4PrkPx=ps^~c_I S7%Wc!0000+gKKBb;;WWpZ*-`M!TtYdxf-J08ceYs99kwVscn=*ra8RJ?aDb`OLQ zqEspkv}Mz;p6bmXY0c$n&KBqk)(LlRvAVh8zWY7;dwt8DDwRrC-2+kW%Ee;w)L?Js z>;YNG_}Y^3EcMz3^~B=KJe{QC?dO-D{`Q~xm3U&}Eg{4w&N-(5N~O}k;A737b!w%O z`WAPLVSh&lH~+m3Jst-;7609-)8ScOIh15f-1w+AHZ}{)dO`?wpwRq7UrW&A=@_Lv zl;>fsWo3DZ_H0O3py_H25w5|{Wy6Vqs+_-_IZ^ub{5cr}!SEBk!B7&0+*)5JF$N1H z#t>FQ;@UkhRj_r^q(MG+pLP*5`pnLt=XoQ_7&9zwhE!_&d^^U0uqjwez0pAH7*YW) zLCJ_`4@Xp^n8SsXG;JA@N}A(QqKJ$|WSY>aMH`DwEn7QxDP34#;=MUO{9*xI#NArN zJD)db^XfD!Gpww0P1;IU*ER@Rvh2?XU>#V;3nxc-uInO6qDhSg5z*cj^TBHdztv!j z@l>^@FR4u9rJl$85k{kp0VlwLl^!aS(v&e^QxKY)w<8dmdrnflZ|4*+m*@({E3mS- zL}6bWel816;E-4hQp6w=Kr_1-VO>O_Yahm#d9}U0J@eNMbG^U6Fc1rg@tV+5;he!b zi^_uApK9Xs^1<^|Dj^GN zlCLiXtBafN^aI&GRO2qAqoXakTyDe|GnCEPV_L^Dtf#ZanE6Vj@>9874&5Hk07*qoM6N<$g2J7>Bme*a literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/clock4.png b/src/qt/res/icons/clock4.png new file mode 100644 index 0000000000000000000000000000000000000000..ba036f47d33e7ac9a3db80ccdec293e0ee3be034 GIT binary patch literal 962 zcmV;z13mnSP)D(iwsmO+s?$ftr*sTFZ0xRGYjMF_~`(Ig8SE6t;MA*OkKU%t$p zd;jof4%Nvl9&cX7ZnT1xwRQgeruuVzjr)QinB|9{e*g~;5BIgV zC%?}p>ivvok&p)Q-Wt?~L$)dlEv*>%XtC?JTk&|M^5K9G;%jDR9{_?NXlrfrzdIp) z-^3f(2no5|aooH6H=LDdI_YEMpBVKyhYN3*gr@SBdwcIq0GNW!xGey!=Y!&E84876d!CPy4B=XYKwDR#w+w zh~OMx3?U3dsLBo?BCv>IT^X5U`^eT*aQ>aG4k_h1pNM*FrB+B0B3*k7YxP*xpt6^lBN^sTj#5_4w9W--5BUypGJ#D&*=ifMZBWxKe0FeLe|eG{|YNr;yE6 z@W~LNCQBe9>Bq4xS;6l%Hnl)F4Q&av=@4z5Ge{=)01*5X0kB}9v9PG%JdGXl5Jhg% z2QUe_2I2-REzINTXLiu_!aM*1v-#hJ0fYeyjj7uTRw!g2s|68F`WX@QEBwQ}eKExG zCc=#ygwaugOAy8<6s$D3J*)89&>mdE;QFCbzd zbPctC2whzTtgf!$S@n0e6eAe4u>1p||IOHY@N4kabokYEFPqJlil=I)dG}pxY;Hhn zf^~pageZ#8l)KP~q0xl2Ed;rJWO5QOkM0clzJHDrQ7O~C&v-UQp#*qA1Ljc?b}xly zvjw3f6k{;ZFkGPLRe>m$C>%F1COOQVuU|{%6u4Ld?3ZxaJQP2?J zMF9!0Goa_Jxm|@8{Nq5VyT@xhxxx+}IztI&}Y8LdP+}>~+HL zKN2J(-1|j9I*t2Pfty$NprBEi$(s5@cBxXS#LPJd#u%HJnE0UgjooWQ7wUX#MIzND zk<1gO#~V2Rxrf~I22vO-%w){+jb}cYot^v4IcJ|b)A;!KnPivxa^Rhvq3l46bniYa z6n54b9^dnD_v`HX_5*imZf@>p=bU}&jR%a5jwTa{#5p1wW`QmdX}=ZQMIu^iG#cMm etJTOIlKurBK+b`Ue8F7+00005lf7%2Q5431 z*P7R~<@F=+B~k_HmQtz^5TUr(Ng=Dy;oXWN6~RsHR$TjM6o14)aFBxFVCfog$RHH8 zEulkO92{)@%;^v<#=bG+z}cSXckg-bxk^N^Dy5VLJOK6p0e%C2WXqto0ZJ(a7_nIF zWhfMS<@I`xCX>mm-|v5{)oO2m?;>)!3*gD;^Pf_w)Kh@bXe1twM*%dO&0pnm`D6vT zGK$Ex1`dnG;(zCKI(-%h1QNh;G#Y(dsZ=i8?Y1P7$#=m1KfMLO1{?xz;2xlWN7-!l zqSb1NX__rSU+eAL;jQKyZj59D4uZj;F`v&F389bvUY-6pm&;u=8V$*0GJW9a zn&uXOVVdT7yq3~1FwBvQ~w*r8@NF?&QTCGZ{RDT+4n)VE^ZR>27TXa?XKA-PY zMCf!n?b&ShW%K>lwF{se4u`&2ELbj=KY;(tV6jLW>D$BMa6;Gh#Bex#-|cqKL}YQ7 z8`#}$_jAAB|HA2Xp4_C}1-Ne*#%Vkrf8uhv5`cDBzWFU8GbZ8()Nlj2>E@cM*00Id~L_t(I%Y~A?OIraH#eaYA@meZ*3NKTJJd6+Q zKcHYn!9|x6*MfEE5^#`Gp*qH6@ z?ZuLsy$s$;vTQbMfX&a(nsgwa$C=^W6=eEx6@c7kgwQP5Hmqa2#KA#8p z34Dp7h+?q_K&4UvxX~7Q9R`&k2!NH zzzg6pa1w^0^?IaEJUBi+j+U2~otc>#YPFgV_ z27zn9=~W3n20_5+=qN!DFfuaI?w?=$#U*|Nj}vMLvJObYvj6}907*qoM6N<$g7KCg A3;+NC diff --git a/src/qt/res/icons/transaction1.png b/src/qt/res/icons/transaction1.png deleted file mode 100644 index f19450aa9fd8b77b8823793e5afd22fcbdadefb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 969 zcmV;)12+7LP)Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipb? z6*34S^mzvW0013nR9JLFZ*6U5Zgc_CX>@2HM@dakWG-a~0009Z zNklAEoO|y~GEIikm}({@wVFi=HBvt!VG!I&Mk?Y)g_PpnrJ(IX z#f5^RD;II)CW2IOrIVKGN>a25rgdX%ut}QCosch+G`aVlbKVaZ)ncFhAD+$6;NPig zqCo$xR$jk4zcW66dw+B}%?B-nk5`kmCr?&q$Dey>`t-@}8Dr2g#smN`_hZHIau|*OC-Z3-) z1`5L_Vo7S3F0YQ;n@e{`B~S$}0OX;8pc3Gn0szXfMYStIVk1OQD0v__fT%L_+s0^| zZn>J0lpq2jfl)#P1?hzjE4)g)cn%LgLW4l4|0U~@Y|C zJTh~3thebkYCBCNXpDdeK`mGTX27$7*949d6q1PNsD1v`q2{ws^vqae(0cpz$EHS4 z9cu=O(p<}wO9_Q0a2mr&0;eSuTt<#EIf>I3#`>C{UwmrH7_{zV621R$Kdw}yl==%sUJ%Z3M00000NkvXXu0mjf-wLWK diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index a84b45ae..eeb948b6 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); } 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: [PATCH 112/312] 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 00000000..793dc7f9 --- /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 00000000..6a78adf7 --- /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 00000000..09ccc254 --- /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 00000000..7d9dc37a --- /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 00000000..9fd58d9d --- /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 00000000..e31f0e79 --- /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 00000000..c03c159a --- /dev/null +++ b/src/qt/res/icons/questionmark.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + ? + ? + + + ? + + 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: [PATCH 113/312] 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 63bb26d8..ec64770c 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 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: [PATCH 114/312] 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 eced2014..820183ee 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. 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: [PATCH 115/312] 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 eeb948b6..d2cdd4a6 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); } 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: [PATCH 116/312] 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 c716cd3e..9126a645 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 91b87fb7..2d69df3d 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 a71c3482..cb24c190 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 761a2669..92d7daeb 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 4e9a0213..4d3efe26 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 97391e09..10cafaf1 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 e70aa7eb..7bf4fa6c 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 d46b48fb..1bf123b2 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 1528fdf6..3788f9fd 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 1c11944e..01072c42 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 a9e55d5a..9120f19c 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 6c1f3a5e..34f30d53 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 d2cdd4a6..bed08d78 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: 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: [PATCH 117/312] 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 4d3efe26..028882f4 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 10cafaf1..822c03d9 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 09d1fc92..f5f12fcf 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); 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: [PATCH 118/312] 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 cb24c190..917355e7 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); 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: [PATCH 119/312] 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 820183ee..623a5027 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 ec64770c..3d4a3e16 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 028882f4..f487da70 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 0000000000000000000000000000000000000000..95bd319ce13d756a20341b7521bd554cffbe54b9 GIT binary patch literal 1055 zcmV+)1mOFLP))u014Kkn1^4&({_*ki{s#vI#oXNG@BRJ!JR2Jo2NM$z00II4 zG}hMN*7*4P)cN`R0Q>v<0P^zn02db$03#zD00M{!XvlJ)nWmpVe`UCO^$x?+r!OX* zIdkpghYz2@Hpt0IG4Swkdj9zFgZbC5Uku;A|72ieV`Gq#5(CQv1Q3&qjCioDtoX`b zzkcif{ri`JgM+==+S<^DmzNvpt>0ipKyS#t|MZjl>(}oLpFVzJ;N|6H(9ux>%K-!s z3mY5rH&<6%MPQ(Vf4!IgG^xblzj4+9&+XLuk21Q4^BnC74V{~3HWG~^jXLgw-0|Nj64_V)My>gw$P_xJb!1_=-V1MJ2C8Rd}x8TjQ701*dC^Y#Yx^6BZv@#xt9 z0R##E0*Em*w3Ok?moGpKKfO6QIqZ3Pxir3h{rN&jOwusSLBK@j)g%U~-}e|qfYyEE zWMz27$ouE+ZRX;yUw=&c{OvCTKmf6T{K3e`#K6kRvK1K0TR(mJ#`%wt>ATp!z2^Tf z-rT07^-+kIoeS(=?*9x7%1l34{{H9eyK8D>l|NF+q!{#cX#>&jV&(6RAq?rHyXAtH3#q^Jf+2i?Z#+LvA#KM3B z{AXZf`1O}*%8@e+Y`^|9^q9J_GBUjT!NBm~3j^by{|vgqzZoA3ayJ455KaTYQN+x^ z08Cqb`;IXD`T6}vpN;BYW(E#m;(h;{feDz%xS9S60R#|U17L;#y*6pr5e9~z&;Iv0 zTd^=Ru>WUx|B`{>$|L4W00BhM0C0K-TKbO>Xvi&2hI=o6)Cuu0u-|*ne*NtarW}9( Z0|4(~c&K0)VI}|o002ovPDHLkV1f~|8dU%Q literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/receive.png b/src/qt/res/icons/receive.png new file mode 100644 index 0000000000000000000000000000000000000000..ea9eac679ffabdb076b3b0b23fa0874f12bb7472 GIT binary patch literal 716 zcmV;-0yF)IP)-P3UfP$&cyQF_~#E!_=uE^Q0($dn{*x29b&*9lq@ z@bK~R@$&NW_xJbs`1t$#`~Uy{9Y1+z0000nbW%=J01PEWO>cC3fTyXdtE;uq*z)rA z_xSnw`T6?#`uh6%`uqF*{QUj>{r&#_{{H^{{!on5ssI20#z{m$R2Ugu!OKpT>q@4zgfAvmap28-6obTjv+)}&a*<}&mrb1cwlcq1-1MiC zcZz*CC_m9LO;T~m2H%rZpS@0000 Date: Sat, 18 Jun 2011 18:46:01 +0200 Subject: [PATCH 120/312] 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 c2c239db..b67e2a64 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 33aeef33..f682f746 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 51fdf11b..013fb6bd 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 a76374d1..cde934e8 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 0456041e..108842f3 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 436ffbec..7aa6d41c 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 ca6380fc..8b439efd 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 8a55856e..cafb175d 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 ca88bec9..bc9b2245 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 fd4a9e45..2c1efc40 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 4759e8c1..a1f16949 100644 --- a/src/util.h +++ b/src/util.h @@ -15,7 +15,6 @@ #include #include -#include #include #include #include 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: [PATCH 121/312] 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 108842f3..5a8cc24c 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 7aa6d41c..73935bce 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 f487da70..2dfcd40d 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 e1b3ef17..b3559c3b 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 822c03d9..86fc8b32 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 f5f12fcf..71419374 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); 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: [PATCH 122/312] 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 5a8cc24c..61426a3e 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; From 3c7ebaedcd871d03033651b1ba3f4f2333e7d69b Mon Sep 17 00:00:00 2001 From: mark Date: Mon, 20 Jun 2011 07:30:54 -0400 Subject: [PATCH 123/312] 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 f3299554..27703592 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 b67e2a64..a7fb4bd6 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 013fb6bd..f3061853 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 61426a3e..2dbbd674 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 2c1efc40..688605ef 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -5,6 +5,7 @@ #include "strlcpy.h" #include #include +#include #include #include #include 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: [PATCH 124/312] 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 34f30d53..2f00fa87 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 c082fffe..a7f6537b 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 bed08d78..e25ee1d1 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); 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: [PATCH 125/312] 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 27703592..501695bd 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 00000000..e475441f --- /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 00000000..2171578e --- /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 595b7f40..3aaec1b5 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 1bf123b2..1b5b2fef 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 01072c42..5f9ee18a 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()) 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: [PATCH 126/312] 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 e475441f..19fa2301 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 2171578e..67304c8b 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 3aaec1b5..b88839a7 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -91,11 +91,7 @@ - - - Qt::TabFocus - - + 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: [PATCH 127/312] 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 2fc6ca78..f9b95c40 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 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: [PATCH 128/312] 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 9126a645..4f99a3c7 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() 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: [PATCH 129/312] 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 19fa2301..935bd13a 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; 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: [PATCH 130/312] 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 2d69df3d..0558bfa4 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) 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: [PATCH 131/312] 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 0558bfa4..1cd82b76 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 87994143..32dd4d9f 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 2dfcd40d..bc986dcf 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 86fc8b32..4e6a34c7 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 71419374..169ed8c4 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 dd054176..58ecb494 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 f0ba28a8..ecad1964 100644 --- a/src/qt/forms/editaddressdialog.ui +++ b/src/qt/forms/editaddressdialog.ui @@ -53,6 +53,13 @@ + + + + Set as default address + + + 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: [PATCH 132/312] 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 ecad1964..95909fb9 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 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: [PATCH 133/312] use #ifdef QT_UI to distinguish Qt UI instead of hardcoded #if 0 --- bitcoin-qt.pro | 3 ++- src/headers.h | 6 +++++- src/init.cpp | 12 +++--------- src/{externui.h => qtui.h} | 0 4 files changed, 10 insertions(+), 11 deletions(-) rename src/{externui.h => qtui.h} (100%) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 501695bd..7e918f14 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/headers.h b/src/headers.h index f682f746..38d9566b 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 f3061853..cbd9fc02 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/externui.h b/src/qtui.h similarity index 100% rename from src/externui.h rename to src/qtui.h 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: [PATCH 134/312] 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 917355e7..7d5712f4 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 45915c85..cf799732 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 00000000..8a6acf11 --- /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 + + + 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: [PATCH 135/312] 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 bc986dcf..52b33fb8 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 1b5b2fef..3697b9fe 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) 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: [PATCH 136/312] 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 8a6acf11..34648941 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 e25ee1d1..dc3ef245 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: 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: [PATCH 137/312] 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 52b33fb8..ed09ac9c 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 34648941..f9155d53 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 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: [PATCH 138/312] 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 4e6a34c7..e39bb7ec 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 169ed8c4..da3e52e2 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 b88839a7..46c71456 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 5f9ee18a..67c270e6 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 f73c38d6..206a854e 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(); 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: [PATCH 139/312] 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 623a5027..b3c9c7c7 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 3d4a3e16..663eb486 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 ed09ac9c..1b4e13c4 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 46c71456..32124248 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 0000000000000000000000000000000000000000..f882aa2ad8a378c229aecf7c2651a42a2f8d25b8 GIT binary patch literal 879 zcmV-#1CacQP)`L67Pgx?s>x;iesW>}d@Y9b!$flFK zd-Zkk>eAOO7q2e;&6~gd=U;yHZ3qZ=y-cN&e=KU>SIztBbb7fB5n)vW z34xep#4PL7AAfmtu(LCLy{U*n0V0Bk5JW%(X|>$l-C^(ZM||`3S5F`BKKgSL5%ErS znV`uLAZ8h=iL>(sXXl+}U)0YN;N?~YG?54aO$mVSzxx(b=j!T;5C7%>{Pz1F{4Wvc zL_>ru6U-pbbIMYy`cUo7>FJBb(eq6bL#z@RECX>lV*oNNpScl%+DT;!!RLrXV4w;a}Q2h=85vk4U_rf;zxgMY> z5=Bv<>b21fJRk(j6@n9}1l{gC2HRc=QmLer>J@=`CBn=I!Evi64Nj1(6aKN-_HKcy zwsOEyh>%vdnnK*A7hCAZc2IyO!|MUsgS9({a1-tuT|NriK@WJJCxR2KGG$Xws3vU* zakD{eO)8?oE#Yy9f|X}E){UGhX2aNCdnmhw=aK+31NUaN?v5Ls)eoH+eb|Blf?JoA zUIESOMZkS28fI2MJ6EbE6P9H;3fmu6-X^Rql)JRs?jg z?MY&HVY|^5Yy;bk1Z)di0J+ZB_b4ZuPVVk6Ah-k0b!!>2u`=#sX6F{*6jNk)@_~vx7?lsl>Q0C|FG!;2p|?1g030pJ(1Q6T--+=)H*9-*m(hLmC=CPBWtKbg!#_$8^sb9Yt z7@?M9&F26Cgc1VC4xkA9{RfH@hM&LwG5r4XAFon?0740Ye?%lUqQFOBx&fN?SWf?f>l?CkIa333F;F@Ht>0kic#27Q@-3{02rge+RLnKdrXHxU#$Pz(nE z0mOs|0f+;L#s7dw9Ar5U4>uU|@^Ulq@$oS5^Yegd2<8UW6%6X?s{ZrlZ3JZ}J|JdB zb^t&C!5#1e90k9L#{Yoj#Q*<zgHg^ z{ylvQp<(#lCx(CDeiD}>fl&ae=|PS#Fi>WYmX-?W?Og$P1V8|>pcW#CJoEoE(1Opn zE&0ntTomx~ax!e%bQ~NKAjg1cAt8|fpi~7YGXVq;3${}1|3_e!_=-PA{A2!0R1_E) zse#kMj~~C`>TlnE2BP?Z_!U3^p*r96Bh!ktgJu>{AOTbVF71R zq@)M3n+YI*P#geCZ2w<>Wgy1nzhDOtR`>fiB&0wM9wsJ4p#=i~0fg#+k3h?R6IU4h z`|}sCJO2RXfByUfwh&=C1KbfX01!Z^QSjj_BtH_9x>-;|0A%Q|Uw?r3FW7RJPeD0= ziHQ-(a?}6@2q3g5AigL@4gr|O2um@2jO++xhXVu<)?7f81OEPjTMo)vn3gi&bOdq; z00a<<13rBG%fQd|kGLrK{RbS>kQOy&FynIsvI77Dhy~Gx_1#Pm2A3^067oFkA!03d+i4%mI; z{^0@X4m5=wB`Xygz82q0ujK&k2jwEfP8UQwX~9v;E>AcL@5 zhTjp$Apj6S=)MG{s;^K?por!80W*kHM<6=@Ab^<2ZFhny807u}$TCQDAkz>_(Z!LY z03d)^$ZmN9tJcexA2U37@CM!%M8=@9;UCbum}38d9iEqPa{vMi0Q+WtY{NEovH$=8 M07*qoM6N<$f=oDrEdT%j literal 0 HcmV?d00001 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: [PATCH 140/312] 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 935bd13a..8166ce6e 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 32124248..efefe6ab 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 a81cc14f..ec8b4352 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 9120f19c..84b617b7 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 3243164c..42f44e0d 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 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: [PATCH 141/312] 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 f9155d53..79c2fdb1 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 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: [PATCH 142/312] 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 79c2fdb1..935815c4 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) 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: [PATCH 143/312] 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 efefe6ab..3641f61e 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -119,6 +119,9 @@ + + 0 + 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: [PATCH 144/312] 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 7e918f14..4036c141 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 a7fb4bd6..f044355a 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 9826194e..b89b34e0 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 38d9566b..02dba30a 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 cbd9fc02..e4605a2b 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 61b27285..a02260c2 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 00000000..7dd045fe --- /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 00000000..6080d7d7 --- /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 2dbbd674..54902e82 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 73935bce..aa74ac5a 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 8b439efd..4b137262 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 afb19526..d0072df7 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 1cd82b76..eece092f 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 32dd4d9f..d8465853 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 7d5712f4..c31be1b6 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 1b4e13c4..76ae3ddb 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 e39bb7ec..b70b71ee 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 da3e52e2..9c23a14a 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 3788f9fd..8f285c64 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 0124e2ab..bdb797a2 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 84b617b7..bb2537a4 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 5a859493..fde861b6 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 2f00fa87..864dffa9 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 a7f6537b..ba071dfd 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 dc3ef245..18ab421e 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 804f004e..72a645b3 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 42f44e0d..a3b9eb01 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 bc9b2245..644ad922 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 97334ca0..bd1b5b3c 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 22a6020d..ae9fdfff 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 688605ef..2bc2cdd5 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 a1f16949..4c1e74b7 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 00000000..eab58aaa --- /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 00000000..69110a4a --- /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 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: [PATCH 145/312] 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 76ae3ddb..24311bfd 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 f9b95c40..04277896 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 18ab421e..e49629c9 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 72a645b3..d19e1a3a 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: 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: [PATCH 146/312] 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 b3c9c7c7..bd1f599c 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 663eb486..2e4cbbb5 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 24311bfd..c8230a92 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 0000000000000000000000000000000000000000..f98e2a8ca7386c19292423b94c97818052a38674 GIT binary patch literal 1279 zcmV-i5SR2M2sP1Q8DgD*PY4i$8=S_9=hi%Zn8a+)G%E|(Y(Ls{k>Jy-J+^gZRuz6 zDMVfr6%hp!3OZK*1J_>+Nw9kpW4RBBPTyNef0EVViE#Mz~k^Z z98Me_&xM=cKGS})@#W@g{hzADHB|u+7LOe`R65Lr`O%3-o?TQ_l|Y~v zDN4|w0p5`4rb{=aV!G*jO_P7$nhjOcFQQIlO(YQPX#BsUz1D7@~A*RS$Q3S?U8O{k< z1w-4S{3q*?=*W>Fz*NOYeLvjc{Q5Vn^jFYGArb_gf&f9K5FbGRR-Nj=4+`3%bG?6kRMTPBD))hq$kAJt;&u(6^0DryJy|T~E z95~ZDyw`I+vl*GqsN0Oh#%K`f(!IQXcJu4~V#17%<4FKPXeDi0NsC6@Ad557D8&WG zjjBOs0`QbiwxV^?4xMzKR@$Z+H_76RdQ@k}Z_gk<%PZMLYbGt)B|sNlZc-)u>1H!i}U!C!HsYvzZc_ z1z??=U4tQ#2y5)~lB>U8{bKFyjistF{{@~KDQfpwvA_TT002ovPDHLkV1nEmR^I>s literal 0 HcmV?d00001 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: [PATCH 147/312] 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 55620f3e03d516fa4e8a401b9f76c1eb4c5e9339..bf43144e5e68f1a7f9e958858237c159939b930b 100644 GIT binary patch literal 34168 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 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: [PATCH 148/312] 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 c8230a92..5bf79ee6 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) 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: [PATCH 149/312] 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 4036c141..76841f84 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 5bf79ee6..44f77460 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 b3559c3b..f6241a47 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 ec8b4352..c68532b8 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 748e29bf..58f89795 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 67c270e6..33d9a254 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 00000000..cd1194d9 --- /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 00000000..a44c9c4d --- /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 e49629c9..28d22b85 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 d19e1a3a..c26acbc1 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 00000000..bf7d55d7 --- /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 00000000..e75dcc25 --- /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 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: [PATCH 150/312] 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 44f77460..f5885dbe 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:"))); 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: [PATCH 151/312] 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 8166ce6e..a4cbbe0a 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 33d9a254..d83962d1 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; } 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: [PATCH 152/312] 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 76841f84..539c3264 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 c31be1b6..397af5fd 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 f5885dbe..2b1990b9 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 f6241a47..41b665c2 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 b70b71ee..125cb910 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 9c23a14a..a5028ff3 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 d83962d1..5c889b23 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 206a854e..bbb6a5fc 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 00000000..0fb7d210 --- /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 00000000..5b46dfb6 --- /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 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: [PATCH 153/312] 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 2e4cbbb5..fc35f895 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 2b1990b9..4e5eaa06 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 125cb910..30b4fe72 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 a5028ff3..18b3ba11 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 0000000000000000000000000000000000000000..ee2dc40563a1e068ba73859657fa785304cd9d06 GIT binary patch literal 28756 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 Hu0mjf7RKiu literal 0 HcmV?d00001 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: [PATCH 154/312] 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 eece092f..ecf6f3f3 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 4e5eaa06..84eb3665 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 ea9eac679ffabdb076b3b0b23fa0874f12bb7472..e59a2cdc92cfb81e93786b0bfd0bddbaef63a63e 100644 GIT binary patch delta 1746 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 Ye;CqFjyt9`8~^|S07*qoM6N<$f_!^eRsaA1 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: [PATCH 155/312] 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 bd1f599c..f34c2c91 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 fc35f895..2dfc7dd9 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 04277896..40ade40a 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 0000000000000000000000000000000000000000..1d691451514079074d6c1a4b057d8c1406e5621d GIT binary patch literal 1627 zcmW+%dr(tn7XNZ@$OA4UJT&n|l2T1t=n}w+f@>}{izxDtIu&heQXak^5m-S%a+4?z z&GJyodneR!)v>J&)6P__ySX8t7(u9Y#-g?4(x6bbHGm+Hz!0nkxX z=H7N;AtE;=D-D2?D*+%30L(!jjsfs@IRL+_0Z3>9KydWDsyi6~zaKX%leU*U%~@YS zjcHl2*1nI6|CCk^M#f|UfK)M@#1P~fw76&b34})#&<&l_{V+bfd|o@+`2aqMUvUDj z_5wy1^DSX@=Ipo#wGCNt&npEVqPh1I*I~bQvYn={56)?eKcYSFvoQb52|!Gc5?5sv ze>N~c7Y%7ese2s$ac~E%eQtBXq0k>7+`XKJulh#dtD3*imir&BJ`?6>|)VSXZ+D=tF5br%g49AeRWrRP|)2om~lD&Fm;XkjyYCd=J0#) zPxlx1G{4`?RqOMETRSFY+Xk13iIf5$TS|YjlO!eZqK=9jm{j1spi49>41ezKBwy^b ziz8M(6t8;suIojh?1<3FZT97ex_l-3O5@j<@jU)Yk!#?Tp%oxB(N?&>l(mL?qC%^M zaq`%hki)BRQ12uZwLx<8m*6*ADnM<#_UL{QJ1z`ucO{KZ{5Qf zKH5f#sr;L#pwvKEqRmZozl*o@O)qS&KJKd~c!YqaaJig*istoz_vpaaZ^{4Ex?(-_ zR4sM&vjkKc7bh=8#yOcMT%u8a@&^(zuRtOfI>&%9Th=>6Rd5BXCWHRU4;0FD_nPG0;b}z3< z4<}G2e>dBM+^(OqhOqYRUsp8(b?G_GVFiLki=OZ0Pdq?&WEzdVj@1BTi(7S+yHuSoKt8`Rsna1N1iQ||Q#@>PXg%f-tC5yzlZhJa zK8IbRPhr|}xMy~LI??vVq!7}`VecE!rTXgX*6rEKOFL=nyytRz*_r_EMb45W>$D)7&)C>fe=P@unY5OL8C*sB@@Khcwi)4}-#d|!Zx_)9JDPkbImZt$PE&*>h0%pY zjcmP;D!Jm>OY8!@pOaB+>u1fC+={KZUx?-78_ Me@j>1_&}}yALD7)0RR91 literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/editdelete.png b/src/qt/res/icons/editdelete.png new file mode 100644 index 0000000000000000000000000000000000000000..945d221eeaa3cf0ffabbf288d7da7541adf05ddf GIT binary patch literal 1368 zcmV-e1*iInP)PbXFRCwBA z{Qv(y11@m%;xpWm1i;N(ckU;L*(>2w1F{<+fEWomz+7L7frWr&AQQg-W?=a7^FPD) zAHNyCeEZ4p>iu_yr;lzwjP$cnz~=yf0AeEK6GkF@`|BSA!{5I^{O>;y|AXK^zZhJc ztmPN2JNOKrUVs2%VxR(G2Aaam48n{ItSrn7EImH1}iHw@g*CMV1xuf08zyOV1t0JU}AtcfCU&0KYslICdNMu??3+nCeB|B zzkYmW(9zMAnYa4jOJscj0YoqtSO9YYGZ78|ra^|^e;62kf^)-Ph6hhyGhDcO52yN! zaBHyL00G28tq=f310y4Z21S9aq%eb|r~t#a@4pycz5C4Y6__>u0WJLX`#;0A>(@Y9 zjDh$pKmbwA0ZdR=Fad*$@h?Q|A23^jEdKlF9|J2ZGsC~X{}~vWP%}0_0Fj;sjxJ#M z-)P9d@a+SpBbZ>>5||DiUt-|uc4Oc?xtxKWorQs&jg^6e9f*N6G5`o57LqNWmk24v zx*Zu9OZPJ{a0#GfN00~q+&#y@*5=Ic@AF3nk;_LI{sTkgvsoC!f9C%TO#e{*3=lv} z{k1l{60SgIWVaNb1g&DrzILE-!>IAm@4=_>w1BwYO$YYl&Aj>}?EdL9n|NMp|Oi(GwhUNf(03zN2)(H%ZN%K*J z{sDvb&%Fx_zZx7Feq7kczyb<(ESCSuoX+sy+?Rm`=m-`TR4V}jh_o~Sas(*;fl&dn z9Ap6)-@U-V+Ug9k5Y6&`*)tdzouV07{{9Eon}7eKIshPmNY92qM}X?zzcUgcmj8xg zES59m&1PV5i~?p+VD Date: Thu, 30 Jun 2011 21:11:51 +0200 Subject: [PATCH 156/312] 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 4f99a3c7..ce9c6a53 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( 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: [PATCH 157/312] 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 ecf6f3f3..25500764 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: 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: [PATCH 158/312] 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 bf7d55d7..6b314f4f 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 e75dcc25..96cdf977 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); 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: [PATCH 159/312] 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 13347961..13d263b7 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 827cc741..d2caa3ee 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 92d7daeb..4308a893 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 84eb3665..bdedfc69 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 30b4fe72..06ad5adf 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 18b3ba11..659fa657 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; 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: [PATCH 160/312] 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 28d22b85..b2fe167b 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; } 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: [PATCH 161/312] 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 b2fe167b..5b11b331 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 c26acbc1..835b3875 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; 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: [PATCH 162/312] 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 6b314f4f..84de1571 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); } 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: [PATCH 163/312] 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 84de1571..8763e10e 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 96cdf977..fe0f154b 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -27,6 +27,7 @@ public: Today, ThisWeek, ThisMonth, + LastMonth, ThisYear, Range }; 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: [PATCH 164/312] 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 bdedfc69..eeca18ef 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 41b665c2..95495372 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 40ade40a..d99651d6 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 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: [PATCH 165/312] 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 8763e10e..7bbe5500 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)); 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: [PATCH 166/312] "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 eeca18ef..f6b87c54 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 95495372..046186c5 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); 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: [PATCH 167/312] update build instructions (thanks tschaboo) --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9d7ad10f..31199145 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: 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: [PATCH 168/312] 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 25500764..ca605241 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 d8465853..3ababfc6 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 58ecb494..8ffabf47 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 d99651d6..9bfcb30f 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 95909fb9..b4a4c1b1 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 3641f61e..59599f2e 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 5c889b23..14d50963 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 bbb6a5fc..968cbe76 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 5b11b331..0d0d97bb 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 835b3875..f75f414d 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 0fb7d210..f962b9a7 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 5b46dfb6..9c7d16fc 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: 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: [PATCH 169/312] 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 ce9c6a53..5eb60b77 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 ca605241..6829fea6 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 3ababfc6..b509481e 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 8ffabf47..7ea5638b 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 6f396d04..62199611 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; 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: [PATCH 170/312] 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 59599f2e..73a42b63 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 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: [PATCH 171/312] 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 5eb60b77..5a744aec 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 25b8839c..b032e554 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 6829fea6..e375ff8c 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 b509481e..6f34a600 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 73a42b63..26de3d2a 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 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: [PATCH 172/312] 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 9bfcb30f..12ecb136 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 - - - 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: [PATCH 173/312] 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 31199145..9c1db1e6 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: 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: [PATCH 174/312] 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 26de3d2a..7f843c45 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 14d50963..4b974371 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 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: [PATCH 175/312] windows build fixes --- bitcoin-qt.pro | 15 ++++++++------- src/{rpc.cpp => bitcoinrpc.cpp} | 0 src/{rpc.h => bitcoinrpc.h} | 0 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 ++- 8 files changed, 43 insertions(+), 37 deletions(-) rename src/{rpc.cpp => bitcoinrpc.cpp} (100%) rename src/{rpc.h => bitcoinrpc.h} (100%) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 539c3264..40c0ded6 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/rpc.cpp b/src/bitcoinrpc.cpp similarity index 100% rename from src/rpc.cpp rename to src/bitcoinrpc.cpp diff --git a/src/rpc.h b/src/bitcoinrpc.h similarity index 100% rename from src/rpc.h rename to src/bitcoinrpc.h diff --git a/src/init.cpp b/src/init.cpp index e4605a2b..cac921e2 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 397af5fd..78a20c51 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 12ecb136..66f1076a 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 c68532b8..31b28024 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 bb2537a4..809e4730 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 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: [PATCH 176/312] 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 7bbe5500..d024400f 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); 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: [PATCH 177/312] add windows build instructions --- README.rst | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9c1db1e6..458b28f8 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 ========================== 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: [PATCH 178/312] 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 eab58aaa..f8963367 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); 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: [PATCH 179/312] 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 f6b87c54..cd9291ac 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); 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: [PATCH 180/312] 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 40c0ded6..a8705c36 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 f34c2c91..d4eb8488 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 2dfc7dd9..20ecd9f3 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 cd9291ac..bf4aa320 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 046186c5..432a9e3b 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 00000000..ef9c0730 --- /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 00000000..faed2986 --- /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 00000000..85b927b8 --- /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 0000000000000000000000000000000000000000..60f135178360930a102923bdc1fff925b95bf842 GIT binary patch literal 746 zcmVf#Esx(;>0l_lEBD_ zK8+xVAYKy<5y6##M8pB`<%b4r$tq{ z%rKWq2j<-1=;-@55m~+nsH*NSE?&D43CQz&d12uy^YgWt!n@rbCnrDsjd}t7exEGM z$g-L8J*z}OPL}`W-lNt%OwfO0a#kPPQ6|Sz*>v7mORh>*rlra1fsdB z`ZeLP*Xwb7{G&?1paKpKV2nXkG3I=`P*p@Ex=6U0x!=l;wOXxE>5(l+;clMic|k}h zCKX_e!CG4_AT{B$=PybHrLo>>Mnbkf?E$d5atDCDFJA$;d+R15Qs5Lk;Nt1yN6~zg zA6z_GU5UKWY~1#1M5MqO`ziQWewf$;sfZ9XjUOrnq_5>F4?O9#Be)w|YXNw_vkMGb z0kE_C85p#}Rk2h+cz00rKo}|9g|(LTR?~YxL})b|ktb44rGR1aH+~o*pxDDvB0|u( zen>f$6&QCP=(M+@mc0G&5jfu;oF`h%Mk%MV0>eBIhBKe5!sfJ0#!8*3P2c>Qi07lA8M*si-07*qoM6N<$f`EuiC;$Ke literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/overview.png b/src/qt/res/icons/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..6b94b43a2c05e38edb72d5021ecd40ab4e64ff00 GIT binary patch literal 7015 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 literal 0 HcmV?d00001 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: [PATCH 181/312] 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 a4cbbe0a..f16c0c51 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); 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: [PATCH 182/312] 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 5a744aec..220679c9 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 b032e554..fe243a62 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(); }; 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: [PATCH 183/312] 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 220679c9..6d53ee86 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 fe243a62..befa8b76 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 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: [PATCH 184/312] 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 0d0d97bb..65e16323 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); } 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: [PATCH 185/312] 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 bf4aa320..3ac87262 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 65e16323..70dbd0ff 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 d024400f..a3407e85 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); 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: [PATCH 186/312] 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 3ac87262..fbab51bd 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 432a9e3b..e04bcf91 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 ef9c0730..4cb28d5b 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 faed2986..87f95fdd 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 85b927b8..fbd6853b 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; 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: [PATCH 187/312] 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 a8705c36..77d70b7b 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 d4eb8488..786427f2 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 20ecd9f3..e64744cd 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 fbab51bd..34da1350 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 e04bcf91..a5fcc8a8 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 00000000..62c0b949 --- /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 00000000..7367f3a6 --- /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 864dffa9..1b527bc7 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 c196be2a..0050c878 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 70dbd0ff..52c9b0ce 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 f75f414d..85bfeebc 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 a3407e85..037dfbb3 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 fe0f154b..25212c97 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(); }; 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: [PATCH 188/312] 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 7dd045fe..bfad27c6 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; } 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: [PATCH 189/312] temp patch for qtui --- src/{rpc.cpp => bitcoinrpc.cpp} | 10 +++---- src/{rpc.h => bitcoinrpc.h} | 0 src/headers.h | 4 +++ src/init.cpp | 9 +++---- src/net.h | 1 + src/qtui.h | 48 +++++++++++++++++++++++++++++++++ src/util.cpp | 10 +++---- src/util.h | 10 +++---- src/wallet.cpp | 4 +-- 9 files changed, 72 insertions(+), 24 deletions(-) rename src/{rpc.cpp => bitcoinrpc.cpp} (99%) rename src/{rpc.h => bitcoinrpc.h} (100%) create mode 100644 src/qtui.h diff --git a/src/rpc.cpp b/src/bitcoinrpc.cpp similarity index 99% rename from src/rpc.cpp rename to src/bitcoinrpc.cpp index 6f951b74..0493c6a0 100644 --- a/src/rpc.cpp +++ b/src/bitcoinrpc.cpp @@ -13,7 +13,7 @@ #include #include #ifdef USE_SSL -#include +#include #include #include typedef boost::asio::ssl::stream SSLStream; @@ -46,13 +46,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) { @@ -843,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)); @@ -1161,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) { diff --git a/src/rpc.h b/src/bitcoinrpc.h similarity index 100% rename from src/rpc.h rename to src/bitcoinrpc.h diff --git a/src/headers.h b/src/headers.h index d1844eb2..02dba30a 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 635799cc..adbfa18c 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 afa264b7..8f21de8d 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 00000000..a3b9eb01 --- /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/util.cpp b/src/util.cpp index 479c601e..3d89f6a8 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 e7110570..4c1e74b7 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 6ef75ef2..5b88f387 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); 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: [PATCH 190/312] 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 644ad922..0493c6a0 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 cac921e2..adbfa18c 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 7dd045fe..bfad27c6 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 54902e82..594f1d3b 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 4b137262..0d3348da 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 cafb175d..8f21de8d 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 e375ff8c..d04989ea 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 0d66d6a9..31862a71 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 2bc2cdd5..3d89f6a8 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 f8963367..5b88f387 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 69110a4a..7d9db972 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); }; 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: [PATCH 191/312] 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 52c9b0ce..6a7f7aab 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; } 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: [PATCH 192/312] convert to full tab-based ui --- bitcoin-qt.pro | 6 +- src/qt/addressbookdialog.cpp | 177 ----------------- src/qt/addressbookpage.cpp | 181 ++++++++++++++++++ ...{addressbookdialog.h => addressbookpage.h} | 20 +- src/qt/bitcoinamountfield.cpp | 5 + src/qt/bitcoingui.cpp | 151 ++++++++------- src/qt/bitcoingui.h | 34 ++-- ...ddressbookdialog.ui => addressbookpage.ui} | 115 ++++------- src/qt/res/icons/export.png | Bin 0 -> 1339 bytes src/qt/sendcoinsdialog.cpp | 34 +++- src/qt/sendcoinsdialog.h | 5 + 11 files changed, 377 insertions(+), 351 deletions(-) delete mode 100644 src/qt/addressbookdialog.cpp create mode 100644 src/qt/addressbookpage.cpp rename src/qt/{addressbookdialog.h => addressbookpage.h} (73%) rename src/qt/forms/{addressbookdialog.ui => addressbookpage.ui} (50%) create mode 100644 src/qt/res/icons/export.png diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 77d70b7b..5e2646fe 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 6d53ee86..00000000 --- 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/addressbookpage.cpp b/src/qt/addressbookpage.cpp new file mode 100644 index 00000000..67ed0fec --- /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/addressbookdialog.h b/src/qt/addressbookpage.h similarity index 73% rename from src/qt/addressbookdialog.h rename to src/qt/addressbookpage.h index befa8b76..f25bbc99 100644 --- a/src/qt/addressbookdialog.h +++ b/src/qt/addressbookpage.h @@ -1,10 +1,10 @@ -#ifndef ADDRESSBOOKDIALOG_H -#define ADDRESSBOOKDIALOG_H +#ifndef ADDRESSBOOKPAGE_H +#define ADDRESSBOOKPAGE_H #include namespace Ui { - class AddressBookDialog; + class AddressBookPage; } class AddressTableModel; @@ -13,7 +13,7 @@ class QTableView; class QItemSelection; QT_END_NAMESPACE -class AddressBookDialog : public QDialog +class AddressBookPage : public QDialog { Q_OBJECT @@ -28,16 +28,20 @@ public: ForEditing // Open address book for editing }; - explicit AddressBookDialog(Mode mode, QWidget *parent = 0); - ~AddressBookDialog(); + explicit AddressBookPage(Mode mode, Tabs tab, QWidget *parent = 0); + ~AddressBookPage(); void setModel(AddressTableModel *model); - void setTab(int tab); const QString &getReturnValue() const { return returnValue; } + +public slots: + void done(int retval); + private: - Ui::AddressBookDialog *ui; + Ui::AddressBookPage *ui; AddressTableModel *model; Mode mode; + Tabs tab; QString returnValue; QTableView *getCurrentTable(); diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index f16c0c51..1359a32b 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 34da1350..99dbbc11 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 a5fcc8a8..8c3632a3 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/addressbookpage.ui similarity index 50% rename from src/qt/forms/addressbookdialog.ui rename to src/qt/forms/addressbookpage.ui index 66f1076a..d3feedb5 100644 --- a/src/qt/forms/addressbookdialog.ui +++ b/src/qt/forms/addressbookpage.ui @@ -1,7 +1,7 @@ - AddressBookDialog - + AddressBookPage + 0 @@ -15,87 +15,38 @@ - - - 1 + + + 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. - - - - - - 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 - - - - - + + Qt::AutoText + + + true + + + + + + + Double-click to edit address or label + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + diff --git a/src/qt/res/icons/export.png b/src/qt/res/icons/export.png new file mode 100644 index 0000000000000000000000000000000000000000..69d59a38d20e0e3f1265b9cbc0937d1d3f847451 GIT binary patch literal 1339 zcmV-B1;qM^P)uqfx0f-a;ZgbdN?326Dw0$C9P+*KAXMr)w;$B3W#tsQ! zN~A0hH47p4$Qq3Xip3(Nl(=~DVsime#JB!n&PoInu zpm{IvrCf^^as)JQu4yQhO1N<00?wQ{gYNEb3=9k)olbu-K0Y2QLfbDv1i7XiAke}_ zbq=7dt<5Ls>+8eN(9jzIrgdG9hYIk?fn!B{h@3GD4h~{|ejc`M`y`{Iqwgh?$xZ;@ zXR}!?yaZkunx$DG4g2lXsZ$sn97H~!2NAgmX0c0`E`50J+_`rF+&EkUubEslt+kp3 zRfs$9?CeBWR~HI}g35JrY;5fP-rnA!OeXWKPYYnQW}0^$$2syO_(#!Qudc4bFbp`3 z1I{^w5C=vNsZl|o-%pGsbvnwr|IR;#ykU7z&71 zJ2Nv=HVosTuIm#($UP7yB1q7z2P%~cR##UsJUpzr8+Y#9SzK9JDeAiZmFM&`;5VG| zMp#{s5&{4_J3AJ0$esM1Y6cT-K^JYE(c>MS=QmGUY ziG=UFckkYxOG`_C1NbbP%}Vuo|6`;^AC~%7$fcAZBGl`3-?zNHjAF643m~1zWCQ}X zp8+&Am%>y5De4jjWMlB2RArG(=+ zuq+FfWuaCxarW$4JbE;Tcp^dh*@x2`8ymkx1*8QX$GlJ=r9{16$J*K&o;_Q}*48H4 z+dGg-4decU2S44;-O3Qr=282M*6Ve8_39N_mPL(5gKXO-+qQ{wPMmWRLXZ%G9LFIk zr4P3VsHnB9Ce~B7);Mux%Ub8ykox z;&2>+O64W=>(>jnf5=?{@WT5~5M}-;h$4&hVKnH*i0KE2J zvH@TRz>{W2Ey|dhEykDzAmJjej2>bxG#6^a5CG}IaUm3NW#nFu#9djZ$4?)*f+4~5Kj!Iilk5Gnyk#u$i5{tLgLs*$b}ue1OF002ovPDHLkV1hpwS84zN literal 0 HcmV?d00001 diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 4b974371..8050baff 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 968cbe76..4e019b72 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; 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: [PATCH 193/312] 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 67ed0fec..5127eb60 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 f25bbc99..c4039523 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 8050baff..2265f88c 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 037dfbb3..5a383da2 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; 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: [PATCH 194/312] clarify todo/dones in readme --- README.rst | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/README.rst b/README.rst index 458b28f8..d9b47022 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 =================== 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: [PATCH 195/312] 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 99dbbc11..cfdccbea 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) 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: [PATCH 196/312] 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 d9b47022..949bf17e 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 5e2646fe..2a389e68 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 00000000..87185155 --- /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 935815c4..afe0dc89 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... 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: [PATCH 197/312] "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 6a7f7aab..a529dadb 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(); 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: [PATCH 198/312] update README --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 949bf17e..da480adf 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 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: [PATCH 199/312] 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 786427f2..f58b0da4 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 e64744cd..1522ce61 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 cfdccbea..06c2034d 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 06ad5adf..8885b4cb 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 659fa657..6c2c275c 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 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: [PATCH 200/312] 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 7f843c45..a5c38a53 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 2265f88c..91397a7d 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); 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: [PATCH 201/312] 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 06c2034d..add8abc9 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 a5c38a53..c3b966ca 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 0000000000000000000000000000000000000000..c9e71184c5c0641bc14763cc14ae899c98363eb6 GIT binary patch literal 1013 zcmVPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipe- z4l*kL>;fJD00VVNL_t(I%U#gRPh4dH1@QAd?##V&nSp^}W_S%kp$JqeRRLd(x={nR zO^rfAS#&3E>c+JTS0=jFbfcm1MN(^vreZY2rj@8P6~d#XQ#&vqP+-bDn7K3ed$`a> z-9{N%vP7#*jY61bY71cee5Bp4(kNPK@*?5r<-wtn#G-i@a2=eAd8zf0a(Dgr=x z^nm5f@4pSeE#C@YdCU!-s z1djrsxMP*2UAy|~AHRLoM<~iT77;{#+$EPo$3yegq&- zE=-&u04#HDIR?N6K(r>5D&ZCy+v5Q809bHuJ!Ia$SNouVXjlB%+;f)PX^W(-{TcE9*?h_kiTC*YGX}vE$D+q>9kpX&feP|F{|pW zf+nVN_D+!8{s7?yH^ObqQ7S{iI%uKBR|6PjbW)l3TYWDLz&ut(_>YLo0A1q&P z+i`L*Ffjepob;KMk>w8y0|UcfCh6VZMgHqENQg+W0R#Yq0DJ!dY`t0_7bfN30|);A zCJi_MzU7+xDh)vb2NnPW&Cu6b1OoOP>fhkS`vL(2|K0(t{|fGA5XcUo_yyPwoelC* zy#nL$vi1cM`+5!x5e5Vi_^(_ zkn#ll{`wRlCHKt-2r~fw_5k|e_RRo2B3TE@1fu8&(r^p{{PhL^_VxJy)#?!#qFB?D!0000F1O)UM#pm?rHvj+t00IuayY~F>Ckp5^Kf&$LXAb}#^aAnj z_&x;*3`YhQ@VoKt`X>(nI~W5b!s7}bB z|Na9f7Ci?1|M(XI{q_a`=jZgH8UHi|`WdY9{2Z0ssI201WL_ zXw=x^nhf_O`y~fJ1K7~(_Ttkf8vN=P>jE@QEv-#NQTW`)+?najBvxXYq|Fr6A`J>L z;6kMIH-G9@$#sc9ZFleu^9cq1wv z_&Bd`an9j8=)v0+m%FbUX*P>`(Jo-ng5vbzRN9&Wn@&R(ABO}<=)hM9;yyH5)$wRF zeot!muB$r@sTv2%h=PIhXpYSR)$kNc45&8NL=rjANw2$_U~b?;-=E}C`HKr~Mc*H^ zE_mX_JLAivB@A32^MQ_z>1SYGI}JrrhCGxYgGvAqU8Mb!TR!{vf$ZG8y1m<6jea;W zY3HM0#1#QFq=b!BV21=1D(JsJWLtN1IQiC>wS4qLV z5wN4{4*|#bNo>=Y3~%dvPF%?WNCdrM-Oq^I zPdnkKQ#!WXO7@b8BvE^cWs6zoX6+JFAA_p3t#Gf|-(gYO?sk0nPw)Sm(mqoz)gG+f tij(L!fgrY#bpayTo->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 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: [PATCH 202/312] 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 9724368a..9b7cc632 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() 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: [PATCH 203/312] 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 a529dadb..2477a1e8 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); 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: [PATCH 204/312] 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 d04989ea..9ca75420 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 6f34a600..d48e7866 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 add8abc9..52919513 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 8c3632a3..b82818aa 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 9b7cc632..4adda7e8 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 2477a1e8..17622e07 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 5a383da2..53c33c7c 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 25212c97..f02751a0 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 f962b9a7..052bf37e 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 9c7d16fc..5b46dfb6 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: 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: [PATCH 205/312] Readme update --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index da480adf..5412cb99 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 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: [PATCH 206/312] 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 aa74ac5a..124c7c26 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 9b84fb9e..ff0b4afb 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()); 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: [PATCH 207/312] 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 aa74ac5a..124c7c26 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 1b527bc7..c74b48bd 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; 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: [PATCH 208/312] 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 5127eb60..4f629fc2 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 c4039523..53c7728c 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 52919513..94bd9765 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 b82818aa..6f4ca191 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 53c33c7c..03df4226 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); 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: [PATCH 209/312] README update --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 5412cb99..25ed9a8c 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 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: [PATCH 210/312] 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 2a389e68..99b6eb38 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 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: [PATCH 211/312] 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 c3b966ca..8009bd2b 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -107,9 +107,6 @@ 34 - - - 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: [PATCH 212/312] 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 99b6eb38..5dd37b0b 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 78a20c51..bcc73de8 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 00000000..bb029bba --- /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 00000000..4008c7fa --- /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 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: [PATCH 213/312] 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 5dd37b0b..aee056ea 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 bcc73de8..63d1d706 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); } 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: [PATCH 214/312] 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 94bd9765..567de522 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))); } 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: [PATCH 215/312] 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 25ed9a8c..9ef45765 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 567de522..144bb22a 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 6f4ca191..95e0eb70 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 4cb28d5b..d8362a7b 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 87f95fdd..c620200b 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 fbd6853b..acf83c72 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 052bf37e..3e7152da 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 5b46dfb6..4c34cfbb 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 5b88f387..fa577552 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 7d9db972..078d7e6e 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); 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: [PATCH 216/312] 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 144bb22a..0c66bd4c 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" + 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: [PATCH 217/312] 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 aee056ea..9708b82f 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 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: [PATCH 218/312] 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 0c66bd4c..016f2619 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 c620200b..d991f0d7 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 4adda7e8..01d68dc0 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 4e019b72..46814af4 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 3e7152da..afe095c9 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 4c34cfbb..1105fb03 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 From 608810a3e77b9cdc6513e1f09388fd75f655dbee Mon Sep 17 00:00:00 2001 From: Celil Date: Thu, 14 Jul 2011 17:11:11 -0700 Subject: [PATCH 219/312] 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 4f629fc2..063e510c 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 03df4226..8aa43395 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 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: [PATCH 220/312] 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 016f2619..9ee178ac 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); 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: [PATCH 221/312] fix quoting --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9ef45765..16d03c3a 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) 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: [PATCH 222/312] 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 5b88f387..fa577552 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 7d9db972..078d7e6e 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); 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: [PATCH 223/312] move README.md out of the way for now --- README.md => README-original.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README.md => README-original.md (100%) diff --git a/README.md b/README-original.md similarity index 100% rename from README.md rename to README-original.md From 97f908e483d06796a1b9c8ce34de304fcc36c31f Mon Sep 17 00:00:00 2001 From: Celil Date: Fri, 15 Jul 2011 16:10:48 -0700 Subject: [PATCH 224/312] 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 9af4c671..e1b85517 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 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: [PATCH 225/312] 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 9af4c671..cdff04b4 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 f58b0da4..f3900fe0 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 4578ca74..125ceebb 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 d48e7866..296fa580 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 1522ce61..9ef8b2af 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 4308a893..37387780 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 1359a32b..d545dc52 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 67304c8b..2a0ef4bd 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 7ea5638b..a0b27e83 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 8009bd2b..57b79279 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 00000000..1159ef53 --- /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 00000000..4b5acd8b --- /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 00000000..9fc026fa --- /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 01d68dc0..38a0a655 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 46814af4..0f90be81 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 00000000..6e87e9cf --- /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 00000000..55fd12a1 --- /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 afe095c9..6e4b814d 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 1105fb03..af2cac4b 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; 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: [PATCH 226/312] 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 9ee178ac..016f2619 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); 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: [PATCH 227/312] 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 0000000000000000000000000000000000000000..a44b6d130b5aeeccc3641a6c0b831d83d9c03235 GIT binary patch literal 1224 zcmV;(1ULJMP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipe_ z3ndRC4ewL{00c-$L_t(o!?l;oZyZGshrjCXnb{p@AHhOFh(km`+z5&kaX{q6Kaf9z z1Ls7DBNECXaNz{v0A~bek}DyJ0up&3L=r_750Tge<7e#c?kWzmUXQUMHp;d%>fTaM z*H^#luIUvsW52EXtN;xa5xGAfdX%y)wLB?-df-Hnqj{ai%$cmkDcbs@iUaJ zL@|_1B9llYNn|pY=Dud|?hH%stUXx(1WwGJpd%fUB@&IpL?T%tOEW(D)KL!7_6=Zw znljSS*twF3lRnuPN7Hlw1jw36+C<-0Ad*mW)2mJg0BZoY705fJ-?C-P(*h8(W}?Pj zdq72r#0Ee!V$(>0f@&t_xWoCRA1je_8Yk?D0|+T^E1;5zTS~|_M7G@Q*r_OxGck`^ z(T`8SmPN*=-~a&fVcC7D>h(T)_SEaIq6*r)Z8jdOVwO-<35WWIY6XDWWRBVPJTn{J zxpeJEFS?<46cAAq-#8ps-Yl}dD7+cexpS|5@cya8FTQ}PVlkm9VWw!xSk92rq$fp4 zU5)0a&^AEq`6PiUSYdu30+-f;j0U*UBA&p*>co|a4j3soRpBwEXRoDv@7bJs*E9YtU$_aK3HJ!@;5A9 zzk!c&N961QktOc1DGAN;#mw^UJ1ktj%KF{gI1JyCSN{Wo zNjh2zBp`2YHaq`cqPCoPwD6F{pKf#i%2h^xJj4MWPauQXjvR+^DN6rX?X+)>ytb$MW3Itj^7`^2@KpwYA-!ClRo{A-4K9L=-4m5DJ7W!Ve#R z!rjlmAapud&aBVRKem8q<82p&dkjRL*osC069qh*i;HY>u(lUO7WT}4+GB5Mvj_ae zW!0Apkskl6wd<}cfLIE611C-8bCPg?LkJ>>87od!qHs~njJNxP6ZeKJ6pM;FkK(FXg7d2hqWg|bD z(v_^$dS#!%7+_cp8B_yi!VKM_W+g387oc<{y`o3I>@%na+e(yu>Y~Pv{B!}jp{6cs zW{Q~xqy^C{d*qEgU4Txg==$yiM(UtPY-|y4u=;{aAEQkzJZx3YUY>c545Bl_6%<2LF0000 Date: Sat, 16 Jul 2011 19:45:37 +0200 Subject: [PATCH 228/312] update readme --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 16d03c3a..dc9a8b15 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 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: [PATCH 229/312] readme update --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index dc9a8b15..98add9f3 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: 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: [PATCH 230/312] 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 e3ad3504..3a482e7c 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 016f2619..6a6f3f32 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 8885b4cb..c147aa5a 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 6c2c275c..f7ad14c2 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 cdd1a74d..59f49625 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 6e4b814d..4ff2e0ab 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 af2cac4b..668d4463 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); 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: [PATCH 231/312] 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 f3900fe0..2ab8f56e 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 0000000000000000000000000000000000000000..c3ce165adbad91ba5e4c7013b12f8df92219401d GIT binary patch literal 28597 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 fgTXt||GV)Yvgres/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 6a6f3f32..ed687c45 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 95e0eb70..4fc17dd3 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 910fc396eded0241ddb2e19678f1406a815e163f..8e428b6a7033b6ee8b06116f47ba1069461ba232 100644 GIT binary patch delta 682 zcmV;b0#*Iz2)YH39De{G^Z#K00004VQb$4nuFf3k00006VoOIv0RI600RN!9r;`8x z010qNS#tmY3ljhU3ljkVnw%H_000McNliru*b5L91v|sxE|vfQ0y;@VK~y-))sszV zk^vmXAK%xnbUK|)&8eFjPL|o&`f?P@jdh5iAn1$1pt{7aqkmnt(I*7O4j%dKPP_J8b{;r;Fc>o&DfqZvo^JL zTb|CNKo<~*`G3`s&j3 zGMfhpF{Z+I@Fe1imuk39nvRN3yE@g^+SP4s&2kAL5O9@+`$-=F*l)C2PB{-eGpTeI zmSe!w7(5K!#f#YV;wl9fbq+E9rv#PflS~cb6hRV7Y=1H;hs-xIX4f0cedgX7tyHFC znF45rz@wlS(UovGRl;8SELH%3Ctl!Z>wB;m+4d4GHNWV1}B;dl&6 zOab0EUVO+#Uw$lNpgfLD-6F{aP|iEbUE8}Uo2Vg!iZF&2HFogDMG4JiL9r#wybWL} zlX{vcVt-#@6ou~(%m;ufi+ky1`9YIX1AqV{Ttk^#1061fLC8ix|Yzrx>wRGmdl)JJRWI!jNLCP94> z!5@5wg=G9zzJinQPk{YXz<70`ccaUoz$>8;JR<=gwJLBmM`1KH_^%i2EBplCVa-Qb Q`~Uy|07*qoM6N<$g5=^ewg3PC delta 1114 zcmV-g1f~1B1?LEm9Df05^w0MI000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|DhWnpA_ zami&o000CDNklQ z7S{iI%uKBR|6PjbW)l3TYWDLz&ut(_>YLo0A1q&P+i`L*Ffjepob;KMk>w8y0|Ucf zCh6VZMgHqENQg+W0R#Yq0DJ!dY`t0_7bfN30|);ACJi_MzU7+xDh)vb2NnPW&Cu6b z1OoOP>fhkS`+ouf1OMIutp5t`W)R2@p!fyY4xJ71QoRD>@v`;>68m}%3=swd5%{nG z008+A|2{}8E+b+kL)Gd5PMR(N00000AQ?yt>iw|qHzhp<+Stp{sQe-0ssgB z_x=HD-4E#j<~&LN7|Ea$4-+c<{`~&$_2>Qu)@&B|DSwdi1pNN`6d@(|%?AiG0RHv> z`r!7>06ijE2g(GZ=m^qq3d2mlfi|F{DF2SyhGRtf|1CG!9O{sSi#JqG;$_!k2G_67jw z=k%c&|1<^q8LaaB9If&i1~dcw?FRt*?G66|000004DD5D)Y#&h4EH4aB?mwQ*wE|t z;?pJ?{OT9$0yIr6txZHx_}s_bnd!_VR$`i@&3_csA`J>L;6kMIH-G9@$#sc9ZFleu^9cq1wv_&Bd`an9j8=)v0+m%FbU zX*P>`(Jo-ng5vbzRN9&Wn@&R(ABO}<=)hM9;yyH5)$wRFeot!muB$r@sTv2%h=PIh zXn&5)0oCvnOAM$s)F=^+cV8j&xG^B)$RA7$i zhO4}#fFQvbkl>am*H!K-5b11Zq|q+GUw=bf(DqV;HiK75!MqW$qw5a=$M;EW)0hke z^yinM3Bx~qM;71I&(7R6_rU`~FkppE?-?fxmkV&mVwrzW2+qP{^GO>QKZD+z?Y-8eyJ+W;kXZExAzRz8!>Z$sluIgHA zUEMGG#rmvE-;PpHltP5Vg98BpAyAMOSN%Jp{O30RJO1wrp5b1{9jMt z0=~Y!P^3DZ|IT5ZrFGo?j?(|V6~q% z{#xK9RYep)LBRe_|FwD*bV&F+BFg-)kAZ>uYazb-G64Yr+maC%QRkWHR{O!QU_$Wq zY1oGCwaIHwU02RDe9<+jS7x3h?cS)fHom~YnVh~@Vjb3iOK;8@FoYHtr`8n1Vn^Fp zc_D7ujB%Sp5p(GGw&`;s@!DwhCb;!&Yv$=qVCLw>k2vs)g@FdV1#*x31PeprgMWQQ z|B45cD5nvSc<6x(d&#)WL{JkCgUsOWnncS`!x&02>OA;8iA2~iSF-D~1jG8K@|&p_ z%C0cpu0H51-KSAmnuTEQ+59a?^A~wp*VL-lhM5{ zTp=aLS=2D8UEStrjD4?Zk&Bz|gnky26H|`%m@x_zpKn-_^}{eiDK}}avNiQagu$nJ ziJPwwSkYiTzn%50D+2QJuWA?a)5+Y?g}Boy#1Vs5VA`(xP$H^^c(nJT(#;670u5CT z#GA;z&eETFdh2<#N>#g2G6Bd;qE|O~% zSsy4v5=UIFn>dwX+p3$$@g&3{QjSVGIB@ZvC5sQyF>7T`GK^{q`_@9yXwV7F3of`& zIBQPz19V}rj8cl?JBNLsJ~e3b;#&pUKUH2TqO1J2bQcW4J{{wn`S`<6bt}yF+-qRg z;TfBwHgo*Wrl9&&^=i{|DXAF**NSi6q&UmU#eWVTlHGa43MX!F=m`4{YU16%VvnW> z*iW~6MeVKjjB&ks zNP?b{#%}lJ!tmcAWWSVCiXn2qHc6NaL0>Xwm9zYDWJNs(aG~~OYXXFxp+pE4J1Ggpdme>?~`J<=+*|UfV`m&od9UN?F)dA&d5sdOo3J?>%U!v1#mK zcnZdUZr`?d!20)|XonQ*6HxxBpT*?cwwf8#S|Xh5D935SUduI;e*q=KLe^;fi};~? z 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 literal 0 HcmV?d00001 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: [PATCH 232/312] 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 57b79279..8907ab3d 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 1159ef53..2dca6b74 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 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: [PATCH 233/312] 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 ed687c45..c4462dd4 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); 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: [PATCH 234/312] windows build fix --- src/serialize.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/serialize.h b/src/serialize.h index cb3a3ea0..857d0468 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 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: [PATCH 235/312] 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 8907ab3d..547582e8 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 + + + + + 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: [PATCH 236/312] 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 2ab8f56e..c0323379 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 f6b22f45..5199a8ea 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 d3feedb5..fb098c82 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 945d221eeaa3cf0ffabbf288d7da7541adf05ddf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1368 zcmV-e1*iInP)PbXFRCwBA z{Qv(y11@m%;xpWm1i;N(ckU;L*(>2w1F{<+fEWomz+7L7frWr&AQQg-W?=a7^FPD) zAHNyCeEZ4p>iu_yr;lzwjP$cnz~=yf0AeEK6GkF@`|BSA!{5I^{O>;y|AXK^zZhJc ztmPN2JNOKrUVs2%VxR(G2Aaam48n{ItSrn7EImH1}iHw@g*CMV1xuf08zyOV1t0JU}AtcfCU&0KYslICdNMu??3+nCeB|B zzkYmW(9zMAnYa4jOJscj0YoqtSO9YYGZ78|ra^|^e;62kf^)-Ph6hhyGhDcO52yN! zaBHyL00G28tq=f310y4Z21S9aq%eb|r~t#a@4pycz5C4Y6__>u0WJLX`#;0A>(@Y9 zjDh$pKmbwA0ZdR=Fad*$@h?Q|A23^jEdKlF9|J2ZGsC~X{}~vWP%}0_0Fj;sjxJ#M z-)P9d@a+SpBbZ>>5||DiUt-|uc4Oc?xtxKWorQs&jg^6e9f*N6G5`o57LqNWmk24v zx*Zu9OZPJ{a0#GfN00~q+&#y@*5=Ic@AF3nk;_LI{sTkgvsoC!f9C%TO#e{*3=lv} z{k1l{60SgIWVaNb1g&DrzILE-!>IAm@4=_>w1BwYO$YYl&Aj>}?EdL9n|NMp|Oi(GwhUNf(03zN2)(H%ZN%K*J z{sDvb&%Fx_zZx7Feq7kczyb<(ESCSuoX+sy+?Rm`=m-`TR4V}jh_o~Sas(*;fl&dn z9Ap6)-@U-V+Ug9k5Y6&`*)tdzouV07{{9Eon}7eKIshPmNY92qM}X?zzcUgcmj8xg zES59m&1PV5i~?p+VD Date: Fri, 22 Jul 2011 17:06:37 +0200 Subject: [PATCH 237/312] 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 d545dc52..f9df91b3 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 2a0ef4bd..fd09ab2c 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 4b5acd8b..2430cc9f 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 9fc026fa..f7b9486a 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 6e87e9cf..2d4fe9b1 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(); } 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: [PATCH 238/312] 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 8aa43395..50291fea 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 f02751a0..54925f28 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&); From 2eac3a6decf3353e5db8a8b07f3307d04e77d329 Mon Sep 17 00:00:00 2001 From: Celil Date: Fri, 22 Jul 2011 16:55:50 -0700 Subject: [PATCH 239/312] 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 f9df91b3..ea0a98b7 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(); } 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: [PATCH 240/312] 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 cdff04b4..f1bc8e7f 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 00000000..d722ebe9 --- /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 00000000..1a1ab53b --- /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 0000000000000000000000000000000000000000..01afd3c6da9b3cb4e7dee5532865f0bfafb6b1c0 GIT binary patch literal 15086 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 literal 0 HcmV?d00001 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: [PATCH 241/312] 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 c4462dd4..938d030d 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 547582e8..5b30d99e 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 2dca6b74..13593c2c 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 + 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: [PATCH 242/312] 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 e59a2cdc92cfb81e93786b0bfd0bddbaef63a63e..e8f418a4f8e6654da3f0a80f59fddfb40117f851 100644 GIT binary patch delta 1779 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 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: [PATCH 243/312] 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 fb510fbea6c96133e9effa439775cc0cbcbb008b..0dde6f395c07349ea421f625ef1948a1dcb598cc 100644 GIT binary patch literal 2163 zcmV-(2#oiMP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf2 z5ds4xdmmE(00;9)L_t(o!=0CVY*Y6Y$3H(i_H}HW*ugjtoMN0fkkAGaN=wt1TgIT} zQJ__&YSq+Dl_qVp>bhx@)=B#Vgt}>(s!}v2S8QXoJmX#xpx zLdc6GcKr6ezy0AGW>Pz&J<`$D)jhx8_j}IoJbo8uk<*Vqbtk|viwoKr3VqmLTGnV?(RBXwJu5$62fQzCAN(Bwm1e1w)>n5LEaFl`P1|1jHUxeI zjgw|e`}|ECrL%Eq;C(p0LBi47AaO)MsYK~b5VCnZ^Bjxnox`dKje~8=?GRW4jWKBV zZF)jlH7PvL{t$ab`NM|<$5%`EXciJj1=u7?1VRg*d)`&)^@}ABJ|vvWnjKLHje%|H zj5W7x=YvKkwEuF;GxEvh_lUHb!JuiFD$4ofcfTe%JQQ3b2^(~!5`~cev5hXjzg{Gg36SC@O0m_(b4T8jZ(lq=0aPzG7Dq3s z@hky5G(GBAc>39mkIA)}v`EFGw|FN3Ba&i|hqk@DnYu9+T(1$>tA96vl7>)2V71F% z?h&zM@*Bq^0jt%@%U_<7r#d?)P7(JyAuIrXcJG=)Yd1Zy_VETEV{iN!H8MH#bo7Lg z*yg(UqU{)CHztBlNdoOAEN9d7HQv+W@)z4hB$djd51Sg|LQ!s zLjY`^Uw>c!@U>m4%hB{*472{=3}%#wwqt ziOEzdFL9*AfFv>3EnCi<6n?Y4wQNr5whxB7&)u~4K+6cc;d3~fjyEi2vZFmqB2Oqy z0?K@KWPbiKiNi;v^5H=-o=#^O=CenVa#zWfJzXM`PVFy-9iwpe8&yyQEqB09f2p(i zEzbf*zdDPG$mbIlvqDwi`^3(l;rd{I!O|>}5?L`{K0P`jrqZd_KG+7FdjR+(U%VDV zOF8VUu-Th;Ih=G4^pP&GxebT|T7mBhU@e;`Uk*=*SSH;%1l#+dJ=exKbCDH7C2+x# zPBne5sg>*9m0XWZk_0k9TGtfWVovALl)kaW>5{L8Cj@GyH3r+hf^!}qJR5-s;DwL{ zE|}A)rpr=RKIn3DBN8SJsJd#ps(Dla<~Ut)cX&bwHPaf0?I++IGr;o_00;rK5VFE~ zdn(zinoX4}YZeh7zD`6>EUkb#0cV+;GYt=Af!Yl4lEYhv z<4>EOY8z(-YMU)VV7-ACEB@2?u`zfjU5e8^LjaX?=W#6+rzbKM+zNr$ffsYj@SuGTM^h!GW`%^5YYx72}g&y5jY{ocA`&42$#gW;W4 zF>tN_rniH@ny8G>(pl)KmnE?!BUWZv&il4YK#z z6%mUjT2;ZeCXI8N1=>^GR|LCk4o8!>)WvX5H}RY%mC}2J&Eq99K13`Q??}7c­ zZW90>+H58nk8Vh5{I$BgysS7D7g{8YSr5M*m{V6T2d?#sSUkQzBiVkJmhJ5TXAId< zZW6ps{r`4bQ=&|Mp`Zx@n!#9r_^fw zGFn?FJLgzsn}G`-L*T#+@LOKiNzxMD6Y!J7$j}XS>rdsB+aa{Hx?hDIewFs_qQV4f zHKc&g1iYX|Cr^(#?Zg)@KqliI0=5Fd&4uqT|C2T_T~WlAkqu9b4>c~?*H@JFyr<~) z{tdf}AY|sgkyVeL9=iYjj;{>_PUOGY6_#ETrokl*8wP8* p%`*09m8&4S`0mmPT>L*a{{;|vTY-!e0lxqM002ovPDHLkV1kdY`8xmr literal 876 zcmV-y1C#uTP)A=D;nOFk$cj$e%(t{zyx0xos(>#|OgVh~LXO58wGtzLWEr zM1;+FN^5z#)@OMk;Mo!c6XAJds()sscPiICvYBi8uRxi4yz;}HsZU>hvFeCt@AG#2 ze!v6|Bh2>=q`Iz;{P4vC>nF3hZ^t)gl#OPGrFmcXvtzeE*rlHIY%8{7Er2376qSNz z$L%h*zrR~Q@m=*_zwIy|-T#DuM?D$*+%s^kLXCTq3;)36B+Si1xfeWMcocz&Nm6q$ zyeyTsyZf(|sV9Pu1x`7}&iQ4bEV2+Mnn)lGO`zK#kpRPhK!~-PUCc#hnEpGCpG8k= z!N_L-ieEjxz0N$@yqL_8h{e#~dIwW;3oG#@$fO~YW>o~W?F?G$X_AXEV#X@_w27Df zN=p@udQ*Oc zf72mD)!U`V=aRclRo?dO=@bz(fR2LQXY2FvgEf}r3dO2pzWLL-;p*qs>+m4vpRZ%r zbySLc zz(L>4CQU+6n#Zmc_(t-Lh7yqaJ&1LS};$Yz*HW)toCs>`o!J$zu|&oBG_ zH;o%8IouwsaVmQQE_(oka5NphAIsm3{`m3aX70Z;KUy4Ma9t4q0000 Date: Sun, 24 Jul 2011 14:45:51 -0700 Subject: [PATCH 244/312] 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 f1bc8e7f..e0025472 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 From a8c50c06e315007941689d3297919622bc93053b Mon Sep 17 00:00:00 2001 From: celil-kj Date: Sun, 24 Jul 2011 14:54:40 -0700 Subject: [PATCH 245/312] 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 e0025472..feb64533 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 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: [PATCH 246/312] 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 59f49625..79856d47 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 2430cc9f..8ca230c9 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 17622e07..7062ceec 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) 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: [PATCH 247/312] 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 f1bc8e7f..e8d2deff 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 b312b979..330a7bfd 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 938d030d..bd80e42d 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 00000000..ff30563f --- /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 00000000..fa85755a --- /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 31b28024..308a6ba9 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 58f89795..26a1a037 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 3697b9fe..4f3a82d3 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 07e85297..d5238a36 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 d991f0d7..9515117c 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 38a0a655..58f9422b 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 2d4fe9b1..9fc04111 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 7062ceec..2b8fe0b4 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 50291fea..f88b6fc1 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); } 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: [PATCH 248/312] =?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 ff30563f..567e51ff 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("???"); } } 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: [PATCH 249/312] 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 79856d47..deef3e34 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 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: [PATCH 250/312] 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 330a7bfd..7fe28f9a 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 fd09ab2c..6e724d06 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 567e51ff..8414a759 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 fa85755a..18b62351 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 4f3a82d3..94f7abac 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 8f285c64..89617097 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 bdb797a2..4ba44dc2 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 9fc04111..f3847f16 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; } 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: [PATCH 251/312] 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 8414a759..7cd50232 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 18b62351..4dfdbea0 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 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: [PATCH 252/312] 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 7fe28f9a..f1b4e9fd 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() 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: [PATCH 253/312] update readme --- README.rst | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 98add9f3..3ab37001 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 =================== 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: [PATCH 254/312] 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 266412c8..04922c74 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) 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: [PATCH 255/312] 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 58f9422b..d5f15e36 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"), 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: [PATCH 256/312] 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 7cd50232..9a9a4890 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) { 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: [PATCH 257/312] 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 bd80e42d..d1e7bb94 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 f88b6fc1..9c38934a 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())); 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: [PATCH 258/312] 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 250aeff9..03cd592a 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 63d1d706..bc652d31 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 f1b4e9fd..73498050 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 6e724d06..cc92159f 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 d1e7bb94..655ae5ca 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 c147aa5a..5cf02eac 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 f7ad14c2..544334e4 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 94f7abac..a923f3ea 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 89617097..d72a0e9e 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 4ba44dc2..ed26f83d 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 9515117c..c04bbf60 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 acf83c72..c54dda32 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 00000000..c0ad8c12 --- /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 00000000..2a3533da --- /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 f3847f16..abdbc81b 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 2b8fe0b4..99f2d580 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 9c38934a..12fdc947 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 732472c1..4d8d6fe4 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 668d4463..c989e7fb 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 { 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: [PATCH 259/312] 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 544334e4..845ad10d 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(); 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: [PATCH 260/312] 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 625b8e3f..9aeee5de 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 += "
"; } 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: [PATCH 261/312] 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 73498050..a9c89334 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)); 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: [PATCH 262/312] 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 deef3e34..7fbf7fcd 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 a923f3ea..e922209f 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 d72a0e9e..4656ad08 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 ed26f83d..7f489c50 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 99f2d580..27e85ceb 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 85bfeebc..3322ff4a 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; 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: [PATCH 263/312] 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 da086b27..c8329bb2 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), 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: [PATCH 264/312] 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 4d8d6fe4..53555cc0 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); } 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: [PATCH 265/312] 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 53555cc0..d8139e9f 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); 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: [PATCH 266/312] 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 d5f15e36..54cae21a 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 9aeee5de..88dc2d8d 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); 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: [PATCH 267/312] address icons --- doc/assets-attribution.txt | 3 +- src/qt/bitcoin.qrc | 4 + 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/{icons => src}/bitcoin.svg | 0 src/qt/res/{icons => src}/clock1.svg | 0 src/qt/res/{icons => src}/clock2.svg | 0 src/qt/res/{icons => src}/clock3.svg | 0 src/qt/res/{icons => src}/clock4.svg | 0 src/qt/res/{icons => src}/clock5.svg | 0 src/qt/res/{icons => src}/clock_green.svg | 0 src/qt/res/src/inout.svg | 122 +++++++++++++++++++++ src/qt/res/{icons => src}/questionmark.svg | 0 src/qt/transactiontablemodel.cpp | 69 ++++++------ src/qt/transactiontablemodel.h | 5 +- 17 files changed, 169 insertions(+), 34 deletions(-) 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 rename src/qt/res/{icons => src}/bitcoin.svg (100%) rename src/qt/res/{icons => src}/clock1.svg (100%) rename src/qt/res/{icons => src}/clock2.svg (100%) rename src/qt/res/{icons => src}/clock3.svg (100%) rename src/qt/res/{icons => src}/clock4.svg (100%) rename src/qt/res/{icons => src}/clock5.svg (100%) rename src/qt/res/{icons => src}/clock_green.svg (100%) create mode 100644 src/qt/res/src/inout.svg rename src/qt/res/{icons => src}/questionmark.svg (100%) diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index c0323379..0a719f17 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 5199a8ea..8d4bab54 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/tx_inout.png b/src/qt/res/icons/tx_inout.png new file mode 100644 index 0000000000000000000000000000000000000000..ff6bb1c5c3ed6049f5cbde02b95d53e7e4739a2f GIT binary patch literal 631 zcmV--0*L*IP)2 zCW_i=T*0MQ5TkDTiD`VW#99mn}3=_-H|nM`J0 z(rmlkeo68n$?LUR?bdjJ1LNb-#)>+w>kiWC^e#z_u!3tOZ`5kFI{-k>^DYCp7vcs0 zo|AkQ1i`m(j0+@>0@wlIOSjwITdh`uC{fH9^Ck#_H2_lpk^rnE8}=IY9MS!8Fc5BH zqtTd-$Kz)uodb{}IVWiwfR6y)NqP)msZc1q#_uHQZ_HM!mH3Bk6LZpY{t29v_VYB- RoqqrT002ovPDHLkV1oR%3Jd@M literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/tx_input.png b/src/qt/res/icons/tx_input.png new file mode 100644 index 0000000000000000000000000000000000000000..1673d06ad33a333b0a7c6652a30c4e420e933dc2 GIT binary patch literal 594 zcmV-Y01T`nbwjKg&X^f zu#xEC58Ra$F6e;90!q4+*s^>Ql1mFAB!l9DjuaFDCq$$|$fSsU_b9lFE(f9UNTZ$I zH}m$*td^26kD0lu0i>hA^SrYpYXH_HtxC#}+?F&Z`OdcO4FG@1r6lxvy>*gx04M)* z*)GZF)oS(W5rS^FyJCzP$Ib-cGk`HkOVWiHwm+Rtm+STV!C?!=n5$uYV2rty$z(p3 z%jKU@LbKVto6qMjl6(SSF_lW?0DL(lAbBl(iLaGPeOm;vx-v)K}Wok&46z;h8nyWRFl-g6vhPtpTPPN`J7Bx%LA?PtjrB7*aA zKz|0~xURcvS=Jdzoe(;f;Iij=-vE3d`927O2}!G1EM6hm40C=UVQFK`W)KAX02Tq{ z0N9djk^q2c4efQ3fRfPZbWUZn+1rxt09YfrD(M7(?*O(Wy#cUZtJOXw2_WfT&VIkY ga13_DIq!G=4ZoY?i>Xjr?*IS*07*qoM6N<$f(sV~tN;K2 literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/tx_mined.png b/src/qt/res/icons/tx_mined.png new file mode 100644 index 0000000000000000000000000000000000000000..a336868e8a89b553996f84ad63fba5aba551db06 GIT binary patch literal 754 zcmVoPDU@-W+qod+9=#H$FZdPbQPc%gV~$04y&r2jcPg-DEPE7LoUZgM)4V0CKt9*@cCL zeThV3PbQNg6bhw`F`of~!Qf4;HK|mpd}?ZHn^NkwwKlW{kWQyF6B84xuC7v1Q9*5O zZCBw<6{XY> z;H8LIpm~0NekqYi_(X)7nwn45)zv#RfXCx0)mkf`&nNZu^{2Gf=K-IHJO(uoEBO>ZTrStHmX?-DYps87 zZf>-ys_H~rTidMK=pR^X%S2=Z*q@hHfs>I)B)Wc9uNV6J`^$l6dGjLh6es~6IgZo5 zDPUk=pwjJjkBP`$AR{8j!r|~?rPN4%_P*mdXEp@HVzDZ>+x-IA4G7TQ+S>Z0si`Ro zM1a?T0{X1A7uEn`u~;~h$-EMgoq7FYab9E0FQwEW;3I!lf6H2X=`SFg&7K3c11P1s zB9X|Q^;wNEKb2AkfiD0e(q*mPQiyOJ7zKKaF;~}{iy$eN%XI*l0gf7Dz9Axr2#(|U kMMM?)&1!gfxU_Kp4ZR*Ocy4Rr_firTIcA}30GUJyf?$PY89+hOhNKfD4<(IBzP8rB18}TeNPn*e+QFeZ6X(pt(l4a4xP=XtZlV2m+au|6}#Tyq>}#adgi z)?RmAHy?&!p5(naW_dIk-KZpZrPQmOQ+SSsKY+0n|m=_G*f01hO*1kfs%%O8%-CuyE=Fc>)h f@Es8@`kns(Nci4D#R}x@00000NkvXXu0mjfcvt^1 literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/bitcoin.svg b/src/qt/res/src/bitcoin.svg similarity index 100% rename from src/qt/res/icons/bitcoin.svg rename to src/qt/res/src/bitcoin.svg diff --git a/src/qt/res/icons/clock1.svg b/src/qt/res/src/clock1.svg similarity index 100% rename from src/qt/res/icons/clock1.svg rename to src/qt/res/src/clock1.svg diff --git a/src/qt/res/icons/clock2.svg b/src/qt/res/src/clock2.svg similarity index 100% rename from src/qt/res/icons/clock2.svg rename to src/qt/res/src/clock2.svg diff --git a/src/qt/res/icons/clock3.svg b/src/qt/res/src/clock3.svg similarity index 100% rename from src/qt/res/icons/clock3.svg rename to src/qt/res/src/clock3.svg diff --git a/src/qt/res/icons/clock4.svg b/src/qt/res/src/clock4.svg similarity index 100% rename from src/qt/res/icons/clock4.svg rename to src/qt/res/src/clock4.svg diff --git a/src/qt/res/icons/clock5.svg b/src/qt/res/src/clock5.svg similarity index 100% rename from src/qt/res/icons/clock5.svg rename to src/qt/res/src/clock5.svg diff --git a/src/qt/res/icons/clock_green.svg b/src/qt/res/src/clock_green.svg similarity index 100% rename from src/qt/res/icons/clock_green.svg rename to src/qt/res/src/clock_green.svg diff --git a/src/qt/res/src/inout.svg b/src/qt/res/src/inout.svg new file mode 100644 index 00000000..bfab8ef6 --- /dev/null +++ b/src/qt/res/src/inout.svg @@ -0,0 +1,122 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/icons/questionmark.svg b/src/qt/res/src/questionmark.svg similarity index 100% rename from src/qt/res/icons/questionmark.svg rename to src/qt/res/src/questionmark.svg diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 27e85ceb..58ec2c7a 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 3322ff4a..71b06441 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(); 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: [PATCH 268/312] 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 58ec2c7a..1606df9f 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; } 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: [PATCH 269/312] 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 fcb0c833..c328ca37 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 bc652d31..749afb4b 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 8d4bab54..e5653619 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 0000000000000000000000000000000000000000..3846e6f68de0ff1379fed451bb64d0271e175622 GIT binary patch literal 5816 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�Z!PV(HV&(3%*QWGu^6iJ0oFO<|x) zM3(mR`jQx_+4X)5izD}g)H)Y&8pXRVyL)cK8ih8t?iQUt{zPUnHzeIQQQ~TXs5TR@(jIct(vWwbNsBr*#nFR9$87^y z_ixB$3c(?UY4E1FwcckzTrDy%sQNea)cE29OWzuImaMYSd1;LW{&b6RQo@YFN4+f6 zUFY4jTgl|lf(*3noI(sFrxDo!GaCSr3&)@0K*GZ^iz&jQAl>T>;xu`%$6n&;hj^5W z8G@T?4k-IwesTJ;e7aNbWn7IeMqzBUX^Ik4RCjMD_KTud3)YzR{^Fs~< z_@Bo^y^$BBKV&#-*j7(d@~z!k=d!aLRTdbx4~_Ca%s)zR&dJJDn4{0jdj;eM4|cY( z;VTP89oTD`D$LMSLI#?O+LtW4IU-^{rBQ?_$@DF4=9TgT&c>F8~QQmB^Crk z={BDE=HI>l)J<3Pc+bBXMJ~tUGpn-lYvoLvlyI*ptE&mv6VfFC+_~~+jU8|_&bHT@ zaGDs9lNZ%4oi8f5woCQ+FhE2s+k(X;Fs~mjltlbg#H;i6hJB1wmAr__km6ZDMtqcw z;+aM}H?8W64lifuP%B%-*-oN`e$tG{SAep$_pt7-RJuYu6Aas)$k}mSKK2fho(7`J zTGG=Z;Lv&7Xa~K>ZuyY2!g<<+eJ6I_WUnF4v>?)x{zoEw&d;n3(-~z%r)9G#z`yfn ziDw{QEtWCr#znc}wA>skI*g@*Lu%%|d6KmF>TQ~hDg$&`GS z!k)(iu3>4;CcwMl_C@>Q6KE#-QM``$lo6PcEN{>NnKc_-#rY`RAAg&@L;+Wb!(lxQ z>O2%))#>?2XZS7DQoqX!FZ!ewiTkk^rZufB{YQWw^WwB>PCwZ0RfjY3$2D|dmR<}1 zqfs`nb)C%(eu^Ak%}e5c-fbA#u}D;LD%co%U!~PDZhf9xNXax ztf^84NI_Z85BrwB{MG;;z~4e$2=8iI{*z12Kx0_Y(J_YKggKIBRyIG(&EJyt33f3x zgQ~{qg!AZ3MIvup7#}74vo~a!T9)76e<>1E5SuK_Q7FHj_lApdH(lLDxzyvCDr;GE z;9C}XR-RA4<^LA02ZOe*T9Ini)ap)pZ`GdT!i&-!TAo1Kt3<{1{5P4*5|AX&abi}5 z1}dPYeDllj#OML+t&~AMG9GghvBM(KCm|FSGV7mz{^hrp*UyOpevU#lhrAL>jjua; z_s`cjG7!hRAJaEX9G<-DraY5V$*np%xf#qKb$V<4w-~%?;p@j6x>tEv$(bKOZm-D+ zH3*H)OmdaaIOZBjhhmDKIX~{JCo2?o@iIYnQf);n3IR{$Wi)2X{*SFdFLI29G|M9Q zF^Omu!@{YGB~tRjU_?jO54;z1>yY!B-cu$Y^NS~a-U=wQiL0v2nCIyhbIX>*@2*LH+5afsYldf6v+Iu_W3|niqsM~>pd<;= zh>g!vtgzaSjEP>loF|Dq1B_N9wFfxT&M$$;zr3b()@xo@A&MU}|JlodcQX8W z!E_YgR1@T4UUvYBA9A<^)UME~{9jl6W8@HZS!NM;DQ_UP5f?pMru?nGzuMT7e^6NF zI9L;OZS_MpFu91dy!f~xDbSwSF?n?>ZgktABuE$=?y=SWddDE-Vf)}DFIAw)jof`H fC6gOH`%+hJc7P+O`*0eSo)wMf*I?kKyPW$UZ|j#{ literal 0 HcmV?d00001 diff --git a/src/qtui.h b/src/qtui.h index a3b9eb01..17fc44e9 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 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: [PATCH 270/312] 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 3ab37001..afe8c720 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 749afb4b..8ae3762f 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(); } } 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: [PATCH 271/312] 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 0a719f17..e4a00fa8 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 + + + 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: [PATCH 272/312] 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 a9c89334..e25cefad 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); 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: [PATCH 273/312] 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 d8362a7b..cc67fae5 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 308a6ba9..ece06907 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 26a1a037..fb5c575a 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 c04bbf60..f79e1f3e 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 c54dda32..2abddf16 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 ff6bb1c5c3ed6049f5cbde02b95d53e7e4739a2f..5f092f97aa3db749e5e7f490158a489e16cbf4cd 100644 GIT binary patch delta 2408 zcmV-u377Ww1d0FdS2i4#krI z$;jMwh4UvwP?AF(?cI$?5W=j&A7~uy_o{IiB$|jyL)+bs4M7M5A}AXh=TM`YGGv62 zT{%z2?XLYkAOC23>$=|cdu_MQY_FubZ@+FsX?6h3=A;14 z4xrhb6rkAwG@FwGG&_K1bHWQSr(OriWO5;}%owvwRnHcYcA#BV+Y8sNz!)&5s$(KD z28@ZwzpS;d0>s~J+*8!Iv^`Jx!0#93Ohg5Ya7!1C7)XJJc07)j3 z*BWDP2Re^d03O*63|VV81%tt@K*J_5M+mTG%NGCStcM98 zB44Q2;jzRR^Od@Nlw6CdUSF@nV~MIZPJm=Gxm-jR)$6c~{{p+n_u_8>ZiD@wKR7se zNj(mxIzgY$_n8`($0mU1fGr}jTUGa2YxlXX`%ZUv_jqZ>NF-t|y6B<~zu&)5RTlzh z0!vkY^=fMDdtBGO6d10>7D@zA)yrpL_m-+|7LmuZ+3fGTySv}>VpJIJzFqiiFHIX7 z8ag|d%Uumz4O|6$z+)^QnMK@3P!vFbPkPAzzP0w#!C-JZ4IGRBY}>Z&t^ND=|7~Dk z;2}WL>GTc2FRR&=HrsGGe7~xu9LIUdYqFH@H_}}EkR#go^e}DgZK);AQ-P_x{(reiUKtz^!%)ia6W3Q}TyY?(&%nRhJ z{vyBM|9OBwATSE-u4XSH2EcXQyUEw+Q&cq_3WdJrrTn4*r+bX|6_5Em(bw0vG?&YN zy`-w8KR|!U_10=WRP{fhQ0RQ%#**7U5qT&S3f*1J-T+u@JqaKp4XXjS-FDjwYwh#o z-vp&)EbHs*>jY5M8OsxqgQ|M3wKgXrnF{IO4~N4)jzl8UF8p||0U~m6{`~oWt(MOV zp-`yD7_$jDwNkq4x)%cYe7>1#z__Y^emfWp{xcqr@3Gc)0S7DO`(`GSc{CD<%&p|4 zs7zG>RCQlxXJ@WjzG{TS;X6g-r+}|Q@;LA%$8k0kzHb$gyMXTk_p0jsz9ur>c+%&8^99zgUq9}-4$-#e z{rznwkx2M}Q>s~r$au9p<&2DsTqz>Q>lFJc*LAy0cXzi-q3z9ts(KkJABN*NPa0#c zr}FEmsfft!u~_Uk#U^mXAff^_t^YspzyoPjy#^Sq#r|lZs^Mrf`Xe}hG!CdLfU2HR zYk`jAJa4VNg8ZULCHAQ571r8Cxm<4HWkAohKPL2aU7?3V@~1WG9ohLLx@NR`Dt@aGr@s@fj_TV zvu3%^=X(NJT$X%}h-?&pksAgE1~%8?zesOy?@vUe($rpoTcgqFgQYi%Lt!sXpe2<` zwbv6|JRaZUy6&fdKUPRy01So0;d?8M-3JE;Pa7N@Jd^y7P?2yre5a~@qe7BZ)mvh* z*n{N>b09B(m*p^H%o)H))r>O`kH<%PdU~#GZEZ~e*Ony%;6ANo(D)I674_|IEO>Od>e2v&;_go{NZr;qpJFIj;tN7h};;BMyFj_ znoSeHTHA=7U;x&Cty`CK9Oq`>2W3fbwzs!G2hh^eatmRcZwo)z%2^*%8%q49WpkBI!Cz8H(e z9<63yYy!Ky1l*`5P>$m`(ZVK2Rz!X^6`V00)ok0^+AM&7wYF$C33NxJ(Z{@$Z2+>_ z>}y^!t7^3gQ_VPz^J{DE@@Ow$_GQT3Se_)9(U%$?qKP6_LFn@?K%lwZj*u&uhyw-rD^9u@}i;F{RRi*U0!MstggQ+2Zr|Y^q>vh%yo~qZulmNyUPj7eNh=f$NeD2;Xo)MAgjvYH5Z{)GsY9~b{olajVBCAz(CHYy|u|k2Z zs=5(>NUmPJx*^+g<;?1=@WzcBmjItDh;bPWIBZ?U81RCK42sC6RjXD_w}jR(W_?(? zNFtG#>-YN?yRN$gSW-A+uo!3uPNVYPALP#zjRGU8`WF#-)fh8utsM>o0k&^;6JAh_$Qh;U$&}>e4 a0saT)@WAbj8CBx|0000w z00Gzm`dp@Rkwzzf0rW{kK~y-6rBgeLQ&AB9zGF;uU3Eo#1QwQUQiN3tOA94FAWd>2 zCW_i_{LV4X7b3Bnq z%vftv*4mT#d_LLfbS_9*41K10z22E}xqR$b0FsA8`9m(3dy>s&zeYyKajp!9!{tt= z^9sO<1CwWlMn>$;1QZUT5O=`#S3lq8az2QUrb zd`!~r5c53(mrA8A$z<|K_}ob{@&QR;WBdsK3o!uE2kwuY>h<~$k{2Dv`6THofD@Ta zW?s^4yWM_C@*&CVwOZ}gcz^@r7Isi)M~Xm06@?4E(5q1 z;syYolYABg!MAXX3nY&M*a6^6x7*!YtyY65QOp?gCJ2Hx08;>x0IVb%_8RpZ(fx5S z5N=|l(U^|M<7XwE1CSy)Cutjij{x3DdJJHxP$;~{?9oqqrT002ovPDHLkV1g4|{sRC2 diff --git a/src/qt/res/icons/tx_input.png b/src/qt/res/icons/tx_input.png index 1673d06ad33a333b0a7c6652a30c4e420e933dc2..0f5fea3a8456c73a27c2f0f3d01b3bc96aee389d 100644 GIT binary patch delta 2116 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 diff --git a/src/qt/res/icons/tx_mined.png b/src/qt/res/icons/tx_mined.png index a336868e8a89b553996f84ad63fba5aba551db06..613f30fecc041468d7ff766236dc509d815f7aab 100644 GIT binary patch delta 3260 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 u$K&x1yUy|%``zIGDqitFoqfeS;Jw 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 = 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 a44c9c4d..4dd2a8e5 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 1606df9f..458341c0 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 71b06441..0daa5f6a 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? 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: [PATCH 274/312] 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 f79e1f3e..98f0476b 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); 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: [PATCH 275/312] 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 e5653619..629349c6 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 98f0476b..8aa3bb86 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; }; 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: [PATCH 276/312] 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 458341c0..988a91ee 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) { 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: [PATCH 277/312] 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 655ae5ca..6e760654 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 8aa3bb86..778ee037 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 2abddf16..4b4cc922 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; 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: [PATCH 278/312] 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 6e760654..c9feca5d 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 4fc17dd3..c48fa8cf 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; 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: [PATCH 279/312] 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 063e510c..a8ca635e 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 a0b27e83..2b3d9bf0 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 62199611..81086a45 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 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: [PATCH 280/312] 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 5a66f851..456043af 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 988a91ee..f418a2bc 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 0daa5f6a..17bfeccd 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: 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: [PATCH 281/312] 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 c9feca5d..84d8fe46 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) 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: [PATCH 282/312] 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 c3ce165adbad91ba5e4c7013b12f8df92219401d..dc8be62831673c2e99f05f5e5b42581e6e4e1db1 100644 GIT binary patch delta 10546 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 diff --git a/scripts/img/reload_scaled.png b/scripts/img/reload_scaled.png deleted file mode 100644 index 9a45b1bd1d5de4d3801647de10bb3eb3f0ef7079..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 905 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 X_3U)dpy>Z91dJB^2GVJs?Y{p4I{`$4 literal 27707 zcmb5VV{j+m+V>mVwrzW2+qP{^GO>QKZD+z?Y-8eyJ+W;kXZExAzRz8!>Z$sluIgHA zUEMGG#rmvE-;PpHltP5Vg98BpAyAMOSN%Jp{O30RJO1wrp5b1{9jMt z0=~Y!P^3DZ|IT5ZrFGo?j?(|V6~q% z{#xK9RYep)LBRe_|FwD*bV&F+BFg-)kAZ>uYazb-G64Yr+maC%QRkWHR{O!QU_$Wq zY1oGCwaIHwU02RDe9<+jS7x3h?cS)fHom~YnVh~@Vjb3iOK;8@FoYHtr`8n1Vn^Fp zc_D7ujB%Sp5p(GGw&`;s@!DwhCb;!&Yv$=qVCLw>k2vs)g@FdV1#*x31PeprgMWQQ z|B45cD5nvSc<6x(d&#)WL{JkCgUsOWnncS`!x&02>OA;8iA2~iSF-D~1jG8K@|&p_ z%C0cpu0H51-KSAmnuTEQ+59a?^A~wp*VL-lhM5{ zTp=aLS=2D8UEStrjD4?Zk&Bz|gnky26H|`%m@x_zpKn-_^}{eiDK}}avNiQagu$nJ ziJPwwSkYiTzn%50D+2QJuWA?a)5+Y?g}Boy#1Vs5VA`(xP$H^^c(nJT(#;670u5CT z#GA;z&eETFdh2<#N>#g2G6Bd;qE|O~% zSsy4v5=UIFn>dwX+p3$$@g&3{QjSVGIB@ZvC5sQyF>7T`GK^{q`_@9yXwV7F3of`& zIBQPz19V}rj8cl?JBNLsJ~e3b;#&pUKUH2TqO1J2bQcW4J{{wn`S`<6bt}yF+-qRg z;TfBwHgo*Wrl9&&^=i{|DXAF**NSi6q&UmU#eWVTlHGa43MX!F=m`4{YU16%VvnW> z*iW~6MeVKjjB&ks zNP?b{#%}lJ!tmcAWWSVCiXn2qHc6NaL0>Xwm9zYDWJNs(aG~~OYXXFxp+pE4J1Ggpdme>?~`J<=+*|UfV`m&od9UN?F)dA&d5sdOo3J?>%U!v1#mK zcnZdUZr`?d!20)|XonQ*6HxxBpT*?cwwf8#S|Xh5D935SUduI;e*q=KLe^;fi};~? z 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 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: [PATCH 283/312] 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 f418a2bc..02bd733b 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(); 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: [PATCH 284/312] 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 84d8fe46..b20f633f 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 c48fa8cf..377da726 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 ece06907..3516d4f8 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 fb5c575a..012e4975 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 54cae21a..4d315fb7 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 0f90be81..9c56e518 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 abdbc81b..e97f675f 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 55fd12a1..ccc223b5 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); 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: [PATCH 285/312] 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 a8ca635e..ee64cc2c 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 8ae3762f..a21983b7 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 e25cefad..1af582f6 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 b20f633f..2c9c25d5 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 7bf4fa6c..88948d07 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 e922209f..0eeb6f86 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 4656ad08..efc216da 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 778ee037..e8180e04 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 4d315fb7..a9a89c28 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 e97f675f..fccef232 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 456043af..a4c5b371 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 02bd733b..6343fe50 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 12fdc947..0b2a3e60 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) 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: [PATCH 286/312] 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 3516d4f8..6329d51d 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 012e4975..5f63c16e 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); }; 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: [PATCH 287/312] update readme --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index afe8c720..283c8362 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 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: [PATCH 288/312] 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 6329d51d..74863e15 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; 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: [PATCH 289/312] 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 c8329bb2..bd314ba0 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 a21983b7..cdd69e31 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 1af582f6..ea38cc86 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 2c9c25d5..dd94652e 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 4dfdbea0..a7bebbc3 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 845ad10d..15387056 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 74863e15..158b84a2 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 5f63c16e..bc4ddb8a 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 e8180e04..7bade9a7 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 88dc2d8d..612b5d89 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 fde861b6..257b2cbb 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 6343fe50..353cd791 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 17bfeccd..da55495e 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 54925f28..f4f815b1 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 d8139e9f..10b3738c 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; 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: [PATCH 290/312] 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 cdd69e31..9b4d88d9 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; 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: [PATCH 291/312] 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 03cd592a..c1493f6b 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 00000000..df9a2591 --- /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 + + + + 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: [PATCH 292/312] update README for issue #15 --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 283c8362..45fb5a3a 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 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: [PATCH 293/312] 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 e4a00fa8..d498e8b4 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 629349c6..1d5a58a4 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 dd94652e..22987267 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 377da726..4b713171 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 7fbf7fcd..b7870199 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 0000000000000000000000000000000000000000..ce8da0bec7df7b25f2eded2ff7e5be5995c55f7b GIT binary patch literal 1237 zcmV;`1Skd5(y@-EOx{p64tqEa>v`@>eHLocL~*xMmioTeof<6OqMMt0kM8 zo78Hx>mh_gXV0EJa_-!@Bde>cheYIhqtW2@?b~wTz=6d_j~*SHCAOQzDTHu2%QD~T zbeNl)`{vA3r1o&ccaq+Kewfc3l+4S@C^Ou27r^TLHfg3k&)PPfIngT4< zYPBole6uXOVvJc@T3VuBub;Yq|9N~MCx^nL3;1yI%L0iDCSnZxe975}*S*?aGQ z)c?7gKCM4|u$rVDZxJJ?=0Fe;N;AeF0@gab_xL0cpCm4+cx}9v<;)TR3bB&C~ z{yuNKwa%VHAN#!VVYRig+F;Cz7+Wh<$a5hZSgeD6^P7n6QLhNyE(A4LBRC7zLE^wU zrLtG4)fJUMKeLdBiBmTgKm@E&yvq?gAh$VHE2snYkmq0ooE5B5oB=0-qiEZJvw>h_ zqIf4(Kvf7q2xBosgq zG^#nomsCN^#uS2y03vajm^e->rD(Bsq!=s?p9e6eWKc8|Xj4}1o;TnG$ zj_4;YqwSgKa2FePm%>$HYl?PwbtZr!)2MM7w#sbE!A0Vimb4Iss-s>nC_)Mml|l;& z8X`gKJ>`T8L7H*+#f%R?4aYqQ&0v0u>kqkrA^d z23OU%ClprpkFd}5w`)g$r>*s0*HR7!?aBJnfo!x@);dUg4YRQh-AyFRVsh3-t9Uf= zuxBqiw;!E<9c?`C2DiVhYinJ7^6dGuG5HNad|}D9`}Vdg%oYa4`2-9K%eGm6s6llBUg|Get^IH6RWVu+U00000NkvXXu0mjfxUEVa literal 0 HcmV?d00001 diff --git a/src/qt/res/icons/lock_open.png b/src/qt/res/icons/lock_open.png new file mode 100644 index 0000000000000000000000000000000000000000..6a3a8edb2377721e37e0a0b6d9d52b4ee7b97b6e GIT binary patch literal 1442 zcmV;T1zq}yP)4}1uYl0!hqVpq8&rwzJ+A% z8Luqu?5?7M=DpWbUGJ-}zN#S{z;52WiMp=QG!4fXix^|kG!0aB#LT0jC~R?Y@gN~V z2XS6rUY5G9&$#=q^F04~Q546C2vt>mv$3(UvcA6l>D=7hKNl7j(hCQ$yu5rQrS$uv zC>8$Utvh$_e3E6^yB988c<@31+_-TAA~I)YpM((3udc3A zRaIbSxI0u801#uO(P)ITXV2Q^=H^le;WwvGpMJPkeBR5!%raGd55TYP-MdGsTIYHG z7iRvYyT4pj)%@1h);S{jbbfw*bp85uo|&0BCnA5ib?a9Ce=%_N>Q#uy>3+Ze*~Z4k z?DqC{>i7GXnfb$og@t;j@0BZ8@({vrd%fNtQ%c#HGiSC_O6O0VI`xm|W?+4N9jdzM z?z4lz0L=X7JkQ_%{^bC0>C&Y-#`rl)1bMqSs4G1kod z(Z!1wZ8!hRmoJ-{eJmoQwY4>*l+GN>J5DM09(4KET@A+JCF6 z`Xa`7d^j8ybzL(6eJ%i0bObnb z;s_ppiTeQF0`R|m0&oHR)!9Q2PyXnY|DYK>L@X_A7II>^C5VyGG!A!%Fpy=0JOlEK z&_AS@omadx&&Xf-1)Ba(KwnV+@Jsv=136O*!ge(9|1R_EvKnOq$aOAb{B+tYC5w0ifLx z?AeCyEf|NlnK%G<7<{~id$*^yEKg^>`!wE71CzxZFk`qg+yOPf3@~luDg$r=n?sGE z4yZX)8Da$M038rOhnw13w^TZa0p{=Cw<{prt<;J#up@LOVQ&2n2%!DKoLU9|_)Z$B zLr3l2(e1r0lYu7XfqPf1=VHLS-Zl_yeBT5Ljm0{S&j2yT*LRuI#P3>cq$E7S(5jy%7m+&{<0?(B{}cdS2IC8j;9!_{ zZP^Li96MSOEy!_ug2r@US{qE)KsV<_6v7H2OdcP10041+ZNa4Qq(D-D)Lqq}>JJ_dYlsM`>NAIQVCJkSij0Wz zqjUY@&GSDzarg&KG1`2X)uT~X*U0LcLz5(^QZN^3@d8XRCRJunA=8x4SeogfnR)3% zBjw5F!QDrr>r1!qt*yssW_49njhQt7(xhU{Od`S}5=4ZVIRKFEEX*tbgqekixO+Nz wxVyW#yP25*P%}%~TH0|oRZXgDX669=Tb!)P&su^EGXMYp07*qoM6N<$f>7J9+5i9m literal 0 HcmV?d00001 diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 10b3738c..9a7b56d3 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 bb1c6e85..a585f8d8 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); From 3f0816e3d926e0ea78ac7b6cd43efe62355885c8 Mon Sep 17 00:00:00 2001 From: Misbakh-Soloviev Vadim A Date: Sun, 28 Aug 2011 14:12:26 +0200 Subject: [PATCH 294/312] 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 aeeef170..537064d3 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 c1493f6b..e3dea66f 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 56f47654..6627de4a 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 c565a92e..7d2a14fb 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 9b4d88d9..6f481575 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 22987267..6aa14dcf 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 2fa8de05..45aadd49 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 df9a2591..6085602f 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 0b2a3e60..b2777b7b 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"); 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: [PATCH 295/312] 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 e3dea66f..28c5a338 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 d498e8b4..91d2e658 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 bd314ba0..6bda1e77 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 296fa580..bc505c48 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 00000000..a297513a --- /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 00000000..761612cb --- /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 1d5a58a4..be0e4dce 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 6aa14dcf..0c2eaab1 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 4b713171..484987ca 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 2b3d9bf0..06e74db2 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 00000000..70d9180e --- /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 b7870199..0cb50750 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 0000000000000000000000000000000000000000..757cad47ed519f9e9040c92759150989d03e67b4 GIT binary patch literal 1239 zcmV;|1StE7P)4UaRVv2|~@qu815EG1qurN^sV}L}WF)rA!W1)!~MZ!XZL_`t{ zD|Ca=#2}zV46no}p=yO{O>OB5Xn}Uhv@`9rGjm_(b8&}4Yai26ILVhgH}~H2|9$6t zk26X{m})4c#@u)AjBgE>S8O!P%Bn@Qw*fvd>7?WJRIvc9JLcDx zS5+OWdi=@dk(ye>tU{-|3B_yC1FdAPv~#(!ZRe_e1D^tUKu;%LG{Ci8@yb|r{7UuG zXDdCw1iPsboxBDjAYd(ffyhG!Zn~FC7a9&czkgr{FgzY&A}wP|^};Z%lH=727gyT0 zCV%xj+DwdeS;pB0SH2ps=*YF4W4P`QOi?_&bn*5>Z&bVr#DLNLo6J#jI`NJV;w$D= z$JgrE!#Ih0a=C2h`hC6cHgq{Xx4qoGp>);O73zyn(xyCK#mYsoZ7}pR;1q0f=caXE zu_+pk;J7Km_?&9a9)EdRq-6O5Gq@Dh*ffPhIqk!MfDnE!eUERbipZEmWz6idv|Wg2gIHTBn-<{hQwYoEY(@n&90u>9RVkWB0M3tKOW|V=3n?#3O_&1UKe(x{&I)V(a z@1AIUSuYkq-a0&Z`AkdyrsT~_t#*48cqwobAQ><{vZ)@D&2^k?==^%~;la;nFHk@PsBZpb$%Q2wmOhG;0YgE^U?h=c@9z2!cb&9~O&iq8~al)`sy2uDyV=!S-(lwgJInTXFCK$(C76*dq7B}hSd z%EQRq00^K$Cg41C&A;2Ho8wxt-_r9)eSlci#cD0EUSIlrj+w zra_({0PU&2o&x|dP%2XRX;D*C68&gR=;Ok}|ChSC2G9U16nv^I97p2|22cT5_ZL#(k2)tF#qV5$gv}Dmi+{*1)c)t&Bmdk z|D28+A%eWGF5C|gWWuBekhuo94RJf@oY{y2{0A`aP-hVaBPRd=002ovPDHLkV1k(u BMR))J literal 0 HcmV?d00001 diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index a9a89c28..852d7898 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 9a7b56d3..dfededca 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 a585f8d8..b141c076 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); 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: [PATCH 296/312] 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 45fb5a3a..c245bdd1 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 ea38cc86..f1edc62b 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 0c2eaab1..e15941e7 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 b141c076..b7b6973b 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 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: [PATCH 297/312] 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 28c5a338..84712e3b 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 5d29492f..59a69842 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 427067bc..238cb5d8 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 6bda1e77..8fd6d52b 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 bc505c48..f4a8dad8 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 5cf02eac..d3074731 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 06e74db2..b8e6fe45 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 612b5d89..6ca3ac8c 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 += "